You Only Look Once 网络: 统一的,实时的目标检测
2006年发表
本文提出了一种新的检测方法——YOLO, 思想是将目标检测作为一个回归问题,而不是类似于R-CNN的分类问题,将从空间上分离边界框和相关的类别概率。
单个神经网络在一次评估中直接从完整图像预先确定边界框和类概率, 能以每秒45帧的速度实时处理图像,fast YOLO每秒处理速度达到155帧,效果得到其他实时检测器的mAP的两倍,且迁移到其他领域时候性能优于当时的其他方法DPM, R-CNN。缺点是该方法对于背景上的负样本上的预测结果不是很好。
最早的方法是DPM方法(2008)可参考该文,,该方法对目标的形变有很强的鲁棒性。输入一幅图像,对图像提取图像特征,针对某个物件制作出相应的激励模板,在原始的图像中计算,得到该激励效果图,根据激励的分布,确定目标位置。
最近的R-CNN算法首先在图像中生成候选边界框,然后在候选框上运行分类器,分类后使用后处理细化边界框,消除重复检测。
YOLO将检测问题定义为回归问题,直接从图像像素到边界框坐标和类别概率。
单个卷积网络同时预测多个边界框和这些框的类别概率,YOLO训练时直接使用整个图像并直接优化检测性能。优势如下:
YOLO非常快,因为其将检测框架作为回归问题,因此不需要复杂的过程。只是在测试时在新图像上运行神经网络来预测检测框位置和类别
YOLO在全局范围内对图像有所了解,当做出预测的时候,与滑动窗口和基于区域提出(RPN)的技术不同,YOLO在训练和测试阶段内查看整个图像,因此它隐式编码有关类别及其模式的上下文信息
YOLO学习了对象的泛化表示,在自然图像上训练,并对艺术作品进行测试时,其效果优于DMP和RCNN检测方法,表明其泛化能力更优
缺点如下:
我们将对象检测的单独组件统一到单个神经网络中,使得网络使用整个图像中的特征来预测每个边界框。同时它还可以预测所有类中的所有边界框。思想如下
SxS
大小的网格,如果对象的中心落入网络单元格中,则该网格单元格负责检测该对象B
个边界框以及置信度分数,置信度分数反映了模型对框中包含对象的可信度Pr(object) * IOU
衡量预测盒子的准确程度,如果该单元格中不存在对象,Pr=0
, 则置信度分数应为零。否则, Pr=1
, 置信度得分等于预测的盒子和真实标记之间的交并比(IOU)x,y,w,h
和置信度。(x, y)坐标表示相对于网格单元边界的框的中心。wedth
和height
是相对于整个图像预测宽度和高度。最后置信度预测表示预测框与任何真实标记框之间的 IOU。(x,y)是bbox的中心相对于单元格的offset,这是便于后续回归坐标(x, y) 不直接回归中心点坐标数值,而是回归相对于格点左上角坐标的位移值。例如,第一个格点中物体坐标为 (2, 3) ,另一个格点中的物体坐标为(5.4, 6.3),这四个数值让神经网络暴力回归,有一定难度。所以这里的offset是指,既然格点已知,那么物体中心点的坐标一定在格点正方形里,相对于格点左上角的位移值一定在区间[0, 1)
内。让神经网络预测(0.3, 0.6)与(0.4, 0.3) 会更加容易,在使用时,加上格点左上角坐标(2, 3)、(5, 6)即可。
C
个 条件类概率Pr(Class_i|Object)
, 这个概率适用于包含对象的网格单元,只是预测每个网格单元的一组类别概率,而不管方框B的数量是多少.最后会得20x(7x7x2)=98
的类别分数(20个类别,2表示2个bounding box),然后过滤掉得分低的bounding box,再使用非极大值抑制(NMS),使得每个网格最多只预测一个目标;
上述操作是对20个类别轮流进行的,在某个类别中,将得分少于设定阈值(0.2)类别分数直接设置为0,然后再按得分从高到低排序。
最后再用NMS算法去掉重复率较大的bounding box(NMS:针对某一类别,选择得分最大的bounding box,然后计算它和其它bounding box的IOU值,如果IOU大于0.5,说明重复率较大,该得分设为0,如果不大于0.5,则不改变其分数,相当于保留该bounding box;这样一轮后,再选择剩下的score里面最大的那个bounding box,然后计算该bounding box和其它bounding box的IOU,重复以上过程直到最后)。
最后每个bounding box的20个score取最大的score,如果这个score大于0,那么这个bounding box就是这个socre对应的类别,如果等于0,说明这个bounding box里面没有物体,跳过即可。
张量大小 SxSx(B*5+C)
基本框架:使用网络的初始卷积层从图像中提取特征,而完全连接层预测输出概率和坐标
YOLO网络架构受到用于图像分类的GoogLeNet模型的启发,网络有 24 个卷积层,后面是 2 个完全
连接层,除了 GoogLeNet 使用的初始模块,只使用1x1简化层,然后使用3×3的卷积层,网络如下:
此外,本文还训练了一个Fast YOLO算法, 只有9个卷积层以及更少的卷积核。除了网络的大小外,YOLO和Fast YOLO之间的所有训练和测试参数都是相同的。
YOLO检测网络有 24 个卷积层,后面是 2 个完全连接层。 交替的1×1 卷积层减少了前面层的特征空间。 首先在ImageNet分类任务上以一半的分辨率(224 × 224
输入图像)预先训练卷积层,然后将分辨率加倍(448x448
)以进行检测网络的训练。
每个网格允许预测出2个边框,类别为20类,最终输出的张量大小是 7x7x30
去预测。因为网格和bounding box设置的比较稀疏,所以这个版本的YOLO训练出来后预测的准确率和召回率都不是很理想
先使用imageNet1000数据集训练卷积层,使用上图中的前 20 个卷积层,接着是平均池化层和完全连接层来训练分类网络,耗时大约一周左右,在 ImageNet 2012 验证集上实现 88%的单一物体 top-5 精度,与 Caffe Zoo 模型中的 GoogLeNet 相当。使用 Darknet 框架进行所有训练和推理。
然后转换模型进行检测。其它工作表明在预训练的网络中添加卷积层和连接层可以提高性能。按照这个思想,网络添加了四个卷积层和两个完全连接层,随机初始化权重。检测通常需要细粒度的视觉信息,因此将网络的输入分辨率从224×224增加到448×448。网络的最后一层预测类别概率和边界框坐标,并将边界框宽度和高度标准化为图像宽度和高度,使它们落在 0 和 1 之间。同时将边界框 x 和 y 坐标参数化为特定网格单元位置的偏移量,因此它们也在 0 和 1 之间。最终层使用线性激活函数,其它层使用Leaky relu修正线性激活函数:
损失函数:
由于在每个图像中,许多网格单元不包含任何对象,此时这些单元格的置信度分数为零,这会影响了包含对象的单元格的梯度变化,解决办法是增加坐标loss的占比,降低不含目标的框的置信度预测loss占比,使用两个参数λ_coord和λ_noobj 实现,分别设置为5
和0.5
注意: 损失函数只有在该网格单元格中存在一个对象时,才会惩罚分类错误;
可以看到,损失函数分为四部分:
其中,λ_coord与 λ_noobj设置分别为5与0.5
- 8维的localization error和20维的classification error同样重要显然是不合理的,应该更重视8维的坐标预测,所以作者给这个损失前加了个权重,记为:λ_coord,在Pascal VOC训练中设置为5,而bbox confidence loss权重设置为1
- 对于不存在object的bbox的confidence loss,赋予了更小的损失权重,记为λ_noobj,在Pascal VOC中取0.5
- 对于存在object的bbox的confidence loss和类别的loss,权重取常数1。对于每个格子而言,作者设计只能包含同种物体。若格子中包含物体,我们希望希望预测正确的类别的概率越接近于1越好,而错误类别的概率越接近于0越好。loss第4部分中,若p_i©为0中c为正确类别,则值为1,若非正确类别,则值为0
训练的时候:输入N个图像,每个图像包含M(M>=0)个object,每个object包含4个坐标(x,y,w,h)和1个class label。然后通过网络得到Nx7x7x30
大小的三维矩阵。7x7
是得到的grid_cell的个数,每个gride_cell由1x30
表示。每个1x30
的向量前5个元素表示第一个bounding box的4个坐标和1个confidence,第6到10元素表示第二个bounding box的4个坐标和1个confidence。最后20个表示这个grid cell所属类别。类似于线性回归的方式。然后就可以计算损失函数的第一、二 、五行。至于第三、四行,confidence可以根据ground truth和预测的bounding box计算出的IOU 和 是否有object的0,1值相乘得到。真实的confidence是0或1值,即有object则为1,没有object则为0。 这样就能计算出loss function的值了。
测试的时候:输入一张图像,跑到网络的末端得到7x7x30
的三维矩阵,这里虽然没有计算IOU,但是由训练好的权重已经直接计算出了bounding box的confidence。然后再跟预测的类别概率相乘就得到每个bounding box属于哪一类的概率。
一个网格预测多个bounding box,在训练时希望每个object(ground true box)只有一个bounding box专门负责(一个object, 一个bbox)。具体做法是与ground true box(object)的IOU最大的bounding box 负责该ground true box(object)的预测。这种做法称作bounding box predictor的specialization(专职化)。每个预测器会对特定(sizes, aspect ratio or classed of object)的ground true box预测的越来越好。
在数据集 PASCAL VOC 2007和2012数据集上进行训练和测试,跑了135个epoches,将PASCAL VOC 2007作为训练和验证数据集,并在2012数据集上进行测试。使用的设置如下:
学习率衰减如下:
为了避免过拟合:
就像在训练中一样,预测一个测试图像的检测结果只需要进行一个网络评估。在VOC数据上,网络对每张图像进行预测,每个box有98个边界框和每个边界框的类别概率。在测试时,YOLO非常快,因为它只需要一个单一的网络评估,不像基于分类器的方法。
网格设计加强了边界框预测中的空间多样性。通常对象属于哪个网格单元格,网络只预测每个对象的一个框。然而,一些大的目标或者目标在多个cell中的可以被多个cell检测到, 非极大值抑制nms解决上述多重检测问题。非最大抑制在MAP中增加了2-3%。
YOLO对边界框的预测施加了很强的空间约束,因为每个网格单元只能预测两个框,并且只能有一个类。这种空间约束限制了模型可以预测对象附近的数量。模型对于成群出现的小物体表现不好,比如鸟群。
由于模型学会了从数据中预测边界框,它很难推广到新的或不寻常的高宽比的对象。模型也使用了相对粗糙的特征来预测边界框,因为网络架构有多个来自输入图像的向下采样层。
最后,当我们训练一个接近检测性能的损失函数时,损失函数处理小边界框和大边界框时候的误差是相同的。大边界框里的小错误通常是良性的,但小边界框里的小错误对IOU的影响要大得多。模型主要错误来源是错误的定位
检测系统最开始是从输入图像中抽取一系列的鲁棒特征比如,Haar, SIFT, Hog, 卷积特征,然后使用分类器或定位器去从特征空间中区分目标。这些分类器或定位器以滑动窗口的方式在整个图像上或在图像中的某些区域子集上运行。将YOLO检测系统与几个顶级检测框架进行比较,一些关键的区别如下所示:
将YOLO与GPU实现的DPM在30Hz或100Hz下运行进行比较。虽然其他方法没有达到实时效果,但还是比较了它们的相对的mAP和速度,以检查目标检测系统中提供的准确性及性能权衡。
Fast YOLO 在Pascal数据集上是当前最快的目标检测方法,mAP为52.7%, 比之前的实时检测系统精度高两倍,YOLO在不影响实时性能的前提下将mAP达到63.4%
基于VGG-16训练的YOLO精度要比YOLO高,但是速度要慢很多。
Fastest DMP在没有牺牲精度的前提下大幅提升了DMP算法的速度,但仍然没有达到实时检测的性能。此外,其检测精度也低于基于神经网络实现的算法。
R-CNN minus R用静态边界框提案代替选择性搜索,比R-CNN方法要快一些,但没有达到实时性能
Fast R-CNN 加速了R-CNN的分类阶段, 但该方法仍然基于选择性搜索算法,每张图生成候选框需要2秒左右的时间,即使有比较高的mAP,但是速度只达到了0.5 fps, 离实时效果还有很远的距离。
最近提出的Faster R-CNN使用神经网络替换掉了选择性搜索来提出候选框。在我们的测试结果中,基于VGG-16实现的Faster RCNN比YOLO高10mAP,但是比YOLO慢6倍。
为了进一步检查YOLO和最先进的探测器之间的差异,我们查看了关于VOC2007的结果的详细分析。我们将YOLO和FastRCNN进行了比较,因为FastR-CNN在Psascal数据集上是当时性能最高的检测器之一,而且它的检测结果是公开的。
对于测试时的每个类别,我们将查看该类别的前N个预测。每个预测都是正确的,或者根据错误类型进行分类:
可以看到,YOLO在定位时候的表现不是很好。定位错误所占的YOLO错误比其他所有错误的总和都要多。Faster R-CNN产生的定位错误要少得多,但背景错误要多得多。13.6%的顶级检测结果是不包含任何物体的假阳性结果。快速R-CNN预测背景探测的可能性几乎是YOLO的3倍。
由于YOLO比Fast R-CNN的背景错误更小,因此使用YOLO消除Fast R-CNN获得的背景检测。使用Fast R-CNN去预测每一个边界框。
从上面结果可以看到,集成YOLO能有效地提高了快速R-CNN的性能。但是这种组合并没有受益于YOLO的速度,因为两个模型需要单独地运行,然后对结果进行结合。但是,由于YOLO的速度非常快,与FastR-CNN相比,它并没有增加任何显著的计算时间代价。
从表3可以看到,在VOC2012测试集中,YOLO获得的mAP为57.9%。这低于当时的最好技术水平,更接近使用VGG-16的原始R-CNN,YOLO系统在小物体上表现不太理想,比如瓶子,电视,摩托等。
结合Fast R-CNN + YOLO
能成为当时最好的检测方法之一,结合YOLO,能对Fast R-CNN提升2.3%。
目标检测的学术数据集从同一分布中提取训练和测试数据。在现实世界的应用程序中,很难预测所有可能的用例和测试数据可能与系统以前看到的不同。将YOLO与其他检测算法在毕加索数据集和人物艺术数据集上进行了比较。
R-CNN在VOC2007上有很高的AP。然而,R-CNN在应用于艺术作品时会显著下降。R-CNN使用选择性搜索的边界框建议,并对自然图像进行调整。R-CNN中的分类器步骤只看到小区域,所以需要提出的候选框足够好。
DPM在应用于艺术品时,可以很好地保持其AP性能。先前的工作认为DPM表现得很好,因为它具有强大的目标形状和布局的空间模型。虽然DPM的性能下降没有R-CNN那么多,但它从一个较低的AP开始。
YOLO在VOC2007上具有良好的性能,当应用于艺术作品时,其AP下降率低于其他方法。。艺术品和自然图像在像素水平上非常不同,但它们在物体的大小和形状上是相似的,因此YOLO仍然可以预测良好的边界框和检测。
由于YOLO系统检测时间非常快,因此可以用于实时检测。详情可以看该视频:
https://www.youtube.com/watch?v=MPU2HistivI
YOLO是一个端对端的目标检测模型,该模型构造起来很简单,并且可以直接在全图上进行训练。与基于分类器的方法不同,YOLO在一个直接对应于检测性能的损失函数进行训练,并对整个模型进行联合训练。
Fast YOLO是当时最快的通用目标检测器,YOLO推动了最先进的实时目标检测。YOLO还能很好地推广到新领域,使其成为快速、健壮的对象检测的应用程序中的内核算法。
NMS实现
import cv2
import random
import numpy as np
def non_max_suppress(predicts_dict, threshold):
for object_name, bbox in predicts_dict.items(): # 对每一个类别分别进行NMS;一次读取一对键值(即某个类别的所有框)
bbox_array = np.array(bbox, dtype=np.float)
print(bbox_array)
# 下面分别获取框的左上角坐标(x1,y1),右下角坐标(x2,y2)及此框的置信度;这里需要注意的是图像左上角可以看做坐标点(0,0),右下角可以看做坐标点(1,1),也就是说从左往右x值增大,从上往下y值增大
x1 = bbox_array[:, 0]
y1 = bbox_array[:, 1]
x2 = bbox_array[:, 2]
y2 = bbox_array[:, 3]
scores = bbox_array[:, 4] # class confidence, ndarray
order = scores.argsort()[::-1] # argsort函数返回的是数组值从小到大的索引值,[::-1]表示取反。即这里返回的是数组值从大到小的索引值
areas = (x2 - x1 + 1) * (y2 - y1 + 1) # 当前类所有框的面积(python会自动使用广播机制,相当于MATLAB中的.*即两矩阵对应元素相乘);x1=3,x2=5,习惯上计算x方向长度就是x=3、4、5这三个像素,即5-3+1=3,而不是5-3=2,所以需要加1
print(areas, type(areas))
keep = []
# 按confidence从高到低遍历bbx,移除所有与该矩形框的IoU值大于threshold的矩形框
while order.size > 0:
i = order[0]
keep.append(i) # 保留当前最大confidence对应的bbx索引
# 获取所有与当前bbx的交集对应的左上角和右下角坐标,并计算IoU(注意这里是同时计算一个bbx与其他所有bbx的IoU)
xx1 = np.maximum(x1[i], x1[order[1:]]) # 最大置信度的左上角坐标分别与剩余所有的框的左上角坐标进行比较,分别保存较大值;因此这里的xx1的维数应该是当前类的框的个数减1
yy1 = np.maximum(y1[i], y1[order[1:]])
xx2 = np.minimum(x2[i], x2[order[1:]])
yy2 = np.minimum(y2[i], y2[order[1:]])
inter = np.maximum(0.0, xx2-xx1+1) * np.maximum(0.0, yy2-yy1+1)
iou = inter / (areas[i] + areas[order[1:]] - inter) # 注意这里都是采用广播机制,同时计算了置信度最高的框与其余框的IoU
inds = np.where(iou <= threshold)[0] # 保留iou小于等于阙值的框的索引值
order = order[inds + 1] # 将order中的第inds+1处的值重新赋值给order;即更新保留下来的索引,加1是因为因为没有计算与自身的IOU,所以索引相差1,需要加上
bbox = bbox_array[keep]
predicts_dict[object_name] = bbox.tolist()
return predicts_dict
# 下面在一张全黑图片上测试非极大值抑制的效果
img = np.zeros((600,600), np.uint8)
predicts_dict = {
'black1': [[83, 54, 165, 163, 0.8], [67, 48, 118, 132, 0.5], [91, 38, 192, 171, 0.6]]}
# predicts_dict = {'black1': [[83, 54, 165, 163, 0.8], [67, 48, 118, 132, 0.5], [91, 38, 192, 171, 0.6]], 'black2': [[59, 120, 137, 368, 0.12], [54, 154, 148, 382, 0.13]] }
# 在全黑的图像上画出设定的几个框
for object_name, bbox in predicts_dict.items():
for box in bbox:
x1, y1, x2, y2, score = box[0], box[1], box[2], box[3], box[-1]
y_text = int(random.uniform(y1, y2)) # uniform()是不能直接访问的,需要导入 random 模块,然后通过 random 静态对象调用该方法。uniform() 方法将随机生成下一个实数,它在 [x, y) 范围内
cv2.rectangle(img, (x1, y1), (x2, y2), (255, 255, 255), 2)
cv2.putText(img, str(score), (x2 - 30, y_text), 2, 1, (255, 255, 0))
cv2.namedWindow("black1_roi") # 创建一个显示图像的窗口
cv2.imshow("black1_roi", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 在全黑图片上画出经过非极大值抑制后的框
img_cp = np.zeros((600,600), np.uint8)
predicts_dict_nms = non_max_suppress(predicts_dict, 0.1)
for object_name, bbox in predicts_dict_nms.items():
for box in bbox:
x1, y1, x2, y2, score = int(box[0]), int(box[1]), int(box[2]), int(box[3]), box[-1]
y_text = int(random.uniform(y1, y2)) # uniform()是不能直接访问的,需要导入 random 模块,然后通过 random 静态对象调用该方法。uniform() 方法将随机生成下一个实数,它在 [x, y) 范围内
cv2.rectangle(img_cp, (x1, y1), (x2, y2), (255, 255, 255), 2)
cv2.putText(img_cp, str(score), (x2 - 30, y_text), 2, 1, (255, 255, 0))
cv2.namedWindow("black1_nms") # 创建一个显示图像的窗口
cv2.imshow("black1_nms", img_cp) # 在窗口中显示图像;注意这里的窗口名字如果不是刚刚创建的窗口的名字则会自动创建一个新的窗口并将图像显示在这个窗口
cv2.waitKey(0)
cv2.destroyAllWindows()
基于darknet项目