"); //-->
前段时间,突然发布的YOLOv4成了计算机视觉领域一大热点新闻。这个目标检测任务的SOTA模型究竟有何创新?这篇解读文章为你一一拆解。
目标检测在近几年开始发展成熟,但即便如此,竞争依旧激烈。如下所示,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 改进策略。
骨干网络
密集模块与 DenseNet
为了提升准确度,我们可通过提高网络深度来扩展感受野和增大模型复杂度。同时,为了降低训练难度,还可应用跳过连接。我们还可以进一步延伸这一概念,即使用高度互连的层。
密集模块(Dense Block)包含多个卷积层,其中每一层 H_i 都由批归一化、ReLU 与之后的卷积构成。H_i 的输入不仅包含前一层的输出,还包含之前所有层的输出以及原始输入,即 x_₀, x_₁, …, x_{i-1}。下图中每个 H_i 都输出 4 个特征图。因此,在每一层,特征图的数量都增加 4 倍——增长率。
然后,通过组合多个密集模块与其间的过渡层(由卷积和池化构成),可以构建出 DenseNet。
下面给出了这种架构设计的详情。
交叉阶段部分连接(CSP)
CSPNet 将密集模块的输入特征图分为了两部分。第一部分 x_₀’ 会绕过密集模块,成为下个过渡层的输入的一部分。第二部分 x_₀’’ 则会通过密集模块,如下图所示。
这种新设计通过将输入分为两部分而降低了计算复杂度——此时仅有一部分输入会经过密集模块。
CSPDarknet53
YOLOv4 使用了上面的 CSP 与下面的 Darknet-53 作为特征提取的骨干。
相比于基于 ResNet 的设计,CSPDarknet53 模型的目标检测准确度更高,不过 ResNet 的分类性能更好一些。但是,借助后文将讨论的 Mish 和其它技术,CSPDarknet53 的分类准确度可以得到提升。因此,YOLOv4 最终选择了 CSPDarknet53。
颈部(Neck)
目标检测器由用于特征提取的骨干部分(backbone)和用于目标检测的头部(head,下图最右边的模块)构成。而为了检测不同大小的目标,需要使用一种分层结构,使得头部可探测不同空间分辨率的特征图。
为了让输入头部的信息更丰富,在输入头部前,会将来自自底向上和自上而下的数据流按逐元素的方式相加或相连。因此,头部的输入将包含来自自底向上数据流的丰富空间信息以及来自自上而下数据流的丰富语义信息。该系统的这一部分称为颈部(neck)。下面更详细地谈谈这一设计。
特征金字塔网络(FPN)
YOLOv3 采用了与 FPN 类似的方法来实现不同大小层次的目标检测预测。
在针对特定尺寸大小进行预测时,FPN 会对自上而下的数据流进行上采样(2 倍),并将其与自底向上的相邻层相加(见下图)。得到的结果会被传递给一个 3×3 的卷积核,以减少上采样伪影以及为头部创建下图中的特征图 P4。
SPP(空间金字塔池化层)
SPP 应用了略有不同的策略来检测不同尺寸大小的目标,即使用一个空间金字塔池化层替代了最后的池化层(在最后的卷积层之后)。其特征图在空间上分成了 m×m 个 bin,其中 m 可以分别为 1、2、4 等值。然后针对每个通道,为每个 bin 应用一次最大池化。这会形成一个长度固定的表征,然后可以使用 FC 层对该表征进行进一步的分析。
许多基于 CNN 的模型都包含 FC 层,因此只能接受指定尺寸的输入图像。相对而言,SPP 可使用不同大小的图像。然而,也还存在不包含 FC 层的技术,比如全卷积网络(FCN);这些技术可以接受不同尺寸的图像。对于空间信息非常重要的图像分割等任务而言,这类设计尤为重要。因此,对于 YOLO,并不必需将 2D 特征图转化为固定大小的 1D 向量。
使用 SPP 的 YOLO
YOLO 中使用的 SPP 经过修改,以保留输出的空间尺寸大小。而且还在大小为 1×1、5×5、9×9、13×13 等的滑动核(sliding kernel)应用了最大池化。空间尺寸大小得以保留。然后将来自不同核大小的特征图连接起来作为输出。
下图展示了 SPP 是如何整合进 YOLO 的。
路径聚合网络(PAN)
早期的深度学习的模型设计相对简单。每一层的输入都来自其上一层。更前面的层会提取局部的纹理和图案信息,并构建出后续层所需的语义信息。但是,随着网络向右侧推进,微调优化预测结果时所需的局部信息可能会丢失。
在后来的深度学习开发中,层之间的互连方式变得更加复杂。DenseNet 在这方面达到了极致。其中每一层都连接了其前面的所有层。
在 FPN 中,来自自底向上和自上而下数据流的邻近层的信息会结合到一起。
层之间信息的流动方式变成了模型设计中需要考虑的又一关键事项。
下图是用于目标检测的路径聚合网络(PAN)。其中,自底向上的路径得到增强,使得低层信息更容易传播到顶部。在 FPN 中,局部空间信息会向上传播,如红色箭头所示。尽管图中可能没有展示清楚,但这条红色路径穿过了大约 100 多层。PAN 引入了一个捷径路径(绿色路径),其仅需 10 层左右就能抵达顶部的 N₅ 层。这个短回路概念使得顶层也能获取到细粒度的局部信息。
顺带一提,颈部设计可以进行如下的可视化:
但是,YOLOv4 并没有将邻近层加到一起,而是将特征图连接到一起。
在 FPN 中,不同尺寸大小的目标是分开独立检测的。这可能会导致出现重复的预测结果,而且无法利用来自其它特征图的信息。PAN 最早使用了逐元素最大运算将这些信息融合到一起(这里不再详述相关细节)。
空间注意力模块(SAM)
注意力已经在深度学习设计中得到了广泛的应用。SAM 会为输入特征图分别应用最大池化和平均池化,从而得到两个特征图集合。其结果会被送入一个卷积层,之后再由一个 sigmoid 函数创建出空间注意力。
这个空间注意力掩码再被应用于输入特征,从而输出经过优化的特征图。
YOLOv4 使用了一种修改版的 SAM,其中没有使用最大池化和平均池化。
YOLOv4 使用修改版的 SPP、PAN 和 SAM 逐步实现 / 替换了 FPN 概念。
用于骨干部分的 Bag of freebies (BoF)
用于 YOLOv4 骨干部分的 BoF 特征包括:
· CutMix 和 Mosaic 数据增强
· DropBlock 正则化
· 类别标签平滑化
CutMix 数据增强
Cutout 数据增强会移除图像的部分区域(见下图)。这会迫使模型在执行分类时不过于相信特定的特征。但是,如果图像的某部分充满了无用信息,则这种操作就浪费了。CutMix 的做法则不同,其是将图像的一部分剪切下来再粘贴到另一张图像上。其基本真值标签会根据补丁的面积比例进行调整,比如狗的部分占 0.6,猫的部分占 0.4。
从概念上讲,CutMix 在目标的可能组成成分方面有更宽广的视角。裁减掉的部分会迫使模型学习使用不同的特征组合进行分类。这可避免信心过高。因为是用另一张图像替代该区域,所以图像中的信息量和训练效率都不会受到显著的影响。
Mosaic 数据增强
Mosaic 这种数据增强方法是将 4 张训练图像组合成一张来进行训练(而非 CutMix 中的 2 张)。这让模型在非惯例的环境中能更好地执行目标检测。此外,由于每个 mini-batch 都包含图像的较多变体(4×),因此在估计均值和方差时,对较大 mini-batch 的需求会降低。
DropBlock 正则化
在全连接层中,我们可通过丢弃一些连接来迫使模型学习不同的特征,而不是过于依赖少量特征。但是,这可能不适用于卷积层。相邻的位置可能高度相关。所以即使丢弃一些像素(如中部的图所示),仍然可以检测出空间信息。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)
*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。