零、图像基本概念
图像表示为二维的矩阵

灰阶图像
0-255,0表示黑色,255表示白色,其余表示灰色。
图像的坐标轴


彩色图像

注意:颜色信息对于任务有时候有用,有时候没用。
一、什么是目标检测
目标检测是计算机视觉中的经典的原子问题,即通过输入的图像来完成物体的检测,它需要解决两个问题:
- 物体在哪里
- 物体是什么
目标检测算法的传统实现 sift hog 等算法。这些算法的大致逻辑如下:
- 通过人定义的算法和规则提炼关键点。
- 通过人设计的算子和计算流程计算关键点的向量。
- 通过匹配算法对向量进行匹配,返回可能匹配到的点。
2012年后,深度学习开始火爆,深度学习开始在计算机视觉领域开始发展。
You Only Look Once 是目标检测算法中,one stage 算法的开端。You Only Look Once 即你只看一眼,表达的含义是只用一个步骤就完成目标检测的过程。
目标检测用矩阵框来框出待检测的目标。在二维图像中,表示一个矩阵框至少需要 2 个点(即 4 个值)。有以下两种表示的方式?

哪种表示方式更好呢?
在目标检测中,有几个很重要的名词:
- bounding box (简称 bbox)
- ground truth bounding box(简称 gd)
- predicted bounding box (简称 pd)

如何度量 pd 相对于 gd 的好坏呢?经典的方法,就是 IOU 。

yolo 系列和大部分的目标检测算法的本质思想来源于人进行目标检测的过程。

通过“部分”的图像信息就能推测出图像大致位置。
- 对识别的物体需要有先验的经验信息。
- “部分”图像离 GroundTruth 不能太“远”。
- 能区分背景和待测物体。
重新审视一下神经网络:

以分类网络举例,神经网络的 logit 输出可以看做是特征提取的过程。最后一层接的就是简单的逻辑斯特回归(线性模型)。
后面所有的 yolo 系列算法会从
- 网络结构
- 损失函数
- 训练过程
- 测试过程
四个方面进行讲解。其中所有的网络结构均以 darknet 官方的 github 库为准1 ,为简略图,目的是方便大家理解和学习。网络结构的详细图在对应的文件附件中。
二、 YoloV1 算法
在 two stage 算法中,需要先找出候选区,然后识别候选区中的对象。
yolo 是 one stage 算法的开篇之作,它并没有真正的去掉候选区,而是划分了“负责”区域(7\(\times\)7 个网格)。在每个“负责”区域会预测两个 bbox ,所以一共会预测 98 个 bbox。
RCNN 系列是先通过算法找到候选区,然后对候选区进行边框回归,得到最终的 bbox。yolov1 则是直接对负责区域直接进行判别和回归,一步到位的 bbox。
回想一下前面说的关于人进行目标检测的本质。“负责”区域可能包含“部分”我们关心的待检对象,通过一系列复杂运算,推理出物体的 bbox。

2.1 网络结构

YoloV1 的网络结构如上图所示。
在 YoloV1 才出来时,作者并没有加上卷积的 BatchNorm,这里是作者修复后版本,加上了 BatchNorm。
最左边是图片的输入,输入图片是 448\(\times\)448\(\times\)3 (注意,ImageNet 的数据是 224\(\times\)224\(\times\)3)
从输入图像开始,一直到第一行最右边的立方体 \(7\times7\times1024\) 都是通过卷积得到对应的 feature map 。如果直接将 \(7\times7\times1024\) flatten 对接 12544 的全连接,这个参数量是非常可怕的。
所以作者选择了通过局部连接层(卷积 = 局部连接+共享参数)。然后 flatten 后接 dropout 层,最后接 1715 的全连接,再reshape 成 \(7\times7\times35\) 的输出 tensor。(github 上有的代码实现会在两个全连接中,再增加一个 512 的全连接,以减少参数量)
可以看到,整个网络结构使用的是 leakyrelu 激活函数。最后一层的输出没使用激活函数,所以输出是 logit。
按照我们前面对神经网络的理解,可以把 \(7\times7\times35\) 看成是提炼出的特征。
注意,由于最后输出的 tensor 是通过全连接得到的。所以每一个像素点的包含了整个输入图片的信息。
分析一下 \(7\times7\times1024\) 的立方体和输入原始图片的感受野对应关系。该立方体的每一个点对应的感受野是有交集的,并不是相互之间不相交的。这里要特别注意。
最后来分析一下 \(7\times7\times35\) 的 tensor 表示的含义 。每一个 \(1\times1\times35\) 会“负责”原始图像 \(64\times64\) 的区域。输出的 30 维向量。主要包含以下的信息。

