目标检测在近几年开始发展成熟,但即便如此,竞争依旧激烈。如下所示,YOLOv4 宣称已经实现了当前最前沿技术的准确度,同时还能维持较高的处理帧率。使用 Tesla V100 GPU,在 MS COCO 数据集上以接近 65 FPS 的推理速度,YOLOv4 实现了 43.5% AP (65.7% AP₅₀)的准确度。但对于目标检测而言,高准确度早已不是唯一的目标。我们还希望边缘设备也能流畅地运行这些模型。因此,如何使用低成本硬件实时地处理输入视频也成为了一个重要的研究方向。YOLOv4 的开发历程很有意思,其中评估、修改和整合了很多有趣的新技术。而且其也优化了计算效率,使检测器在单个 GPU 上也能很好地完成训练。Bag of freebies (BoF) 与 Bag of specials (BoS)为了提升准确度,可以针对训练过程进行一些优化,比如数据增强、类别不平衡、成本函数、软标注…… 这些改进不会影响推理速度,可被称为「Bag of freebies」。另外还有一些改进可称为「bag of specials」,仅需在推理时间方面做少许牺牲,就能获得优良的性能回报。这类改进包括增大感受野、使用注意力机制、集成跳过连接(skip-connection)或 FPN 等特性、使用非极大值抑制等后处理方法。本文将探讨特征提取器和颈部的设计方式以及那些好用的 BoF 和 BoS 改进策略。为了提升准确度,我们可通过提高网络深度来扩展感受野和增大模型复杂度。同时,为了降低训练难度,还可应用跳过连接。我们还可以进一步延伸这一概念,即使用高度互连的层。密集模块(Dense Block)包含多个卷积层,其中每一层 H_i 都由批归一化、ReLU 与之后的卷积构成。H_i 的输入不仅包含前一层的输出,还包含之前所有层的输出以及原始输入,即 x_₀, x_₁, …, x_{i-1}。下图中每个 H_i 都输出 4 个特征图。因此,在每一层,特征图的数量都增加 4 倍——增长率。然后,通过组合多个密集模块与其间的过渡层(由卷积和池化构成),可以构建出 DenseNet。CSPNet 将密集模块的输入特征图分为了两部分。第一部分 x_₀’ 会绕过密集模块,成为下个过渡层的输入的一部分。第二部分 x_₀’’ 则会通过密集模块,如下图所示。这种新设计通过将输入分为两部分而降低了计算复杂度——此时仅有一部分输入会经过密集模块。YOLOv4 使用了上面的 CSP 与下面的 Darknet-53 作为特征提取的骨干。相比于基于 ResNet 的设计,CSPDarknet53 模型的目标检测准确度更高,不过 ResNet 的分类性能更好一些。但是,借助后文将讨论的 Mish 和其它技术,CSPDarknet53 的分类准确度可以得到提升。因此,YOLOv4 最终选择了 CSPDarknet53。目标检测器由用于特征提取的骨干部分(backbone)和用于目标检测的头部(head,下图最右边的模块)构成。而为了检测不同大小的目标,需要使用一种分层结构,使得头部可探测不同空间分辨率的特征图。为了让输入头部的信息更丰富,在输入头部前,会将来自自底向上和自上而下的数据流按逐元素的方式相加或相连。因此,头部的输入将包含来自自底向上数据流的丰富空间信息以及来自自上而下数据流的丰富语义信息。该系统的这一部分称为颈部(neck)。下面更详细地谈谈这一设计。YOLOv3 采用了与 FPN 类似的方法来实现不同大小层次的目标检测预测。在针对特定尺寸大小进行预测时,FPN 会对自上而下的数据流进行上采样(2 倍),并将其与自底向上的相邻层相加(见下图)。得到的结果会被传递给一个 3×3 的卷积核,以减少上采样伪影以及为头部创建下图中的特征图 P4。SPP 应用了略有不同的策略来检测不同尺寸大小的目标,即使用一个空间金字塔池化层替代了最后的池化层(在最后的卷积层之后)。其特征图在空间上分成了 m×m 个 bin,其中 m 可以分别为 1、2、4 等值。然后针对每个通道,为每个 bin 应用一次最大池化。这会形成一个长度固定的表征,然后可以使用 FC 层对该表征进行进一步的分析。许多基于 CNN 的模型都包含 FC 层,因此只能接受指定尺寸的输入图像。相对而言,SPP 可使用不同大小的图像。然而,也还存在不包含 FC 层的技术,比如全卷积网络(FCN);这些技术可以接受不同尺寸的图像。对于空间信息非常重要的图像分割等任务而言,这类设计尤为重要。因此,对于 YOLO,并不必需将 2D 特征图转化为固定大小的 1D 向量。YOLO 中使用的 SPP 经过修改,以保留输出的空间尺寸大小。而且还在大小为 1×1、5×5、9×9、13×13 等的滑动核(sliding kernel)应用了最大池化。空间尺寸大小得以保留。然后将来自不同核大小的特征图连接起来作为输出。早期的深度学习的模型设计相对简单。每一层的输入都来自其上一层。更前面的层会提取局部的纹理和图案信息,并构建出后续层所需的语义信息。但是,随着网络向右侧推进,微调优化预测结果时所需的局部信息可能会丢失。在后来的深度学习开发中,层之间的互连方式变得更加复杂。DenseNet 在这方面达到了极致。其中每一层都连接了其前面的所有层。在 FPN 中,来自自底向上和自上而下数据流的邻近层的信息会结合到一起。层之间信息的流动方式变成了模型设计中需要考虑的又一关键事项。下图是用于目标检测的路径聚合网络(PAN)。其中,自底向上的路径得到增强,使得低层信息更容易传播到顶部。在 FPN 中,局部空间信息会向上传播,如红色箭头所示。尽管图中可能没有展示清楚,但这条红色路径穿过了大约 100 多层。PAN 引入了一个捷径路径(绿色路径),其仅需 10 层左右就能抵达顶部的 N₅ 层。这个短回路概念使得顶层也能获取到细粒度的局部信息。但是,YOLOv4 并没有将邻近层加到一起,而是将特征图连接到一起。在 FPN 中,不同尺寸大小的目标是分开独立检测的。这可能会导致出现重复的预测结果,而且无法利用来自其它特征图的信息。PAN 最早使用了逐元素最大运算将这些信息融合到一起(这里不再详述相关细节)。注意力已经在深度学习设计中得到了广泛的应用。SAM 会为输入特征图分别应用最大池化和平均池化,从而得到两个特征图集合。其结果会被送入一个卷积层,之后再由一个 sigmoid 函数创建出空间注意力。这个空间注意力掩码再被应用于输入特征,从而输出经过优化的特征图。YOLOv4 使用了一种修改版的 SAM,其中没有使用最大池化和平均池化。YOLOv4 使用修改版的 SPP、PAN 和 SAM 逐步实现 / 替换了 FPN 概念。用于骨干部分的 Bag of freebies (BoF)用于 YOLOv4 骨干部分的 BoF 特征包括:CutMix 和 Mosaic 数据增强
DropBlock 正则化
类别标签平滑化
Cutout 数据增强会移除图像的部分区域(见下图)。这会迫使模型在执行分类时不过于相信特定的特征。但是,如果图像的某部分充满了无用信息,则这种操作就浪费了。CutMix 的做法则不同,其是将图像的一部分剪切下来再粘贴到另一张图像上。其基本真值标签会根据补丁的面积比例进行调整,比如狗的部分占 0.6,猫的部分占 0.4。从概念上讲,CutMix 在目标的可能组成成分方面有更宽广的视角。裁减掉的部分会迫使模型学习使用不同的特征组合进行分类。这可避免信心过高。因为是用另一张图像替代该区域,所以图像中的信息量和训练效率都不会受到显著的影响。Mosaic 这种数据增强方法是将 4 张训练图像组合成一张来进行训练(而非 CutMix 中的 2 张)。这让模型在非惯例的环境中能更好地执行目标检测。此外,由于每个 mini-batch 都包含图像的较多变体(4×),因此在估计均值和方差时,对较大 mini-batch 的需求会降低。在全连接层中,我们可通过丢弃一些连接来迫使模型学习不同的特征,而不是过于依赖少量特征。但是,这可能不适用于卷积层。相邻的位置可能高度相关。所以即使丢弃一些像素(如中部的图所示),仍然可以检测出空间信息。DropBlock 正则化基于类似的概念,但适用于卷积层。不过 DropBlock 丢弃的不是各个像素,而是大小为 block_size × block_size 的一个像素块。每当你觉得自己完全正确时,你可能只是想错了。如果一个预测结果的置信度为 100%,可能只是说明模型记忆了这个数据,而非学习了什么东西。标签平滑化将预测结果的目标上界调整至了一个更低的值,比如 0.9。然后在计算损失时,模型会以这个值为目标,而不是 1.0。这一方法可缓解过拟合问题。p = tf.placeholder(tf.float32, shape=[None, 10])
# Use 0.9 instead of 1.0.
feed_dict = {
p: [[0, 0, 0, 0.9, 0, 0, 0, 0, 0, 0]] # Image with label "3"
}
# logits_real_image is the logits calculated by
# the discriminator for real images.
d_real_loss = tf.nn.sigmoid_cross_entropy_with_logits(
labels=p, logits=logits_real_image)
用于骨干部分的 Bag of Specials(BoS)Mish 激活
交叉阶段部分连接(CSP)
多输入加权的残差连接(MiWRC)
其中一元或二元算子有很多不同的候选函数,比如余弦函数。在选用这些函数时,我们可以随机猜测,然后基于不同的任务(比如分类)和数据集来评估对应模型的表现。最终,我们可以选出能让模型表现最好的激活函数。使用此方法并进行实验,结果发现了下面这个新激活函数 Swish,其表现优于 ReLU 以及其它许多激活函数。Mish 是另一个与 ReLU 和 Swish 很相似的激活函数。Mish 的论文(arXiv:1908.08681)宣称使用 Mish 的深度网络在许多不同数据集上的表现都更优。为 CSPDarknet53 和检测器使用 Mish,YOLOv4 的两个准确度都获得了提升。过去几年里,研究者很关注应该向网络层输入哪些特征图。有时候,我们会突破仅使用之前一层的传统方法。现在,更重要的是层的连接方式,尤其是目标检测器中层的连接方式。前面已经讨论过 FPN 和 PAN 示例。下图中的 d 展示了另一种颈部设计 BiFPN,其论文(arXiv:1911.09070)宣称 BiFPN 在准确度与效率的权衡方面表现更好。YOLOv4 比较了其与 EfficientDet 的表现,而后者被认为是目前最先进的技术之一。我们来看看这种技术。如下所示,EfficientDet 以 EfficientNet 作骨干,以 BiFPN 为颈部。为便参照,下面给出了基于 MBConv 层构建的 EfficientNet 的架构,这些层由反向残差模块组成。正如其论文(arXiv:1801.04381)中提到的那样,这种反向残差模块的构成方式为:第一层称为逐深度卷积,其通过为每个输入通道应用单个卷积过滤器来执行轻量级的过滤。第二层是一个 1×1 卷积,称为逐点卷积,负责通过计算输入通道的线性组合来构建新特征。
假设输入的维度为 hᵢ × wᵢ × dᵢ。则其会应用 dᵢ 个 k × k 的卷积过滤器——每个通道一个。然后其会为所有通道应用 1×1 卷积过滤器,得到大小为 hᵢ × wᵢ × dⱼ 的输出。因此,总计算复杂度为:
在许多机器学习和深度学习问题中,我们都要学习输入的低维表征。我们会通过创建「信息」瓶颈来提取数据的核心信息。这会迫使我们发现最重要的信息,这正是学习的核心原理。遵循这一原理,反向残差模块以低维表征为输入,然后使用卷积(线性运算)和非线性运算对其进行操作。但是,ReLU 等非线性部分面临着一大问题:非线性运算会不成比例地拉伸或压缩某些区域。在发生这样的压缩时,输入可能会映射到同样的区域 / 点。举个例子,ReLU 可能会将通道折叠进这片低维空间中,从而导致信息不可避免地丢失。正如其论文中写道:移除窄小层中的非线性特性是很重要的,这样才能维持表征能力。
为了解决这一问题,我们可对维度(通道的数量)进行临时的扩展。我们希望当我们的通道数量较多时,在经过非线性运算之后,信息仍可能保存于某些通道之中。下面给出了反向残差模块的一些细节:可以看到,低维表征首先被扩展到了 t_k 个通道。然后,使用轻量的 3 × 3 逐深度卷积对其进行过滤。在该模块的最后,特征又会被约减回低维。当其保持在高维空间中时,就加入非线性运算。该模块的起始处和终点之间添加了一个残差连接。下面左图是传统的残差模块,右图是这里描述的反向残差模块。理解 EfficientDet 的核心概念是很有趣的。但 EfficientDet 在 YOLOv4 上的主要贡献是多输入加权的残差连接。在 EfficientDet 论文中,可以观察到不同分辨率下不同的输入特征,它们对输出特征的贡献也不相等。但在之前的讨论中,我们无差别地添加了这些特征。在 EfficientDet 中,在构建输出时,输入特征的加权是各不相同的:其中 wᵢ 的训练和学习方式与其它可训练参数一样。用于检测器的 Bag of freebies (BoF)CIoU-loss
CmBN
DropBlock 正则化
Mosaic 数据增强
自对抗训练
消除网格敏感度
为单个基本真值使用多个锚
余弦退火调度器
最优超参数
随机训练形状
损失函数能为我们提供如何调整权重以降低成本的信号。所以在预测结果错误时,我们期望其能为我们提供前进的方向。但在使用 IoU 且基本真值框与预测结果不重叠时,这却无法实现。假设有两个预测结果与基本真值都不重叠,则 IoU 损失函数无法确定哪个结果更好——即便其中一个结果可能与基本真值更接近。Generalized IoU(GIoU)通过将该损失优化为以下形式而解决了这一问题:但这个损失函数通常会首先扩展其预测边界框,直到其与基本真值区域有重叠。然后它会缩小以增大 IoU。相比于理论上的迭代次数需求,这个过程实际需要更多迭代次数。首先,引入 Distance-IoU Loss(DIoU):其引入了一个新的目标以减少两个框的中心点之间的距离。最后,引入 Complete IoU Loss(CIoU)以便:增大基本真值框与预测框之间的重叠面积;
最小化它们的中心点之间的距离;
维持框的长宽比的一致性。
原始的批归一化会收集小批量数据内样本的均值和方差,以白化(whiten)层输入。但是,如果小批量数据规模很小,则这些估计结果将具有很高的噪声。一种解决方案是在许多小批量数据上估计它们。但是,由于每次迭代中权重都会变化,因此在这些权重下收集的统计数据可能会在新的权重下变得不准确。单纯地求平均可能出错。幸运的是,权重的改变是逐渐发生的。交叉迭代批归一化(CBM)使用了以下的调整来基于 k 个之前的迭代估计这些统计数据。CmBN 是一个经过修改的选项,其仅收集单个批次内小批量数据之间的统计数据。SAT 是一种数据增强技术。其首先会在训练样本上执行一次前向通过。使用传统方法时,我们会在反向传播过程中调整模型的权重来提升检测器检测图像中目标的能力。但这里所采样的方向却相反。它会修改图像,使其能在最大程度上降低检测器的性能,即创建以当前模型为目标的对抗攻击——即使新图像可能在人眼看来与原来的一样。接下来,使用这张新图像与原始的边界框和类别标签来训练该模型。这有助于提升模型的泛化能力并降低过拟合。对于 bₓ=cₓ 和 bₓ=cₓ+1 的情况,我们需要 tₓ 分别具有很大的负值和正值。但我们可以将 σ 与一个比例因子(>1.0)相乘,从而更轻松地实现这一目标。以下是对源代码的修改:如果 IoU(ground truth, anchor) > IoU threshold,则为单个基本真值使用多个锚点。(注:作者没有更多地说明该方法在 YOLOv4 中的作用。)余弦调度会根据一个余弦函数来调整学习率。首先,较大的学习率会以较慢的速度减小。然后在中途时,学习的减小速度会变快,最后学习率的减小速度又会变得很慢。这张图展示了学习率衰减的方式(下图中还应用了学习率预热)及其对 mAP 的影响。可能看起来并不明显,这种新的调度方法的进展更为稳定,而不是在停滞一段时间后又取得进展。进化算法是一种有根据的猜测方法。其遵循「适者生存」的概念。举个例子,如果我们随机选择 100 组超参数。然后使用它们训练 100 个模型。之后,我们从中选出表现最好的 10 个模型。对于每个被选中的模型,都根据原始版本创建 10 个超参数略有不同的变体版本。再使用这些新的超参数重新训练模型,再次选出其中表现最好的。随着我们不断迭代,我们应该可以找到超参数的最佳集合。另外,我们也可以一开始使用默认超参数,然后再执行突变。正如其论文(arXiv: 2004.10934)中写道:遗传算法使用 YOLOv3-SPP,根据 GIoU 损失进行训练,并为 min-val 5k 数据集搜索 300 epoch。我们为遗传算法实验采用了搜索得到的学习率 0.00261、动量 0.949、用于分配基本真值的 IoU 阈值 0.213 以及损失归一化算子 0.07。
许多单阶段目标检测器都是用固定的输入图像形状进行训练的。为了提升泛化能力,我们可以使用不同的图像大小训练模型。(YOLO 中的多尺度训练。)用于检测器的 Bag of Specials(BoS)Mish 激活
修改版 SPP 模块
修改版 SAM 模块
修改版 PAN 路径聚合模块
DIoU-NMS
非极大值抑制(NMS)会过滤掉其它预测同一目标的边界框并保留置信度最高的边界框。DIoU(前面讨论过)用作 NMS 的一个因子。该方法在抑制冗余的边界框时会使用 IoU 和两个边界框的中心点之间的距离。这能使得模型能更加稳健地应对有遮挡的情况。尽管本文介绍的都是已被整合进 YOLOv4 中的技术,但 YOLOv4 也投入了很大努力来评估其它技术。最后,展示一下 YOLOv4 考虑过的技术列表:原文链接:https://medium.com/@jonathan_hui/yolov4-c9901eaa8e61