YOLO官网:https://github.com/pjreddie/darknet
YOLO及You Only Look Once,是一种目标检测算法,目标检测任务的目标是找到图像中的所有感兴趣区域,并确定这些区域的位置和类别概率。目标检测领域的深度学习方法主要分为两大类(如图1):两阶段式(Two-stage)目标检测算法和单阶段式(One-stage)目标检测算法。两阶段式是先由算法生成一系列候选边界框作为样本,然后再通过卷积神经网络分类这些样本,也被称为基于区域的方法,例如R-CNN、Fast R-CNN、Faster R-CNN、R-FCN等;后者则是直接将目标边界定位问题转换成回归问题,图像会被缩放到同一尺寸,并以网格形式均等划分,模型仅需处理图像一次就能得到边界框坐标跟类概率,例如MultiBox、YOLO、SSD等。两种方法的区别也导致其性能也不同,前者在检测准确率和定位准确率方面更优,而后者胜在算法速度。YOLO是一个one-stage的目标检测算法,我们可以将其看作单一的回归问题。YOLO检测系统如图2所示。
图1 目标检测算法
图2 YOLO检测系统
YOLO的速度是非常快的,因此广泛应用于工业当中。主要基于YOLOv3来对YOLO算法进行学习,YOLOv3也会用到YOLOv1跟YOLOv2的内容,我们需要回顾一下YOLOv1跟YOLOv2的内容。
YOLOv1的论文中给出YOLO的主要思想如下:
1.我们的系统将输入图像划分为S×S网格,具有分而治之的思想。如果对象的中心落入网格单元中,则该网格单元负责检测该对象。如图三所示,狗的中心落在了第五行第二列的格子中(红点所示),那么这个格子负责预测狗这个对象。这里需要说明一下,网格只是划分图像中物体的位置,而不是将每个格子单独分离出来,所有的网格还是一个整体关系。因此,YOLO在训练和测试时都会全局考虑图像。
图3 S×S网格单元
2.每个网格需要预测B个bounding box,每个bounding box除了需要预测位置坐标、预测confidence值以外,还需要去预测C个类别的分数。confidence为置信度,是指bounding box中存在物体的概率;C与数据集的类别相关,VOC数据集为0,COCO数据集为80。每个边界框需要预测5个值:x, y, w, h, confidence。(x, y)表示预测框相对于网格单元边界的中心;(w, h)表示预测框相对于整个图像预测宽度和高度;置信度表示预测框与真实框之间的IOU。置信度的计算公式如下所示:
Pr(object)表示网格单元中是否有object,如果有object,则为1,否则为0。
IOU(b, object)表示真实框与预测框之间的交并比,表示bbox位置的准确性。
网络输出维度为:S×S×(B×5+C)。
在YOLOv1中,把图片划分为7×7的网格,每个网格预测两个Bounding Box,我们采用VOC数据集。因此,S=7,B=2,C=20。网络的输出维度为7×7×(2×(4+1)+20)=7×7×30。
YOLOv1的论文中给出YOLO的网络结构如下:
图4 YOLOv1网络模型
YOLOv1的基本网络结构采用的是GoogLeNet,我们的网络具有24个卷积层,其后是2个全连接层,但是没有使用GoogLeNet中的inception模块。网络的初始卷积层从图像中提取特征,而全连接层则预测输出概率和坐标。神经网络输入图像尺寸为448×448,经过一系列交替的1×1、3×3卷积,来提取图像中的特征,最后全连接层的输出维度7×7×30。
YOLO以一半的分辨率(224×224输入图像)对ImageNet分类任务上的卷积层进行预训练,然后将分辨率提高一倍以进行检测。
YOLO为了突破快速物体检测的界限,还训练了一种快速版本的YOLO。Fast YOLO使用的神经网络具有较少的卷积层(9个而不是24个),并且这些层中的卷积核较少。除了网络的规模外,YOLO和Fast YOLO之间的所有训练和测试参数都相同。
YOLOv1的论文中给出YOLO的损失函数如下:
YOLO的损失函数是比较复杂的,他由坐标损失、置信度损失和类别损失三部分组成。损失函数使用的是平方和误差(sum-square-error)。
表示该单元格i中的第j个边界框预测变量对此object预测负责。就是第i个网格单元中的B个bounding box中与该物体的ground truth box的IOU在所有bounding box中与该物体的ground truth box中的IOU最大,那么这个他负责对object预测。
表示对象是否出现在单元格i中。
表示该单元格i中的第j个边界框中没有object。
网格单元中的bounding box有负责预测的object时,=1;没有负责预测的object时,
=0。
网格单元中没有object时,只需要计算置信度损失;网格单元中有object时,需要计算分类损失以及两个bbox的置信度损失;与真实框IOU最大的bbox负责计算位置损失。
没有物体的网格单元中的数量相对较多,并且该部分的置信度为0。如果各部分误差都采用相同的权重,那么没有物体的网格单元的梯度信息会占据主导部分,导致训练网络的难度会加大、模型不稳定。为了解决这个问题,对于不包含对象的盒子,增加了边界框坐标预测的损失,并减少了置信度预测的损失。因此,YOLO引入了与
平衡系数,用来平衡各个损失之间的权重,其中
=5,
=0.5,从而降低没有物体的网格的权重。
平方和误差也权衡大盒子和小盒子中的误差。大盒子中的小偏差比小盒子中的小偏差要小。如果直接使用平方和误差,那么大盒子中的小偏差与小盒子中的小偏差相等。为了部分解决这个问题,我们预测边界框的宽度和高度的平方根,而不是直接预测宽度和高度。如图5所示为sqrt(x)的函数图像,该函数图像随着x的增加,斜率越来越小,函数增长的越来越缓慢。因此,使用平方根计算宽度、高度的误差,可以使大盒子中的小偏差比小盒子中的小偏差要小。
(20-10)2 = 100 (sqrt(20)-sqrt(10))2 = 1.71
(100-90)2 = 100 (sqrt(100)-sqrt(90))2 = 0.26
图5 sqrt(x)函数图像
YOLOv1的缺点:
1.每个网格单元仅预测两个框且只能具有一个类别,限制了我们的模型可以预测的附近物体的数量,对于密集型目标检测和小物体检测都不能很好适用。
2.多次使用下采样,使用相对粗糙的特征来预测边界框。
YOLOv1在实时性方面在当时做的是非常好的,但是YOLO的缺点也很明显,与Fast CNN相比,YOLO会产生大量的定位错误,尤其是对于一些小物体的预测精度很低。从YOLOv2的论文题目可以看出Better、Faster、Stronger,YOLOv2相较于YOLOv1,作者提出了很多优秀的思想,使其在精度与速度方面做了折中。
新的尝试:
1.Batch Normalization
Batch Normalization可显著提高收敛速度,同时消除了对其他形式的规范化的需求。在每个卷积层后面加上Batch Normalization,可以防止梯度消失现象,并且可以不使用Dropout,也不会出现过拟合的现象。mAP提高的2%。
2.高分辨率分类器
YOLOv1使用224×224的输入进行训练,然后将分辨率提升到448×448进行检测。这意味着网络必须同时切换到学习对象检测并调整为新的输入分辨率。
我们首先在ImageNet上以448×448的完整分辨率finetune分类网络训练10个epochs。这使网络有时间调整其过滤器,使其在更高分辨率的输入上更好地工作。
3.Anchor Boxes
借鉴Fast R-CNN中区域提议网络(RPN)的anchor boxes来预测先验框的偏移量和置信度。预测偏移量不是坐标可以简化问题,并使网络更容易学习。
相较于YOLOv1,做了如下改变:
·使用anchor boxes代替了bounding boxes
·删除全连接层,并使用先验框预测边界框
·消除了一个池化层,使网络卷积层的输出具有更高的分辨率
·网络缩小为可处理416个输入图像,只有一个中心单元,便于定位
·类预测机制与定位机制分离
使用Anchor Boxes之后,mAP从69.5降低到69.2,但是召回率从81%上升到88%,意味着我们的模型还有更多的改进空间。
4.k-means聚类
YOLOv1使用的box的维度是按照经验来进行选择的,YOLOv2中作者采用k-means的方式对训练集的bounding boxes做聚类,找到合适的anchor box。作者取值K=5,会使模型复杂度和召回率之间达到一个折中。使用标准的欧式距离的k-means,较大的box会比较小的box产生更多的错误,因此作者定义了如下函数:d(box, centroid)=1-IOU(box, centroid),以避免box的大小来影响误差,距离越小说明优先权越高。
5.直接位置预测
引入Anchor Box之后,在边界框位置预测的过程中,模型会产生不稳定的现象。如果训练的物体是在左上方的单元格中,那么该物体由左上方单元格的Anchor Box来进行预测,但是网络预测的tx,ty是没有受到限制的,因此,Anchor Box在加上tx,ty之后,可能会跑到网格单元的其他单元格中进行预测,但是其他单元格的预测是由其他单元格上的Anchor Box进行预测。这样就会导致网络在训练过程中出现不稳定的现象。
作者为了改善上述情况,采用了一种预测方法:直接预测相对于网格单元的偏移量。假设Anchor Box设置在每个网格单元的左上角,坐标位置为(cx, cy),宽度和高度为(pw, ph)。那么预测框的计算公式为:
其中为对tx使用Sigmoid函数,使其在(0,1)范围内,可以保证预测框的位置不会偏移到别的网格单元中。让每一个Anchor Box只负责预测目标中心落在该网格单元中的目标。使用该方法,相对于直接使用Anchor Box使模型提升个5个百分点。
图6 具有尺寸先验和位置的边界框预测
6.细粒度特征
YOLOv1由于对于小物体的检测精度不高,因此,YOLOv2引入了PassThrough层,这个层的主要作用是将相对低层(26×26×512)的特征图与高层(13×13×1024)的特征图进行融合,从而提高YOLO算法中检测小目标的性能。
PassThrough层的作用是将输入图像的尺寸(N, W, H, C)变化为(N, W/2, H/2, C×4)。将26×26×512经过一个Filters为64的1×1卷积得到一个26×26×64的输出,将该输出输入到PassThrough层得到一个13×13×256的输出,然后与13×13×1024的高层特征图进行融合,最终得到一个13×13×1280的特征图。
使用方法之后,模型大约提高了一个百分点。
7.多尺度训练
为了提高模型的鲁棒性,作者使用不同的尺寸来训练模型。没迭代10个batches,我们的网络对输入图像的尺寸来进行一个随机的选择。
网络结构:
YOLOv2的网络结构是基于GoogLeNet的网络,这个网络比VGG16快,但是精度不如VGG16。YOLOv2的网络结构为Darknet-19,Convolutional层包含一个Conv卷积层、一个Batch Normalization层、一个LeakyRelu层。
YOLOv3相交于之前版本的YOLO主要在backbone、bounding box、loss function三个方面做了改进。
Backbone:
YOLOv3的网络结构采用的是Darknet-53,它运用了当时最新的Residual的思想。Darknet-53的网络的层数要比ResNet-101的网络层数少一半,训练速度要快很多,而且精度方面也相差无几。
图8 Darknet-53网络结构
网络中的Convolutional层包含一个Conv卷积层、一个Batch Normalization层、一个LeakyRelu层。
图9 Residual结构
每个方框中的两个Convolutional层和一个Residual的网络结构为Residual结构,如图9所示。方框前面8×表示有连续的8个方框内的Residual结构。
因此Maxpool层会对梯度信息造成负面影响,所以YOLOv3的网络结构中不再使用Maxpool层,而是使用卷积层(2×2/2)来进行下采样。YOLOv2中进行了5次最大池化操作,因此,YOLOv3中进行了5次卷积层的下采样。
为了加强对小目标的检测的精度,YOLOv3使用了FPN的上采样融合做法,在三个特征层上做检测。网络的三个输出维度为:13×13×255,26×26×255,
52×52×255,255 = (80+4+1)×3。每个网格单元包含3个Anchor Box,4表示4个坐标信息,1表示置信度,80表示COCO数据集的种类。
Bounding Box:
YOLOv2中使用K-means来确定Anchor Box,然后预测相对位置来计算bbox的中心点。YOLOv2中每个网格单元Anchor的数量为5,在YOLOv3中共有9个Anchors,分别为[116, 90], [156, 198], [373, 326], [30, 61], [62, 45], [59, 119], [10, 13], [16, 30], [33, 23]。这9个Anchor会被三个输出维度均分,根据大中小来选择自己的Anchor。
Loss Function:
对于深度学习算法而言,loss是非常重要的。YOLO论文里只在YOLOv1中提出了损失函数,是使用了平方和误差。但是在YOLOv3论文中,作者没有给出损失函数,在阅读YOLOv3代码时,发现YOLOv3的损失函数中对置信度损失和类别损失是使用的二值交叉熵损失函数(Binary CrossEntroy),位置损失还是使用的平方和损失。YOLO中不在使用根下宽、高的方法,而是直接将宽、高进行差方,于是在前面添加系数:(2-gt_w*gt_h)。
表示该单元格i中的第j个边界框预测变量对此object预测负责。就是第i个网格单元中的B个anchor box中与该物体的ground truth box的IOU在所有anchor box中与该物体的ground truth box中的IOU最大,那么这个他负责对object预测。此时
=1
表示对象是否出现在单元格i中。
表示该单元格i中的第j个边界框中不负责预测object。
网格单元中的anchor box有负责预测的object时,=1;没有负责预测的object时,
=0。