这里有两个 Bbbox,Bbox1(2) 的置信度和 Bbox1(2) 坐标对应。
Bbox 置信度:
Bbox 的置信度的公式如下:
\[Bbox 置信度 = P(Object)*IOU_{pred}^{truth}\]
\(P(Object)\) 表示是否存在待检对象。在训练阶段,如果这个像素点的“负责”的区域没有 gd,则 \(P(Object) = 0\) 反之为 1 。然后就是计算 gd 和 pd 的 IOU 的值。
Bbox 置信度表达了这个像素点对应的“负责”区域有没有,且 Bbox 准不准的程度。反应了 Bbox 的准确性程度。
要注意,Bbox置信度虽然有多个,(一个像素点对应的负责区域会有多个 Bbox,比如这里是2个)。但我们只看和 GroudTruth IOU 大的那个,另外一个自动设定为 0 。
Bbox 置信度的大小期望在 [0,1] 之间。
具体对象的概率:
具体对象的概率公式如下:
\[P(c_i|object) = 0.6\]
表示这个像素点“负责“原始图像的区域“存在”对应对象的概率。注意,这里的前提是该网格中存在待检测的对象。每一个值的大小期望在 [0,1] 之间。
如何定义前面说的“存在”对应对象呢?如果原始图像的待检目标的 bbox 的中心点在对应的“负责”区域内。则表示存在对应对象。

Bbox 的坐标:
2 个 Bbox 需要 8 个值进行存储。
\[(b_x,b_y,b_w,b_h)\times2\]
我们主要需要看一下 gd 的 Bbox 的坐标表示。 gd 的 Bbox 坐标会做一些变换(编码)。
\(g_w,g_h\) 会除以图像对应的 \(img_w,img_h\),从而使得 \(g_w,g_h\) 归一化到 [0,1] 之间。
直接让 \(b_x,b_y\) 回归 gd 的 \(g_x,g_y\) 会有一定难度,既然我们已知 \(g_x,g_y\) 一定在对应的“负责”区域内。所以我们将 \(g_x,g_y\) 相对于负责区域的左上角坐标进行归一化,从而使得 \(g_x,g_y\) 也归一化到 [0,1] 之间。

为什么我们需要两个 Bbox 来进行预测呢?
在训练时,我们希望两个框同时工作,但真正计算损失的时候,我们只去对 IOU 最大的框进行梯度下降和修正。类似于一个相同的工作,让两个人一起做,为了保障工作正常完成。在训练最后,会发现两个框意见开始出现了分工,比如一个框倾向于去检测细长型的物体。另一个框倾向于去检测扁宽型的物体。
总结一下就是,在训练阶段,输出的两个 Bbox 只会选择其中一个参与损失的计算(和 gd IOU大的那个)。
在测试阶段,输出的两个 Bbox 只有一个有实际预测的意义。
通过前面讲解,可以看到 YoloV1 至多只能预测 49 个目标,即每个“负责”区域输出一个目标。
注意!我们可以使用不止 2 个 Bbox,理论上 Bbox 越多效果越好,但是效率会降低。作者取两个 Bbox 的原因是因为性能和效率的取舍。
2.2 损失函数
损失函数章节我们关心两个东西。一是损失函数的输入,也就是标签值(gd)和预测值(pd)。二是损失函数的具体形式。
通过 2.1 关于 Bbox 坐标的讲解,我们知道训练样本的标签是动态变化的,和 yolov1 预测出的 bbox 的 iou 有关。


