前言
想一想 YOLO 系列受欢迎的原因之一,就是在设计时,作者应该是从应用角度出发,在性能和准确度之间做了一个良好的平衡。这也是 YOLO 当时在众多目标检测模型中可以脱颖而出的一个原因。同时也是 YOLO 被应用到许多目标检测的场景中,特别是一些对于实时性要求比较高的场景,大家都会首选尝试用 YOLO 来做。
注意这篇文章还会不断更新完善,并不是最终版本,希望大家如果喜欢,可以收藏点赞。
自己就是从 YOLOv4 这个模型,才开始接触到目标检测任务,并使用 YOLOv5 来做了目标检测和目标跟踪,所以了解最多可能还是 YOLOv4 和 YOLOv5 吧,虽然了解并且使用了这两个网络,不过对其内部原理以及如何实现还是只是停留在表面,并不深入,所以从目标检测系列(5) 开始想要来聊一聊 YOLOv4 和 YOLOv5 的理论与实践。
在 YOLOv1 设计上略显粗犷,为了得到一个端到端快速推理的模型,做了很多假设和限制。不过 YOLOv1 满足大家想要把目标检测应用在一些终端设备的述求。
准备知识
什么是目标检测
在图像中,检测出事先定义类别从图像检测出来并标记出位置
目标检测常见方法
现在目标检测模型一般包含 4 到 5 个模块,
-
输入模块: 对于模型来说输入通常是一张图像,而对于一张较大的图像可能会将其切分为若干张图像。还有就是将图像按不同比例进行缩放得到一系列大小不同图像,然后作为 patch 输入到模型中,好处是让模型学到识别不同大小目标
-
Backbone 模块: backbone 网络是用来提取图像特征,YOLO 作者一直没有直接采用当时流行网络、例如 VGG、ResNet。而只是借鉴这些网络设计思想设计出了 Darknet19 、Darknet53 这样适合目标检测网络。这样做法从一个侧面说明 backbone 网络重要性。
-
Neck 模块: backbone 后面接上一个 neck 模块,在 neck 模块主要是对 backbone 输出不同尺度的特征层进行融合。在 Yolov4 中采用的 SPP 和 FAN
-
Predict 模型: prediction 模块主要分为密集预测(Dense Prediction)和稀疏预测(Sparse Prediction) 两种方式,所谓密集预测(Dense Prediction)通常会把一张图像划分为 S x S 个网格,每个网格都会做预测,负责预测那些中心点落在该网格中的目标。而对于稀疏预测(Sparse Prediction)会产生一些候选框,然后只会针对这些候选框进行预测
YOLOv4 亮点
首先谈到 YOLOv4 设计的初衷,根据作者解释,个人猜想应该是在于如何让深度模型可以落地,所以作者目标是设计出一个又快、又准而且适合所有人的目标检测网络。即使你手上只有一张卡也能够搞目标检测。对于一个网络来说,如果使用多块 GPU 训练能够在短时间内达到一个效果。对于一个单卡 GPU 不但需要一个漫长的训练,而且无法达到多个 GPU 效果。
输入一张图像,模型会预测输出目标类别以及目标位置信息,这里有一个边界框将物体括起来。
YOLOv4 亮点
YOLOv4 可以说是 tricks 万花筒。
从上面这张图来看,YOLOv4 在保持 YOLO 一贯的速度至上的目标,在速度上遥遥领先,并还在原有基础上大大提高了准确度,这样一来就在准确度上又为 YOLO 增加了分数,满足一些不仅关注速度,同样也对准确度有一定要求的场景。
网络结构
如果想要真正了解一个网络结构,比较好的办法就是先去绘制一个网络结构图,然后按照这个网络结构图去实现一篇,这样一些比较细节问题就会浮出水面。
这张图接近画大约有半个小时,接下来内容就会围绕这张图来介绍 YOLOv4 的网络结构。
| 区分 | |
|---|---|
| Input | 416x416x3 RGB Image |
| Backbone | CSPDarknet53 |
| Neck | SPP FAN |
| Head | 1x1 Conv detection |
主干网络
关于主干网络的选择当然是至关重要的,因为只有 backbone 提取到好的特征,才有一个好的开端。选择模型需要考虑 2 个方面,一个模型不但具有提取图像特征的能力,而且需要控制模型的复杂度,这样才能能保证模型推理的实时性
- 增强 CNN 的学习能力
- 移除计算瓶颈
- 减少内存的占用
在介绍 CSPNet 之前我们先看一下 DenseNet 这个网络结构,因为 CSPNet 应该是从
在 DenseNet 结构中,将输入特征图复制出一份,然后对输入特征图进行卷积操作后
CSP 网络概述
在 YOLOv4 结构中,关于 CSP 块的实现与在 CSPNet paper 中的设计还是有点差异的,在 CSPNet 论文中,是将输入特征图的通道按通道数平均分为 2 份,例如输入特征图的通道数如果为 128,那么就会分为 2 个 64 的特征图,而在 YOLOv4 中,并不是在通道上进行切分,而是采用了两个 1 x 1 的卷积通道数为 64 的卷积核来实现对输入特征图进行切分的效果的。
CSP 网络的 receptive field size 要比输入 size 还要大,可以准确的将问题转换 mapping function 问题。通常都会降低 20% 运算量,并且会保持精度,甚至有所提高。
模块化
现在网络结构都是在一些基础模块上,进行组合出更为复杂的模块,然后利用这些模块堆叠出一个网络来。所以在开始介绍整体网络架构前,我们需要把这些小的模块的基本原理给大家说清楚。
- CBM 就是标准卷积结构,在卷积层后使用 BN 层,接下来是激活函数,在 CSPDarknet 中使用的是 Mish 激活函数
Mish 激活函数
在 Mish 出现之前,大部分计算视觉任务都会选择 ReLU 整个激活函数,那么为什么 Mish 能够取代 ReLU 呢?
关于 Mish 函数的优点是因为在小于零情况并没有处理为 0 而是保留对小于 0 部分的记忆,这样也就是可以更
-
ResUnit 是残差块的结构,第一个 CBM 是 1x1 的卷积层,然后是一个 3x3 卷积层,接下来将输入特征图和经过两次卷积的特征图在通道上进行 concate
-
CSP 结构是首先通过 CBM 卷积对输入特征图进行通道增倍、且通过步长为 2 进行一次下采样,特征图的尺寸缩减原来的 1/2。然后采用通过两个 1x1 输出通道数为原通道数 1/2 卷积将输入特征图按通道进行拆分后,一个进入残差块后在进行 1x1 的卷积后和另一半进行 concat
CSPDarknet53
右侧为 CSPDarknet53 网络结构图,首先经过 CBM 卷积块,将通道数增加到 32,然后就是一系列的 CSP 块,CSP1、CSP2、CSP8、CSP8 和 CSP5 (字母后面数字1、2、8、4 表示 CSP 块中残差单元个数)进行 5 次下采样,输出特征图为原图 1/32,也就是 13x13 大小特征图。
不过在这些 CSP 中,CSP1 与其他 CSP 结构有所不同
- 在按通道拆分两个 CBM 模块的通道数并不是输入 1/2 而是和输入特征图通道数保持一致还是 64
- 还有就是在残差单元(ResUnit)中,在第一个 CBM 卷积块对通道进行减半处理,为 64 的 1/2 32,正常 CSP 结构中应该通道数是保持一致的
这一次右侧 CSP 结构是标准的结构图,其他 CSP 都是和这 CSP 保持一致,所以我们就以这个作为例子来简单说明一下 CSP 结构。
Neck 部分
目标检测的 Neck 部分主要用于将不同尺寸特征图中特征信息进行有效的融合。
SPP(Spatial Pyramid Pooliing)
SPP 模块是比较简单,用于不同尺度的特征的融合。这里 SPP 和 SPPNet 中结构不一样, 分别是池化大小 5x5、9x9 和 13x13 池化大小的下采样,这里对特征图进行填充处理以确保经过池化后特征图大小并没有发生变化,然后在和输入特征图在通道维度上做一个 concat。
帮助网络增加视野域,通过很大的 maxpooling 来增加视野域。
这样简单结构却能够给模型预测结果准确度有一个客观提升。大家可能已经注意到网络中只在 13x13 特征层输出加上 SPP 层,那么是不是可以在每个输出特征层都加上一个 SPP 层,其实也是可以的,不过从一些实验结果来看,在所有输出特征图上都添加 SPP 层效果提升并不会比只在一个输出特征图上添加 SPP 有显著提升,差异不大,而且添加了多个 SPP 层可能会让推理时间变慢。
Head 模块
FAN
在之前介绍 FPN 是将深层网络输出特征图,经过上采样融合到
主干网络用于特征提取,而在 FAN 结构是对提取特征进行一个融合(聚集),在(a)表示包含高层语义信息特征图通过不断上采样和低层语义特征进行融合。而在(b)中,是将低层语义信息向高层语义信息进行融合。
在 FAN 论文中,特征融合方式是采用相加方式,而在 YOLOv4 中采用 concat 特征图的方式来进行融合。为什么用 concat 而不是做逐点相加 FAN 1024 和 1024 两边都会有 1024 运算,整合后特征图 concat 可能只要1/2 计算量。
在 YOLOv4 也是输出 3 不同尺度的特征图,分别是 13 x 13、26 x 26 和 52 x 52 3 个不同尺度的特征图,通道数为 anchors_on_scale * (5 + class_number)。我们先来 13 x 13 特征图输出过程
首先经过 SPP ,然后在经过 ConvSet1 也就是一系列卷积块(这里卷积块采用激活函数是 LeakyReLU 而不是 Mish 这个激活函数。
注意在输出会从底层经过一系列下采样得到特征图进行 concat 方式融合后再输出,输出前会再经过一个卷积块(CBL) 随后是用一个 1x1 步长为 1 的卷积,注意这里不是卷积块,后面没有 BN 和激活层来做检测,得到输出结果为 batch_size * 13 * 13 * anchors_on_scales * (5 + classes_numbers)
我们再看 26x26 特征图输出路线,从特征图CSP8(26x26x512)出发一路来到检测头,整个过程中,特征图经过了 2 次 concat 随意融合到了高层和低层语义信息,中途有经过了 ConvSet3 经过反复卷积对语义又进行了多次提炼从而得到更加抽象的信息。
代码实现
总结
在这篇文章,尽可能详尽介绍了 YOLOv4 的网络结构,以及在 YOLOv4 中都采用哪些当时流行的技术。说到这里,不仅要感叹一下,像 YOLOv4 这样可以有效地将当时流行技术整合到模型的先例也不多。很多时候我们虽然知道了一些新的技术、新的概念,却不知道应该如何将其应用到现有的模型优化上,这也是最近看到 AI 相关岗位招聘信息上,都是需要一些具有一定优化模型能力的人。其实从选取模型、模型训练到部署,到落地这段路想要走得好走得稳并非易事。
在今天文章中,对于 YOLOv4 中采用技术和方法只是给出了简单说明,并没有深入其背后原理,也没有对其为什么有效而给出解释说明,这些是在随后更新和发文的方向。
我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿。