上一篇文章YOLO学习笔记——目标检测算法的简介(RCNN, SSD, YOLO)中我们简单的介绍了目标检测算法及其特点。目标检测算法大体上分为一阶段检测和二阶段检测。二阶段检测的算法,需要首先计算锚框,然后预测锚框内的物体类别和坐标位置。二阶段算法以R-CNN系列为代表,这类算法速度慢,精度高。然而yolo属于一阶段算法,将图像输入模型,模型直接计算得出物体的概率值和锚框位置信息。这类算法主要有:SSD、YOLO等,虽然精度上会损失一些,但其速度更快。
这篇笔记,我们一起来讨论一下YOLO V3这个网络的结构和细节。
我的其他笔记链接:
- 使用K-means算法寻找yolo的锚框 - 简书 (jianshu.com)
-
如何将图片和锚框一起resize——python代码实现 - 简书 (jianshu.com)
3.YOLO学习笔记——目标检测算法的简介(RCNN, SSD, YOLO) - 简书 (jianshu.com)
4.YOLOv3网络结构和基础知识学习笔记 - 简书 (jianshu.com) - 如何制作YOLOv3模型训练数据集? - 简书 (jianshu.com)
- 如何训练YOLOv3模型?pytorch代码实现 - 简书 (jianshu.com)
- YOLOv3、YOLOv4、YOLOv5之间的区别 - 简书 (jianshu.com)
这是YOLOv3的GitHub链接:GitHub - ultralytics/yolov3: YOLOv3 in PyTorch > ONNX > CoreML > TFLite
一、网络结构
(1)darknet 53 主干网络
yolo v3的主干网络是darknet 53,这也是一个全卷积结构,其主要作用为特征提取。输入图像大小为416*416,darknet53中的黑色方框表示一个残差结构。方框之间使用卷积层对特征图进行下采样。
主干网络一共经过5次下采样,输出的特征图宽高为13*13。那么darknet 53就相当于一个32*32的卷积核,卷积核的步长为32*32。这也就是我们前面所说的,将图像平均分成13*13个格子。对每一个各自进行预测概率值和坐标位置。
由此可知,yolo的输入图像的最小尺寸为32。并且输入的图像必须是32的整数倍。
那么还有一个小问题:为什么yolo中的下采样选择使用卷积,而不是用池化呢?答案是,为了减少特征的丢失,因为使用池化操作会损失较多的特征信息。虽然在卷积的过程中,图片宽高发生了改变,但同时通道数量也增加了。
(2)yolo的输出
yolo一共有三个输出分别是13*13,26*26,52*52,为什么会有三个尺度呢?是因为13*13,就相当于把原始图像划分为了13*13个小窗口,每隔窗口都进行概率预测和坐标检测。然而这是相对于较大目标而设计的,可能有很多小目标就难以被检测出来。于是就出现了26*26以及52*52的窗口分别对中型目标和小目标进行预测。
这三个输出结构又叫做三个侦测头。分别代表不同窗口尺寸下的目标检测。并且在不同的尺寸下,会包含三个不同形状的框。如图所示:
所以我们将尺寸和锚框形状组合起来,yolo一共有9个窗口,形状不同,尺寸也不同。
接下来,我们将数据代入进去,观察数据的输出到底是什么形状的呢?假设这是有十类目标的目标检测任务,一个批量的数据为N个,每个数据都是彩色图片,宽高均为416。那么输入神经网络的数据的形状为(N, 3, 416, 416),经过主干网络的计算后,输出的形状为(N, 1024, 13, 13)。接下来,我们进观察第一个侦测头的输出结果。其形状为:(N, 45, 13,13)。
那么宽高为13都是好理解的,但是45个通道是撒子意思嘞?45 = 3 * (10 + 5)。3表示的是有三个不同形状窗口,10表示10个分类,因为yolo分类使用的是onehot编码。5表示的是一个置信度和四个位置坐标(包括:中心点坐标cx、cy、预测框的宽、预测框的宽高)。
可能这样的文字描述也说明的不是很清楚,那么我们直接上图:
三个侦测头分别的输出形状是(N, 45, 13,13)、(N, 45, 26,26)、(N, 45, 52,52)。现在我们弄清楚了神经网络的输出了。
那么这里就有一个问题:有没有一种可能,在不同的三个形状窗口都能够检测到某一个物体,那么就会出现重叠的框。那怎么进行选择呢?其实我们仅选择置信度最大的框进行保留。
(3)如何将输出的位置转化到原图中?
神经网络中输出的中心点坐标、预测框的宽高是一个偏移量。那么什么是偏移量呢?所谓偏移量,就是一个点相对于一个参考点发生的偏移。例如:前面我们不是说过,我们要将图片均匀的划分成13*13, 26*26, 52*52的格子吗?我们会给每一个格子分配不同形状的3个锚框,这3个我们设定的锚框就是参考点,而神经网络计算出来的值是真实框相对于锚框的偏移。所以我们最后还是要讲这些偏移量抓换成原图中的位置
假设将一张狗狗的图片输入到yolo v3中获得上图所示的结果,一个5*5的矩阵(yolo输出应为: 13*13,这里只是举个例子)检测到中心点的像素位于索引为(2,2)的特征图上,并将位于这个位置上的所有通道上的值取出,取出相对应的置信度和中心点的偏移量值。那么中心点在原图中的位置等于索引值与偏移量的和乘以一个格子在原图中的宽度。
这样就能将两个坐标值换算出来了。
那么宽和高又是怎么换算的呢?
但是YOLO中所使用的偏移量有一点区别:
tw就是偏移量,根据上式公式,我们就能求出框在原图中的宽度啦。
之所以加上自然常数的作用是,为了使得偏移量有正有负。如果偏移量按照之前的公式计算,偏移量始终是正数,然而神经网络的输出往往是有正有负的,这并不符合常理,所以我们将偏移量写成这种形式。
(4)锚框的尺寸如何选取?
上面小结说到我们可以根据参考的锚框和偏移量,反算出预测框在原图中的中心点位置和宽高。但是这个参考的锚框我们该怎样去选取呢?
- 人为的给定,不推荐这样做,我们要知道不同任务中,物体的大小和形状都不一样
- 建议框来源于数据集中,利用算法计算出锚框的大小和形状。例如:使用K-means算法从标签中自动选取9个锚框。
可参考:K-means聚类生成Anchor box - 知乎 (zhihu.com)、代码实现
注意:在k-means算法中,使用欧氏距离去衡量两个锚框的相似度是不合适的。我们使用的是1 - IOU
,当两个框的IOU越大说明两个框的距离越小。
根据COCO数据集给出的锚框:
- 小目标:[10,13,16,30,33,23]
- 中目标:[30,61,62,45,59,119]
- 大目标:[116,90,156,198,373,326]
那么这篇笔记主要讨论了yolo v3的神经网络结构还有其输出,还简单介绍了一下其中的一些小细节,接下来我们就可以试试怎么去修改代码,让它帮我们实现一些功能啦。
其对应代码可以从文章开头给出的github上下载,看着代码进行学习。