公式中
\(1_i^{obj}\) 表示网格 i 中存在对象。
\(1_{ij}^{obj}\) 表示 网格 i 的第 j 个 Bbox 中存在对象。
\(1_{ij}^{noobj}\)表示网格 i 的第 j 个 Bbox 中不存在对象。
yoloV1 将预测和真实标签看做回归问题。使用均方误差作为损失函数。
思考一下: 为什么 yolov1 概率处的损失函数不使用交叉熵?
对象分类误差:
\(1_i^{obj}\) 意味着存在对象的网格才计入误差,不存在的不计入损失。
BBox 置信度误差:
\(1_{ij}^{obj}\) 意味着两个 Bbox 只有被选择的那个(IOU比较大的)才会计入损失。
\(1_{ij}^{noobj}\) 意味着不存在对象的 Bbox 置信度应该要尽量的低。
Bbox 位置误差:
\(1_{ij}^{obj}\) 意味着两个 Bbox 只有被选择的那个(IOU比较大的)才会计入损失。
而且 \(x,y\) 和 \(w,h\) 略有不通。\(w,h\) 有更好,目的是为了减少 \(w,h\) 的敏感度(检测框大小对损失的影响)。
最后 \(\lambda_{coord}\) 和 \(\lambda_{noobj}\) 是超参数,用来控制不同项的比重。论文中 \(\lambda_{coord} =5, \lambda_{noobj}=0.5\)
2.3 训练过程
YoloV1 先使用 ImageNet 数据集对前几十层的卷积网络进行预训练,然后使用完整的网络,在PASCAL VOC数据集上进行对象识别和定位的训练和预测,在训练过程中,加入了数据增强,lrelu 的值设定为 0.1 ,dropout的值设定为 0.5 。
训练过程中的标签生成如前面所述,这里不再赘述。
2.4 测试过程
在测试阶段,Bbox置信度和对象概率相乘,得到的是存在对应类别目标的概率:
\[p(c_i|Object)*p(Object)*IOU_{pred}^{truth}=p(c_i)*IOU_{pred}^{truth}\]
对每一个网格的每一个 bbox 执行同样操作: 7x7x2 = 98 bbox (每个bbox既有对应的class信息又有坐标信息)
得到 98 个 bbox 的信息后,用 NMS 算法去掉重复率较大的 bounding box 。
注意,在测试阶段需要将 dropout 也开启为测试模式。(如果有 BatchNorm 需要将 BatchNorm 也开启为测试模式,原始论文中没有 BatchNorm 层)。
NMS计算方法如下:
- 设置一个Score的阈值,低于该阈值的候选对象排除掉(将该Score设为0)
- 遍历每一个对象类别
- 遍历该对象的98个得分
- 找到Score最大的那个对象及其bounding box,添加到输出列表
- 对每个Score不为0的候选对象,计算其与上面2.1.1输出对象的bounding box的IOU
- 根据预先设置的IOU阈值,所有高于该阈值(重叠度较高)的候选对象排除掉(将Score设为0)
- 如果所有bounding box要么在输出列表中,要么Score=0,则该对象类别的NMS完成,返回步骤2处理下一种对象
- 输出列表即为预测的对象
最后预测的结果,需要还原到原始图像的坐标值。(预测的 bbox 都是做了归一化的)
在结束 YoloV1 讲解之前,我们再提一个小点:
最后输出的 \(7\times7\times35 \) 立方体其实就是图片的每一个“负责区域”的特征,这个特征无法直接去拟合最终的 gd。所以我们需要对 gd 进行适当的“编码”。然后才能通过一个线性层进行拟合。 yolov1 的“编码”方式并不是很好,可以思考一下为什么?
二、 YoloV2 算法
YoloV2 至少有两个不同的版本,Yolo9000 和 YoloV2'(为了加以区别,我加上'这个符号)。总的说来 YoloV2 是 YoloV3 正式发布前的试水,网络的结构变化不大,但也有四个很有意思的创新和启发。
- 引入 anchor(借鉴 two stage 的算法)
- 改变 gd 的编码方式
- 引入 PA 层(开始尝试 feature map的其他融合方式)
- 联合训练
BTW, yolov9000 这个名字是因为它扩展到了能检测 9000 种不同对象。
2.1 网络结构
首先来看一下 anchor 。在 YoloV1 中我们提过,两个 bbox 会各司其职,起到不同的预测 bbox 的效果。比如一个预测长的,一个预测宽的。所以为何不直接先指定一个先验框,然后最终的检测 bbox 就是在先验框(anchor)的基础上去做修正(two stage 中就是这种思想)

