YOLO学习笔记
照例,先放大佬的链接(https://zhuanlan.zhihu.com/p/31427164)
前面我花了很大的篇幅和时间写faster-rcnn,现在来写一下大名鼎鼎的YOLO。
YOLO全称You Only Look Once: Unified, Real-Time Object Detection,是在CVPR2016提出的一种目标检测算法,核心思想是将目标检测转化为回归问题求解,并基于一个单独的end-to-end网络,完成从原始图像的输入到物体位置和类别的输出。YOLO与Faster RCNN有以下区别:
1、Faster RCNN将目标检测分解为分类为题和回归问题分别求解:首先采用独立的RPN网络专门求取region proposal,即计算图1中的 P(objetness);然后对利用bounding box regression对提取的region proposal进行位置修正,即计算图1中的Box offsets(回归问题);最后采用softmax进行分类(分类问题)。(不是很了解的可以看一下我的另一篇[博文]写的很详细。(https://blog.csdn.net/justsolow/article/details/105187021))
2、YOLO将物体检测作为一个回归问题进行求解:输入图像经过一次网络,便能得到图像中所有物体的位置和其所属类别及相应的置信概率。
这样看起来的话YOLO的优点就很明显了,
1、速度肯定是要比faster-rcnn快的,而且直接处理回归问题的话,2、其对背景的误检率肯定是要比faster-rcnn只看proposal中的局部信息要高的。
那么缺点也是显而易见的,分类准确率肯定是要低的,相应的召回率也肯定会低。
下图是两个网络的区别。这种“把检测转化为回归问题”的思路非常有效,之后的很多检测算法(包括SSD)都借鉴了此思路。之后我也会写到SSD。
1. YOLO网络结构
相比Faster RCNN,YOLO结构简单而,网络中只包含conv,relu,pooling和全连接层,以及最后用来综合信息的detect层。其中使用了1x1卷积用于多通道信息融合。
2. YOLO核心思想
YOLO的工作过程分为以下几个过程:
(1) 将原图划分为SxS的网格。如果一个目标的中心落入某个格子,这个格子就负责检测该目标。(这里S取为7)
(2) 每个网格要预测B个bounding boxes,以及C个类别概率Pr(classi|object)。在YOLO中,每个格子只有一个C类别,即相当于忽略了B个bounding boxes,每个格子只判断一次类别,这样做非常简单粗暴。(这也就是类别准曲率低的原因之一了吧)
(3) 每个bounding box除了要回归自身的位置之外,还要附带预测一个confidence值。这个confidence代表了所预测的box中含有目标的置信度和这个bounding box预测的有多准两重信息:
如果有目标落中心在格子里Pr(Object)=1;否则Pr(Object)=0。 第二项是预测的bounding box和实际的ground truth之间的IOU。
所以,每个bounding box都包含了5个预测量:(x, y, w, h, confidence),其中(x, y)代表预测box相对于格子的中心,(w, h)为预测box相对于图片的width和height比例,confidence就是上述置信度。需要说明,这里的x, y, w和h都是经过归一化的,之后有解释。(由于这里是归一化的,这也就是yolo的annotations的bbox设置不同于其他的原因)
(4) 由于输入图像被分为SxS网格,每个网格包括5个预测量:(x, y, w, h, confidence)和一个C类,所以网络输出是SxSx(5xB+C)大小
(5) 在检测目标的时候,每个网格预测的类别条件概率和bounding box预测的confidence信息相乘,就得到每个bounding box的class-specific confidence score:(这的是简单粗暴!!!)
显然这个class-specific confidence score既包含了bounding box最终属于哪个类别的概率,又包含了bounding box位置的准确度。最后设置一个阈值与class-specific confidence score对比,过滤掉score低于阈值的boxes,然后对score高于阈值的boxes进行非极大值抑制(NMS, non-maximum suppression)后得到最终的检测框体。
3. YOLO中的Bounding Box Normalization
YOLO在实现中有一个重要细节,即对bounding box的坐标(x, y, w, h)进行了normalization,以便进行回归。作者认为这是一个非常重要的细节。接下来分析一下到底如何实现。
如图,在YOLO中输入图像被分为SxS网格。假设有一个bounding box(如图4红框),其中心刚好落在了(row,col)网格中,则这个网格需要负责预测整个红框中的dog目标。假设图像的宽为widthimage,高为heightimage;红框中心在(xc,yc),宽为widthbox,高为heightbox那么:
(1) 对于bounding box的宽和高做如下normalization,使得输出宽高介于0~1:
(2) 使用(row, col)网格的offset归一化bounding box的中心坐标:
经过上述公式得到的normalization的(x, y, w, h),再加之前提到的confidence,共同组成了一个真正在网络中用于回归的bounding box;
而当网络在Test阶段(x,y,w,h)经过反向解码又可得到目标在图像坐标系的框,相关解码代码在darknet框架detection_layer.c中的get_detection_boxes()函数,关键部分如下:
boxes[index].x = (predictions[box_index + 0] + col) / l.side * w;
boxes[index].y = (predictions[box_index + 1] + row) / l.side * h;
boxes[index].w = pow(predictions[box_index + 2], (l.sqrt?2:1)) * w;
boxes[index].h = pow(predictions[box_index + 3], (l.sqrt?2:1)) * h;
注:这里 boxes[index].x是上面的Xc,而w和h就是图像宽高,l.side是上文中提到的S。
4. YOLO训练过程
对于任何一种网络,loss都是非常重要的,直接决定网络效果的好坏。YOLO的Loss函数设计时主要考虑了以下3个方面:(这里不是很懂的我还有一篇专门讲解目标检测时候训练数据不平衡的博客,感兴趣的可以看一下)
(1) bounding box的(x, y, w, h)的坐标预测误差。在检测算法的实际使用中,一般都有这种经验:对不同大小的bounding box预测中,相比于大box大小预测偏一点,小box大小测偏一点肯定更不能被忍受。所以在Loss中同等对待大小不同的box是不合理的。为了解决这个问题,作者用了一个比较取巧的办法,即先对w和h求平方根压缩数值范围,再进行回归。
(2) bounding box的confidence预测误差。由于绝大部分网格中不包含目标,导致绝大部分box的confidence=0,所以在设计confidence误差时同等对待包含目标和不包含目标的box也是不合理的,否则会导致模型不稳定。作者在不含object的box的confidence预测误差中乘以惩罚权重
除此之外,同等对待4个值(x, y, w, h)的坐标预测误差与1个值的conference预测误差也不合理,所以作者在坐标预测误差误差之前乘以权重(至于为什么是5而不是4,我也不知道T_T)。
(3) 分类预测误差。即每个box属于什么类别,需要注意一个网格只预测一次类别,即默认每个网格中的所有B个bounding box都是同一类。所以,YOLO的最终误差为下:
Loss = λcoord * 坐标预测误差 + (含object的box confidence预测误差 + λnoobj * 不含object的box confidence预测误差) + 类别预测误差=
在各种常用框架中实现网络中一般需要完成forward与backward过程,forward函数只需依照Loss编码即可,而backward函数简需要计算残差delta。这里单解释一下YOLO的负反馈,即backward的实现方法。在UFLDL教程中网络正向传播方式定义为:
只需计算每一项的参数训练目标值与网络输出值之差,反向回传即可,与代码对应。
5. 进一步理解YOLO
1、在YOLO网络中,首先通过一组CNN提取feature maps
2、然后通过最后一个全连接FC层生成SxSx(5B+C)=7x7x(52+20)=1470长的向量
3、再把1470向量reshape成SxSx(5B+C)=7x7x30形状的多维矩阵
4、通过解析多维矩阵获得Detection bounding box + Confidence
5、最后对Detection bounding box + Confidence进行Non maximum suppression获得输出
在设置好网络,并进行初始化后,通过forward就可以获得我们需要的SxSx(5B+C)矩阵,只不过其中数值并不是我们想要的。当经过上述YOLO Loss下的负反馈训练后,显然就可以获得我们SxSx(5*B+C)矩阵,再经过解析+NMS就可以获得输出框了。
从本质上说,Faster RCNN通过对Anchors的判别和修正获得检测框;而YOLO通过强行回归获得检测框。
6. 结果分析
在论文中,作者给出了YOLO与Fast RCNN检测结果对比,如下图。YOLO对背景的误判率(4.75%)比Fast RCNN的误判率(13.6%)低很多。但是YOLO的定位准确率较差,占总误差比例的19.0%,而fast rcnn仅为8.6%。这说明了YOLO中把检测转化为回归的思路有较好的precision,但是bounding box的定位方法还需要进一步改进。