1 概述
对象检测(Object Detection)是指在图片、视频中找出不同类型的目标物体及它们的位置。对象检测在计算机视觉(Computer Vision)领域有广泛的应用场景,例如自动驾驶汽车(self-driving cars)、内容监管(content moderation)、安防行业(security industry)等。
本文讲解最流行的算法之一 -- YOLOv3(You Only Look Once version 3), YOLO的发展过程三篇论文如下:
- 2015 - 《You Only Look Once: Unified, real-time object detection》, 作者: Joseph Redmon、Santosh Divvala、Ross Girshick、Ali Farhadi
- 2016 - 《YOLO9000: Better, Faster, Stronger》,作者: Joseph Redmon、Ali Farhadi
- 2018 - 《YOLOv3: An Incremental Imprvoment》, 作者: Joseph、Ali Farhadi
本文算是一篇入门文章,在此不详尽介绍模型的每个实现细节,也没有代码,只关注YOLOv3核心原理和整体的结构。
2 如何衡量对象检测的结果
算法的优劣通常由准确率(precision)和召回率(recall)来衡量,有以下3个指标:
- 位置的准确率(bounding box precision):对象的位置由一个包含对象的最小矩形框(bounding box, bbox)标记,不能太大,也不能太小
- 类别的准确率(precision): 把对象的类型正确分类,不能把人标记成树
- 召回率(recall): 把全部对象都找出来,没有遗漏
还有检测速度也是重要的指标之一。
2.1 准确性(precision)和召回率(recall)
2.2 准确率-召回率曲线(precision-recall curve)、平均准确率(Average Precision, AP, Mean Average Precision, mAP)和重合率(IoU, Intersection over Union, Jaccard index)
只有完美的模型才能得到准确率和召回率都是1的结果,通常准确率和召回率是一对此消彼长的竞争对手。提高准确率可能会漏掉一些不太确信的对象,把更多的疑似对象召回又会降低准确率。如果是在检测行人的场景,我们希望把所有行人都检测出来 -- 高的召回率,包括疑似为行人的物体,毕竟安全第一;但在投资理财的场景,我们可能希望遭受损失次数尽可能少,虽然会错过一些投资机会 -- 意味着准确率要高。
以准确率和召回率为坐标,把模型不同指标值下的输出结果表示出来 - 就是准确率-召回率曲线。曲线下的面积(area under curve)称为平均准确率(AP)。如果是对多种对象进行检测,各种对象的平均准确率的均值称为mAP。
Precision-Recall Curve and Average Precision对于包含矩形(bounding box),怎么判断模型的预测指和真实值是不是相符呢?通常我们看它们的重合率(IoU, Intersection over Union)的大小。定义如下:
通常重合率要大于0.5才算预测正确。
Union and Intersection
3 对象检测公开数据集 - Pascal VOC和COCO
3.1 Pascal VOC, PASCAL Visual Object Classes
http://host.robots.ox.ac.uk/pascal/VOC/
2005 ~ 2012年期间举办的竞赛。可以训练和测试分类(classification)、检测(detection)和分割(segmentation)三类任务。
共有17125张图片,20个分类的对象:
3.2 COCO, Common Objects in Context
http://cocodataset.org/#download
比Pascal VOC新和大的竞赛数据集。有150万个对象实例和80个分类。
4 YOLO介绍
YOLO模型由两部分组成:一个CNN骨干(backbone)和YOLO头(head)。
Overview of YOLO骨干是特征抽取器(feature extractor),通常是由大规模图片分类任务中得到的,如在ImageNet上训练的分类器。理论上很多CNN结构都能用作backbone,如Inception, ResNet等。YOLO的作者是采用自定义的CNN结构作为backbone,叫Darknet,采用了Inception和ResNet的结构,并且速度较快。
YOLO头也是由CNN层组成,它把图片特征转换成对象检测的输出。
关于整个模型的具体的细节,感兴趣的读者可以查看论文或者其他文章。
为了易于理解,下面先介绍模型是如何推理(inference)的,再介绍其损失函数(loss function)和如何训练。
4.1 栅格(grid)和锚定框(anchor box)
为了定位对象所在的位置,输入的图片会被平均划分为个格子(grid),每个格子里有个锚定框(anchor box)。对象的位置(bbox)会输出为相对于锚定框(anchor box)的位置。
为什么要使用相对位置来定位对象,而不直接预测bounding box的位置和大小呢?实际上在YOLOv1确实是直接预测的。但是作者发现由于对象的大小不一,这样做的话误差会很大,特别是对于小对象的预测,所以在YOLOv2引入了锚定框(anchor box)。
锚定框也称作先验(prior)框,因为是在训练前事先根据检测对象的形状确定的一组不同宽高比和大小的矩形框。例如要检测行人,那么会使用高的窄的矩形框;要检测桔子,会使用正方形框等。
YOLO使用的是3组小中大共9个锚定框,从YOLOv2开始作者根据数据集的对象来聚类产生锚定框。如在COCO数据集上使用的锚定框有如下9个(YOLOv3 tiny使用6个锚定框):
栅格里的锚定框并不能精准匹配对象的位置,所以模型的训练目标是找到最匹配的锚定框并修正对象的位置(corrections to the anchor box)。如果定义以下参数:
- : 对象所在栅格的位置,比如对象在图片的左上角那么,在右下角则
- : 锚定框的宽和高
- : 模型最后一层的输出
那么最后预测的bounding box的位置和大小可由以下公式得出:
栅格的大小实际上是特征抽取后feature map的大小,因而由2个因素决定:1. 骨干做特征抽取的步长(strides),2. 输入图片的大小。
4.2 模型的输出
上面只是描述了对象的位置和大小,但模型还需要输出对象的类别。
对象的类别预测类似于图像分类任务,输出的是各个分类的可能性(probability)大小。
实际上,模型的最后输出是一个矩阵:是栅格的大小(feature map的大小),是每个栅格里的anchor box的数量,是对象的类别数。对于每个bounding box,模型需要输出个数字:
- : 是每个分类的概率,
- : 这4个数字代表bounding box的中心点和宽高
- c: 代表此bounding box包含一个对象实例的置信度(confidence)
4.3 排序与去重
对于模型预测输出的数量众多的bounding box,我们需要按置信度排序并过滤掉可能性不大的bounding box。
排序的概率由bounding_box的置信度乘以分类的概率。
选出可能的bounding box后,仍有很多是有重叠得比较厉害的,需要去掉。对于去重,我们要使用一个叫NMS(non-maximum suppression)的技术,即过滤掉跟排在前面的bbox重叠厉害的bbox。这需要用到前面提到的重叠率(IoU),对每个bbox计算出跟后面每个bbox的IoU,如果超过某阈值(如0.5)就去掉该后面的bbox。
4.4 损失函数(loss function)
以上介绍了预测的过程,接下来介绍模型的损失函数。损失函数代表模型优化的方向,设计好损失函数就能够用样本训练网络了。
我们知道,模型输出了3项信息:1. bounding box的位置和大小;2. bounding box包含一个对象的置信度;3. 对象属于的各分类概率;对应的,损失函数包含这3部分的误差。
4.4.1 bounding box误差
- G是栅格的大小(grid_size),通常 ,B是每个栅格里anchor box的数量
- 是bounding box包含某对象的指示函数,包含某对象则为1,否则为0
- 是bounding box的位置和大小(真实值按照4.1部分的公式逆向求出)。戴帽子(^)的是预测值
4.4.2 bounding box包含对象的误差
由于一个bounding box要么包含对象,要么不包含任何对象,类似于一个二分类问题,最自然的损失函数就是binary_crossentropy。
但是有一种情况,如果预测的bounding box跟真实的bounding box的重合率IoU不大,这也是当作true negative来对待;因为它还可能是其它真实的bounding box的候选,这种情况我们不希望计入其误差。
4.4.3 对象分类的误差
4.4.4 模型总的误差(loss)
由上面三部分误差加总即可。
5 参考实现
在github上已有牛人写了tensorflow 2的实现,该实现包含图片检测、视频检测和用个性化数据训练的实现,个人觉得很有参考价值。地址是:https://github.com/zzh8829/yolov3-tf2
最后再附上一张用该实现做对象检测的结果图片,可以看到对很大的对象的检测不太完美,欢迎读者朋友讨论如何改进它:)
Girl and Car
网友评论