首先我们会将原始图片划分成 \(S \times S\) 的区域,然后在每个“负责”区域上设定先验的 anchor 框。这些框的长宽比均不相同。注意,原始图的 anchor 将映射到最终的输出的 feature map 上,因为后面我们的损失计算是相对于 feature map 上的 anchor 计算的。(如何映射呢?最简单的除以 scale 的倍数即可)
还有一个问题,anchor 的长宽到底该如何设定呢?在 two stage 的算法中都是人为设置。在 YoloV2 作者创新的用 Kmeans 对训练数据聚类得到。

注意这里 Kmeans 的距离定义如下:
\[ dis(A,B) = 1-IOU(A,B)\]
下面我们来分别看一下 Yolo9000 和 Yolov2 的网络结构图

Yolo9000 的结构和 YoloV1 差距不大,非常不一样的地方在于,最后的输出层改为了 \(1\times1\) 卷积,大大减少了参数量。

相比于 Yolo9000,YoloV2 的最大不同在于引入了 \(V2\) 这个模块(这个名字是我自己起的)这个模块的目的期望减少 feature map 空间维度的同时尽量不损失信息。而且它具有了 split transform merge 的形式,一定程度上开始有了特征融合的感觉。
最后是网络的输出 tensor,这个输出 tensor 的含义如下:
\[N\times N\times B\times(C+5)\]
N 表示输出特征图空间维度,B 表示 anchor 的个数,C 表示识别的类别个数,5 表示 Bbox 的坐标(4个)和 Bbox 的置信度(1个)。
下面我们就来详细的说一下 Bbox 的坐标,和 YoloV1 的思想一样,我们要将 gd 的 Bbox 坐标进行编码(不同的是,现在是相对于 anchor 进行编码)
假设输出 tensor 的 Bbox 坐标为 \((t_x,t_y,t_w,t_h)\) 。由于最后的 tensor 只是线性层,所以理论上每一个值的值域 \([-\infty,\infty]\)。
假设 gd 的映射到最后 feature map 的 Bbox 坐标为 \((g_x,g_y,g_w,g_h)\)。现在我们需要将 \((g_x,g_y,g_w,g_h)\) 相对于 anchor 进行编码。
假设 anchor 所在“负责”区域在最后 feature map 上对应的左上角坐标为 \((c_x,c_y)\),对应的长宽为 \(p_w,p_h\)。(注意这里用的是所在“负责”区域的左上交坐标)
从 anchor 的 Bbox 变换到 gd 的 Bbox 的最简单方式就是对点做平移,对长宽分别做放缩。
\[x坐标平移 = g_x - c_x\\
y坐标平移 = g_y - c_y\\
宽的放缩 = \frac{g_w}{p_w}\\
长的放缩 = \frac{g_h}{p_h}\]
直接用 \((t_x,t_y,t_w,t_h)\) 去拟合上面几个值是不合理的,因为 \((t_x,t_y,t_w,t_h)\) 的取值范围是 \([-\infty,\infty]\) 。但坐标平移范围在 \([0,1]\)(因为最后 feature map 的相邻点距离是一个单位)。长宽的放缩范围在 \([0,\infty]\)。不能把无约束优化问题直接去套有约束优化问题。所以需要进一步编码。
\[t_x = \sigma^{-1}( g_x - c_x)\\
t_y = \sigma^{-1}(g_y - c_y)\\
t_w = log(\frac{g_w}{p_w})\\
t_h = log(\frac{g_h}{p_h})\]
最后经过反向的解码就得到了原始论文的样子

