论文链接:https://arxiv.org/pdf/1506.02640.pdf
tensorflow源码链接:https://github.com/nilboy/tensorflow-yolo/tree/python2.7/yolo
Ross Girshick提出的Faster R-CNN把目标检测的速度提高了一大步,在用Titan X时检测速度可以达到7fps,同时准确度达到73mAP。但在实际使用时这样的速度还是达不到实时的目地的,几乎在提出Faster R-CNN的同时,Joseph Redmon和Ross Girshick又提出了一种叫做YOLO的检测方法,速度可以达到45fps,YOLO的名字取自You only look once,从名字就能看出它的速度有多快。虽然45fps已经达到了实时的目标,但YOLO从出生那天起自身就带有很多的缺陷,最大的问题就是mAP只有63,这样的准确度就让实用价值打上了折扣,但不管怎么说作为第一个达到实时检测目标的结构YOLO还是有很多值得参考的地方的。
一、整体结构
YOLO之前的结构都会先对输入的图片提取要检测的区域(proposal),然后仅对proposal区域进行分类,因为整个过程分为提取proposal+检测两个阶段,速度就打了折扣。YOLO则直接砍掉了提取proposal的过程,一次回归就完成bbox位置、目标置信度、分类的工作。图1是它的基本结构:
输入的图片是448x448的尺寸,经过24个卷积+2个全连接层变成[1,7,7,30]的输出,这个结构的特征提取部分有点像GoogLeNet,只是用(1x1卷积+relu+3x3卷积+relu)代替了GoogLeNet上的inception结构。最后的输出长宽方向都只有7个点,也就是说原始的448x448图片可以在长宽方向各7等分,49个小区域各自提炼成最终输出上的一个点;此外在特征维度上最后的输出的长度是30,代表了每个点都有30种要进行回归的输出。具体是哪30种数据呢?包括了2个bounding box的位置[x,y,w,h],2个bounding box的confidence值,20个类别的分类概率,加在一起就有BX5+C=30(B=2个bounding box, C=20个分类)个输出。7x7个区域每个都有30个输出,所以整个图就有SXSX(BX5+C)=1470个要回归的数据(S=7,是原图上等分的数目)。
下面来详述上面那30个输出的含义:
bounding box位置:在Faster R-CNN中每个anchor都有9个不同尺寸的形状来确保能覆盖到原图上的目标,到了YOLO就做了简化,每个区域只用了两种尺寸的形状来覆盖目标,如图3红色的区域就用黄色的两个bounding box。每个bounding box都用[x,y,w,h]来定位,x、y代表中心点在原图上的坐标位置,w、h代表bounding box的长宽。事实上这种简化成两个bounding box的方法会牺牲性能,这也是YOLO准确度低的原因,在后来的YOLO2、SSD方法中重新启用了Faster R-CNN的多个anchor的方式来提升mAP。
confidence值:confidence是指某个小区域是否被用作识别目标,以及该区域对应的bounding box覆盖目标的准确度,confidence可以用公式1表示。如果ground truth的中心点落在小区域(图3红框)内部,第一项Pr(Object)就等于1,否则是0;IOU是bounding box和ground truth间的重叠部分面积占两者总覆盖面积的比例。在Faster R-CNN中IOU只被用来得出Pr(Object),并没有真正用于回归计算,到了YOLO中IOU也参与到回归,这么做能得到更加精确的目标位置。
要注意的是某一个ground truth只会对应小区域内IOU最大的那个bounding box。也就是说ground truth和bounding box是一一对应的关系。
如果同时有多个目标落在同一个区域内,该区域内的bounding box只选用IOU最大的目标来计算confidence。
分类概率:在作者的论文中只选择了20个类别来做识别,根据ground truth的类别来给每个小区域打上label。
二、loss计算
再看下loss的计算,公式2是YOLO的全部loss组成,分为4个部分
公式的前两项是全部SxSxB个bounding box的位置[x,y,w,h]相对于ground truth位置的回归损失值,1ijobj就是前面提到的Pr(Object),如果ground truth的中心点落在小区域(图3红框)内部,它就等于1,否则是0。
在有相同的预测偏差时,偏差给小尺寸的bbox带来的影响要比大尺寸的bbox大的多,因为小尺寸bbox只要偏移一点识别就会完全不准,为了缓和这个问题,作者对w、h的回归损失采用开根号的方法来计算。如图4:small bbox的横轴值较小,发生偏移时,反应到y轴上的loss(下图绿色)比big box(下图红色)要大。反过来可以理解成调整相同大小的损失梯度时,小尺寸bbox就偏移的更少,调整的就更精确。
公式2的第3项和第4项是置信度的回归损失,C就是某个小区域的bounding box的IOU值,第4项的1ijnoobj只有当ground truth的中心点不落在该区域才是1,否则是0,和1ijobj正好相反。这里为什么要有λcoord和λnoobj两个权重,论文中的解释是,大部分网格是不包含目标的,所以第4部分的Loss会比较大,以致于压制其他部分的loss对整体loss的影响,会导致模型参数的不稳定,容易发散,所以会设置一个比较小的λnoobj和比较大的λcoord使得包含目标的网格的预测损失能够有比较大的权重。
三、测试过程
完成训练后再来看下结构怎么用来测试。测试时需要将每个bbox的置信度乘上该小区域各个类的预测概率,得到每个类的置信度,见公式3。
得到了全部bbox的类别置信度后,再用一个门限过滤掉得分低的值,剩下的再用NMS做非极大值抑制,得出的结果就是最终的识别结果。
四、YOLO的优点
YOLO最大的优点就是不用再提取proposal,直接对全图进行回归,原先的两步流程变成了一步到位,自然速度就快了很多。为什么能砍掉提取proposal的过程呢?在讲到Faster R-CNN要有proposal的原因时,我们最常提到的一点是:如果对整张图的特征进行分类,目标周围的不相关背景会带来干扰,引起分类不准确。这个问题产生的根因是在Faster R-CNN的最后一级卷积层,空间维度上的信息都已被pooling成了一个点,对一个包含了全部背景、前景信息的点标注label,会造成计算出的loss中包含过多背景干扰,进而训练的不准确。
到了YOLO,卷积的输出不再是一个点,而是7x7个点,你可以给每个点都单独标注上原图对应区域的目标的label(不再是全图一个label),这样就能指引每个点按各自区域内的目标去收敛,不再受到周围背景的干扰。
事实上YOLO的这种结构还是有些缺陷的,因为在输出最终的7x7之前有两个全连接层,这意味着7x7的每个点都携带有全图信息,我们并不需要全图信息,只要一个小区域的信息就能满足要求,有了全图信息反而会造成收敛变慢。YOLO的这个缺陷在一年后的SSD结构中有了全新的解决办法。
五、YOLO的缺陷
YOLO有不少缺陷,几个最明显的问题是:
1、如果两个要识别的物体中心点落在同一区域,但属于不同的类别,就无法都识别出。原因很明显:一个小区域虽然能输出两个bounding box,但都是同一类别,两种类别的物体无论如何都不可能识别全。
2、如果两个物体的类别是一样的,是不是就能识别全呢?当两个物体尺寸、位置([x,y,w,h])比较相近时答案就是否定的。前面介绍YOLO的输出结构时提到训练时gound truth和bounding box是一一对应的,一个ground truth在一个区域中选择哪个bounding box来训练由IOU的最大值来决定,由此可以推断两个尺寸、位置相近的ground truth一定对应同一个bounding box来训练,到了测试阶段两个尺寸、位置相近的目标也就一定由同一个bounding box来识别,因为测试时一个bounding box只能对应有一个输出([x,y,w,h]),所以就无法区分开两种物体。
3、当长宽比和bbox的默认长宽比差异比较大时,IOU会很小,会影响检测;
以上这些缺陷都成为了YOLO后来改进的方向。