\((b_x,b_y,b_w,b_h)\) 是在最后一层 feature map 上的坐标。为了方便收敛,也为了后这一行数据的类别概率,置信度量纲保持一致,我们需要将 \((b_x,b_y,b_w,b_h)\) 归一化,如何归一化呢?
方法很简单,计算 \((b_x,b_y,b_w,b_h)\) 相对于最后一层 feature map 的相对大小。 \((\frac{b_x}{f_w},\frac{b_y}{f_h},\frac{b_w}{f_w},\frac{b_h}{f_h})\) , \((f_w,f_h)\) 表示最后一层 feature map 的宽和高。
在测试阶段,只需要将这个值乘到原始图像的大小上,就能得到原始的图像上的 pd 框。
2.2 损失函数
yoloV2 的损失函数在论文中并没有详细说明,需要通过源码进行比对分析。总的说来和 yoloV1 的基本思想保持一致。

详细解读可以参考连接:
关于这个损失函数,有几个地方需要强调一下:
- 为了解决 \(w,h\) 的敏感度(检测框大小对损失的影响)的问题。这里采用的是 \((2-w_i*h_i)\) 去掉了,在YoloV1 中使用的是 \(w_i,h_i\) 的根号。\(w_i,h_i\) 已经是解码后的值,所以在 \([0,1]\) 之间。
- 类别变量使用 softmax, bbox 的置信域使用 sigmoid(因为编码前的 tx 等都属于负无穷到正无穷了,编码后控制在了 [0,1] 之间,为了使得量纲一致)。但损失仍然使用的 MSE。
- 最后一项是去修正没有预测准的框,为了加速收敛。仅在前一万多次迭代的时候使用。
2.3 训练过程
YoloV2 的训练过程比较有意思,从这里能感受到“炼丹”的不易。
高分辨率预训练:
图像分类的训练样本很多,而标注了边框的用于训练对象检测的样本相比而言就比较少了,因为标注边框的人工成本比较高。所以对象检测模型通常都先用图像分类样本训练卷积层,提取图像特征。但这引出的另一个问题是,图像分类样本的分辨率不是很高。所以YOLO v1使用ImageNet的图像分类样本采用 \(224\times 224\) 作为输入,来训练CNN卷积层。然后在训练对象检测时,检测用的图像样本采用更高分辨率的 \(448\times 448\) 的图像作为输入。但这样切换对模型性能有一定影响。
所以YOLO2在采用 \(224\times 224\) 图像进行分类模型预训练后,再采用 \(448\times 448\) 的高分辨率样本对分类模型进行微调(10个epoch),使网络特征逐渐适应 \(448\times 448\) 的分辨率。然后再使用 \(448\times 448\) 的检测样本进行训练,缓解了分辨率突然切换造成的影响。
多尺度训练:
由于是全卷积网络,其降采样的倍数是 32 。所以可以支持输入任何的 32 倍数尺寸的图片。于是可以输入不同尺寸的图片进行训练。
每10个batch之后,就将图片resize成{320, 352, ..., 608}中的一种。
联合训练:
联合训练方法思路简单清晰,Yolov2中物体矩形框生成,不依赖于物理类别预测,二者同时独立进行。当输入是检测数据集时,标注信息有类别、有位置,那么对整个loss函数计算loss,进行反向传播;当输入图片只包含分类信息时,loss函数只计算分类loss,其余部分loss为零。当然,一般的训练策略为,先在检测数据集上训练一定的epoch,待预测框的loss基本稳定后,再联合分类数据集、检测数据集进行交替训练,同时为了分类、检测数据量平衡,作者对coco数据集进行了上采样,使得coco数据总数和 ImageNet 大致相同。
注意:联合训练和预训练是不同的东西。
由于 coco 和 Imagenet 数据类别不一致的问题,为了能正常联合训练,作者引入了 wordtree。

wordtree 在工业界上是很有大的启发。因为真实场景下的标准数据少,如何最大的利用已有标准数据,wordtree 和联合训练是很有意思的想法。
2.4 测试过程
同 YoloV1,最后注意下一下预测框需要还原到原始图像上,这个前面有提,这里不再赘述。
三、YoloV3
YoloV3 的网络框架有了很大的改变,它在“特征融合”上做了很大的改进。YoloV3 也有两个版本,一个是普通版,一个是YoloV3-spp。YoloV3-spp 版本很重要,因为它直接启发了后续的 YoloV4。
YoloV3 采用和 YoloV2 同样的 gd 编码方式。并也采用了 anchor 的思想。它借鉴了特征图金字塔的思想,用不同尺度的特征图来检测目标。小尺寸特征图感受野大,于是用来检测大尺寸的物体,大尺寸的特征图感受野小,检测小尺寸的物体。(其实也算是借鉴了 SSD 的思想)
3.1 网络框架

YoloV3 总共输出 3 个不同尺度的特征图,分别是 \(19\times 19,38\times38,76\times76\)
对于这个网络结构如果熟悉 FPN 的读者,应该不难看出,后半部分的“特征融合”就是借鉴了 FPN 网络。

接下来是 YoloV3-SPP,YoloV3-SPP 的网络结构和 YoloV3 几乎一致,唯一不同的是,YoloV3 借鉴 SPPNET 的思想,增加了 SPP 模块。

这个思想其实很简单,就是以不同的 kernel (\(1\times1,5\times5,9\times9,13\times13\))去 pooling feature map 然后 concat 起来(为了保证 concat 需要进行 padding的操作),以得到不同尺度的信息。仍然满足 split transform merge 的范式。
关于 anchor ,YoloV3 也使用了 YoloV2 的技巧,使用 Kmeans 进行聚类作为先验框(距离仍然使用 \(1-IOU\) )。使用 COCO 数据集,一共聚类得到了 9 个 \(w\timesh\)的框 (10×13),(16×30),(33×23),(30×61),(62×45),(59× 119), (116 × 90), (156 × 198),(373 × 326) 。
注意看输出的立方体 tensor (\(255 = 3\times(80+4+1)\))。所以每一个立方体 tensor 只会对应三类先验框。前面说过,大的 tensor 感受野小适合小目标检测,需要小的 anchor。反之小的tensor 感受野大适合大目标检测,需要大的 anchor。
特征图 \(19\times 19\),对应(116 × 90), (156 × 198),(373 × 326) anchor。
特征图\(38\times 38\),对应(30×61),(62×45),(59× 119) anchor。
特征图\(76\times 76\),对应(10×13),(16×30),(33×23) anchor。
gd 的编码方式和解码方式和 YoloV2 保持一致,略有不同的是,类别概率输出使用 sigmoid,不再是 softmax。这样一个“负责”区域可以同时输出多个类别。
3.2 损失函数
损失函数和 YoloV2 基本保持一致

不同的地方在于,对于类别和置信度的损失使用交叉熵。
3.3 训练过程
YoloV3 的训练过程,特别是样本的选择和 V1 和 V2 已经完全不一样了。
在 V1 和 V2 中是看 gd 中心所落的负责区域来确定 gd 由哪个点来负责。由于 V3 中有多个最终的 feature map。使用这种策略可能会导致矛盾(即一个 gd 同时属于多个点负责)。所以需要新的方式确定样本由哪个点的区域负责。原则很简单:
所有预测的 pd 中和 gd 的 IOU 最大的那个就是正样本。
作者还创新的把预测 pd 分成三类:
- 正例:产生回归框 loss 和类别置信度 loss。
- 负例:只产生置信度 loss。
- 忽略:不产生任何 loss。
正例:
对任意的 gd,与所有的 pd 计算IOU,IOU 最大那个就是正例。一个pd,只能分配给一个gd。比如第一个 gd 已经匹配了一个正例的 pd,那么下一个 gd,需要在剩下的 pd 中寻找 IOU 最大的作为正例。
负例:
除正例以外(与 gd 计算后 IOU 最大的检测框,但是IOU小于阈值,仍为正例),与全部 gd 的 IOU 都小于阈值(论文中为 0.5),则为负例。
忽略:
除正例以外,与任意一个 gd 的 IOU 大于阈值(论文中为 0.5),则为忽略。
在 YoloV3 中置信域标签直接设置为 1 和0。而不是 YoloV1 的 IOU 值。原因是假设 iou 是0.8,但学习到的可能只有 0.6 总是会低一些。不如直接将标签设为 1 (学习到的可能就是 0.8)。
3.4 测试过程
由于有三个特征图,所以需要对三个特征图分别进行预测。
三个特征图一共可以出预测 19 × 19 × 3 + 38 × 38 × 3 + 76 × 76 × 3 = 22743 个 pd 坐标以及对应的类别和置信度。
测试时,选取一个置信度阈值,过滤掉低阈值 box,经过 NMS(非极大值抑制),输出整个网络的预测结果。注意最后要还原到原始坐标。该改成测试模式的模块需要改成测试模式(比如 BatchNorm)
四、YoloV4
Yolo 系列的原作者在推出了 YoloV3 后宣布退出 CV 界。俄罗斯的程序员 Alexey Bochkovskiy 凭借自己的摸索复现了 Yolo 系列全部模型,并总结了最接近几年目标检测的各种套路。就在前几个推出了 YoloV4.
YoloV4 将最近几年 CV 界大量的trick 集中在一套模型中。这篇论文不仅仅可以看作是一个模型的学习,更能看成是一个不错的文献总署。更有意思的是作者提出了 backbone,neck,head 的目标检测通用框架套路。
backbone, neck, head 其实非常的形象。它表示组成一个“人”的三个部分。从下到上就是 backbone, neck, head 。
- backbone:各类卷积网络,目的是对原始图像做初步的特征提取。
- neck:各类结构,目的是从结构上做“特征的融合”。主要为解决小目标检测,重叠目标检测等问题。
- head:gd 编码,回归和解析。
YoloV3 就是一个典型的 backbone, neck, head 结构:

4.1 网络结构

Mish 激活函数:
\[ Mish=x * tanh(ln(1+e^x)) \]

Dropblock:
对 feature map 的“局部区域”进行丢弃。其实就是 cutout 的推广形式。

PANET结构:

和正常的 PANET 结构的区别:

特征图和 anchor 的对应方式和 YoloV3 一致。原则是大的特征图对应小的 anchor,小的特征图对应大的 anchor。
4.2 损失函数
YoloV4 的损失函数相比 YoloV3 有一定的变化,主要体现在 Bbox 边框回归的损失上。
YoloV1-3 都是使用 MSE 进行回归(只是编码方式不同)。作者认为这种损失是不合理的。因为 MSE 的 \(x,y,w,h\) 四者是解耦独立的。但实际上这四者并不独立,应该需要一个损失函数能捕捉到它们之间的相互关系。
IOU 能捕捉到他们之间的关系 (\(L_{iou} = 1-IOU\)),但 IOU 有很多的问题,后面有陆续的一些推广和改进,最终 YoloV4 选择了 CIOU ,CIOU 是 DIOU 的进一步延伸。
首先看一下 DIOU 对应的损失的公式:
\[L_{diou} = 1-IOU+\frac{\rho^2(b, b^{gt}) }{c^2}\]
\(b^{gt}\) 表示 gd 中心点,\(b\) 表示 pd 中心点,\(\rho^2\) 表示欧式距离。\(c\) 表示包含两个框的最小矩阵的对角线。

DIOU 考虑到了
- 两边框交集的面积
- 两边框中心点距离
但是没有考虑到长宽比。一个好的 pd,应该和 gd 的长宽比尽量保持一致。
于是我们有了 CIOU:



4.3 训练过程
- Mosaic数据增强
- cmBN
- SAT自对抗训练
4.4 测试过程
测试过程重要注意两点:
- 多个特征图需要检测,但是 \(78\times78\) 的特征图不参与检测。
- 使用 DIOU_NMS 代替 NMS 。
\[DIOU = IOU- \frac{\rho^2(b, b^{gt}) }{c^2}\]
这里 NMS 不用 CIOU 是合理的。因为只有 gd 和对应的 pd 才需要保持 bbox 的长宽比一致。在测试阶段,pd 之间并不一定长宽比一致。
需要yolov1-v4的pdf论文以及目标检测相关性的探讨请关注