10-目标检测
目标检测(Object detection)
目标检测是计算机视觉领域一个重要的方向,首先了解一下目标检测的相关问题。
以上图为例。
图片分类已经学习过了,算法遍历图片,判断图中是否是汽车。
而定位分类就是,不仅判断图片中是不是一辆汽车,还要在图片中标记出它的位置,用方框框起来。
图片分类和定位分类都只判断一个对象。
而目标检测是同时检测图片中的多个对象并标记出位置。
先从目标分类并定位讲起。
目标定位(Object localization)
在图片分类中,输入一张图片到多层卷积神经网络,然后输出一个特征向量,反馈给softmax单元来预测图片类型。
例如对象有这么几类:行人、汽车、摩托车和背景,背景表示不包含前三种对象。
那么softmax函数会输出四个结果。
这就是标准的分类问题。
如果想要定位图片中对象的位置,可以让神经元多输出4个数字,一个边界框,标记为,这四个数字确定了方框的位置和大小。
先来约定一些符号表示:
- 图片左上角坐标为(0,0),右下角坐标为(1,1)
- 表示边界框的中心位置
- 分别表示边界框的高度和宽度
如果有上述四种类别,那么目标定位任务的目标标签定义为:
其中,表示是否含有对象,如果对象属于前三类(行人,汽车,摩托车),那么,如果是背景,那么。可以将理解为被检测对象属于每一分类的概率,背景分类除外。
如果检测到对象,就输出被检测对象的边界框参数。
最后如果存在某个对象,那么,同时输出,表示该对象属于哪一个分类,如果存在行人,那么。
对于要处理的目标定位问题,假设图片中只有一个对象,因此最多有一个为1。
例如:
该图片的标签为:
而对于下面这幅图:
标签为:
,因此标签的其他参数没有意义。图片中不存在检测对象,也就不用考虑边界框大小和属于哪一类。
最后介绍损失函数,参数是实际标签和预测标签。
如果采用平方误差策略,那么损失函数为:
如果图片中存在定位对象,那么,损失值是不同元素的平方差求和;
如果图片中不存在定位对象,那么,不同考虑其他标签值,只关注神经网络输出的准确度。
通常的做法是对边界框坐标使用平方差或类似方法,对使用逻辑回归函数,或者平方预测误差。
特征点检测(Landmark detection)
如果不仅要定位目标对象,并且要定位一些特殊的特征点,这种问题叫做特征点检测。
例如构建一个人脸识别应用,希望能够定位眼角的具体位置,一共有四个眼角,那么从左到右,可以使用四个特征点来表示,第一个特征点,第二个特征点,以此类推。让神经网络多输出8个值。
当然脸部特征可以更多,例如嘴角的特征点,眉毛的特征点等等,那么神经网络就要多输出相应的数值。
在训练时,必须要保证标签的顺序,例如第二个标签值是左眼角的左边特征点,第三个特征值是左眼角的右边特征值等等,必须保证所有样本的特征点顺序都相同。
目标检测
在学习了对象对位和特征点检测后,接下来学习基于滑动窗口的目标检测算法。
假如要构建一个汽车检测算法,那么首先要创建一个训练集,x表示图片,y表示标签:0表示没有汽车,1表示有。
如上图,出于对这个训练集的期望,一开始可以适当剪切图片,使得整张图片都被汽车占据,汽车居于中间位置。
然后使用这个训练集训练卷积网络。
训练完成后,就可以用该网络实现滑动窗口目标检测。
滑动窗口目标检测
如上图,首先选定一个特定大小的窗口,然后从左上角开始,将红色窗口框定的小图片输入到卷积网络中,开始预测,判断红色方框内有没有汽车,
然后红色方框稍微向右滑动,再次将框定的图片输入到卷积网络中进行预测,以此类推,直到这个窗口滑过图片的每一部分,到右下角。
不论汽车在图片的什么位置,总有一个窗口可以检测到。
窗口的大小和滑动步幅可以变化。
滑动窗口目标检测算法有很明显的缺点就是:计算成本较高。
如果在一幅图片中剪切出太多小方块,卷积网络需要一个个进行处理。如果选用较大的步幅,会减少卷积网络需要处理的窗口个数,但是粗糙间隔尺寸可能会影响预测性能;反之如果采用小粒度或者小步幅,传递给卷积网络的小窗口会很多,但计算成本会提高。
不过目前计算成本已经有了很好的解决方案,大大提高了卷积层上应用滑动窗口目标检测的效率。
滑动窗口的卷积实现(重要)
上面已经说明了如何通过卷积网络来实现滑动窗口对象检测算法,但是效率很低。下面来学习如何在卷积层上应用该网络。
首先学习如何把神经网络的全连接层转换成卷积层。
假设目标检测算法输入一个14×14×3的图像,有16个大小为5×5的过滤器,过滤器处理后,输出维度变为10×10×16,然后经过参数为2×2的最大池化操作,输出维度变为5×5×16,然后添加两个有400个隐藏单元的全连接层,最后通过softmax单元输出y,如下图,softmax的四个单元对应四个分类。
那么将全连接层转换为卷积层,如上图,前面部分的卷积层不变,两个全连接层都改为使用400个大小为5×5×16的过滤器,那么输出维度是1×1×400,接着使用softmax进行输出。
从数学角度来看,这样做和原来的全连接层是一样的,因为这400个节点中,每个节点都有一个5×5×16维度的过滤器,所以每个值都是上一层这5×5×16个激活值经过某个线性函数的输出结果。
学会了如何将全连接层转换为卷积层,接下来学习如何通过卷积实现滑动窗口对象检测算法。
假设训练集的输入图片维度为14×14×3,和上图一样,这里为了容易说明,只画出第一个通道的平面图。
训练集图片维度为14×14×3,假设测试集图片维度为16×16×3,使用训练集图片的维度作为滑动窗口的大小,步幅为2。
分别将窗口覆盖的四张图片输入到卷积网络中进行预测,卷积网络运行了四次,输出了4个标签。
但是可以发现,这四次卷积操作中,有很多计算是重复的,因为这四个局部图片有很多像素重叠。
所以可以将16×16×3的整个照片输入到卷积网络中,使用训练时完全相同的过滤器参数和池化参数等,最终得到一个2×2×4的输出。
其中2×2×4的左上角向量值对应着原始输入16×16×3的左上角,窗口大小为14×14的局部图片输入。
同样的,2×2×4输出的其他几个元素对应着原始输入的不同窗口位置。
所以卷积操作的原理是:不需要把输入图片依据滑动窗口大小分割成多个子集,分别执行前向传播,而是把输入图片作为一个整体输入到卷积网络中计算,其中的公共区域共享计算。
例如上面的识别汽车算法,将大小为28×28的整张图片做卷积操作,一次性得到所有的预测值。
以上就是在卷积层上应用滑动窗口算法的内容,它提高了算法效率。但是存在一个缺点是边界框的位置可能不够准确。使用Bounding Box
预测可以解决。
Bounding Box(边界框)预测
在之前的滑动窗口卷积实现中,存在的问题是不能输出最精准的边界框。
例如上图,在所有的蓝色滑动窗口框中,都不能精确地框住汽车,红框才是最精确的框,长宽比向水平方向延伸了一些。
能得到更精确边界框的一个算法时YOLO算法,YOLO(You only look once)的意思是“你只看一次”。
实现原理如下:
例如输入图像是100×100的,在图像上放一个网格,为了介绍简单,这里使用3×3的网格,实际实现中会使用更精细的网格。
然后将前面讲到的图像分类和定位算法逐一应用到划分的9个格子中。
在定义训练标签时,对于9个格子的每一个图像都指定一个标签,是8维的。
和之前讲的一样:。
那么对于上图划分的九个格子来说,第一行和最后一行的六个格子什么都没有,标签向量为。
对于中间一行包含的两个对象,YOLO算法的做法是:分别取两个对象的中点,然后将这个对象分配给包含该对象中点的格子(注意这里是指人工标注训练集,而不是卷积网络的任务)。
如上图,左边汽车的中点在4号格子中,因此左边汽车会分配到该格子中。
而右边汽车中点在6号格子中,即使这个汽车左边的一小部分在5号格子中,该汽车也会被分配到6号格子中。
4号和6号格子对应的标签都是。
对于每一个格子,都会有一个大小为8的标签向量,因此3×3的网格总的输出尺寸为3×3×8。
对于左上角的长条格子,维度为1×1×8,对应的是原始图片九个格子中左上角的那个格子的标签向量。
因此相比于之前的卷积层滑动窗口实现,YOLO算法并没有改变网络结构,输入图片依然是作为一个整体输入到卷积网络中。YOLO算法只是对输入图片(训练集和测试集)做了逻辑上的网格划分,同时将训练集的标签进行了精细化处理,每个格子都有一个标签,卷积网络的输出也一一对应。
只要一个格子中的对象数目不超过1个,这个算法就没有问题。
在这里使用的是3×3的网格,但在实践中,会使用更精细的网格,例如19×19,那么输出就是19×19×8,因此多个对象分配到同一个格子的概率就会小很多。
重申一下,把对象分配到一个格子的过程是:观察对象的中点,然后将对象分配到该中点所在的格子中,所以即使对象横跨多个格子,也只会被分配到一个格子中,同样的,在更精细的网格中,两个对象的中点处在同一个格子的概率会更小。
这种方式,与图像分类和定位算法非常像,能够显式地输出边界框坐标,并且具有任意宽高比,不会受到滑动窗口分类器的步幅大小限制,并且YOLO算法是一个卷积实现,并行计算,因此运行速度非常快,可以达到实时识别。
边界框的编码
最后说明在构造训练集标签时,如何编码边界框坐标。
还以上面的两辆车图片为例,6号框子包括了右边的车。
对于任意一个格子,同样约定左上点为(0,0)
,右上点为(1,1)
。
图中给出了6号格子的标签向量,可以大致看出,红色的中点位置约为,然后用红色边界框的高度除以格子的高度得到约为0.5,同样,用蓝色边界框除以格子宽度得到约为0.9。
必须在0~1之间,否则就会在6号格子外,但是有可能会大于1,表示高度或宽度跨越了多个格子。
指定边界框的方式有很多,但这种约定是较为合理的。
在YOLO算法的论文中,给出了其他参数化的方法,可能效果会更好。
交并比
使用交并比(Intersection over union)来判断目标检测算法的运行效果。
例如上图,实际的汽车边界框是红色,而算法预测的是蓝色的边界框,那么判断这个预测结果好坏的方法是使用交并比函数(loU)。
两个边界框的并集设为,交集设为,那么交并比的大小就是交集面积大小除以并集面积的大小。
一般人为约定,如果,则检测正确。
如果预测框和实际边界框完美重叠,那么。
注意:0.5是一个人为约定的阈值,如果希望更严格一点,也可以约定0.6或更大数字,越高,边界框越精确。
非极大值抑制
目前为止,学到的YOLO算法可能会对同一个对象作出多次检测,非极大值抑制可以确保算法对每个对象只检测一次。
例如在下图中检测汽车,网格大小为19×19。
理论上来说,每辆车只有一个中点,应该只被分配到一个格子里。
然后实际在运行目标分类和定位算法时,每一个格子都会运行一次,所以可能有多个格子会认为这辆车中点在自己内部,就如上图,左边车,有多个绿色格子认为中点在该格子内部,右边车也一样。
因此可以使用非极大值抑制方法来解决这种情况。
非极大值抑制,简单来说,就是只留下预测概率最大的那一个格子,其余全部丢弃。
还以上图为例子。
算法给出了多个预测结果,但是预测概率不同。例如左边车辆,有两个预测结果,概率分别为0.8和0.7。那么非极大值抑制就会留下概率最大的那个,即0.8,然后去掉其他的预测结果。
同样右边车辆,会留下0.9的结果,去掉0.7和0.6的预测结果。
接下来说明算法的具体步骤,为了简化说明,假设只进行汽车检测,去掉了类别,输出标签为,就是格子有对象的概率。
那么非极大值抑制具体步骤为:
- 去掉所有预测概率小于阈值的边界框,例如阈值是0.6,那么的边界框会被去掉
- 选择预测概率最大的结果作为输出
- 剩下的是大于阈值,但是小于最大预测概率的边界框,全部抛弃
注意:在这里的说明,只介绍了算法检测单个对象的情况,如果同时检测多个对象,例如行人、汽车、摩托车,那么输出向量会增加三个分量,需要进行三次非极大值抑制,对每个输出类别都做一次。
Anchor Boxes
到目前为止,目标检测存在的一个问题是每个格子是能检测出一个对象,如果多个对象中点在同一个格子里,就没办法进行预测。
使用anchor box
可以解决这个问题。
来看一个具体例子,假设有下面这样一张图片。
使用3×3网格,汽车中点和行人中点几乎在同一个地方,两个对象落到同一个格子里,如果还使用之前的检测方法,将无法输出检测结果,必须从两个检测结果中选择一个。
而使用anchor box
的思路是:
首先预先定义两个不同形状的anchor box
,然后将预测结果和这两个anchor box
关联起来,一般来说,需要使用更多anchor box
,但这里为了简单起见,就使用两个anchor box
。
同时,重新定义输出标签,需要重复两次之前的输出标签向量。
即:
前面8个分量代表anchor box1
,后面8个相同的分量代表anchor box2
。
因此前面的行人和汽车都在一个格子的标签为:
前面8个分量代表行人,后面8个分量代表汽车。
因此使用anchor box
的来检测多个对象在同一个格子的方法是:每个对象都和之前一样,根据对象中点分配到了同一个格子中,然后根据该对象的边界框和anchor box
的交并比来判断应该将该对象分配到哪个anchor box
中。
例如上图的行人和汽车,首先使用汽车的预测边界框和两个anchor box
分别计算交并比,将交并比更高的那个anchor box
分配给行人,这样每个对象就会对应一个anchor box
。
如果输出类别有三个,有多个anchor box
类型,那么y的维度就会更多,例如如果有5个anchor box
,使用9×9的网格,那么输出标签维度就是9×9×40。
anchor box
有两个处理不好的情况:
- 有两个
anchor box
,但是同一个格子内有三个对象 - 两个对象都分配到一个格子,并且
anchor box
形状也一样
anchor box
的出现是为了解决多个对象出现在同一个格子内的情况,但在实践中这种情况很少发生,如果网格划分很精细,那么会更少出现。
但设立anchor box
的好处在于能够让算法更有针对性,例如数据集中有一些很高很瘦的对象,也有一些很宽的对象,那么算法就可以使用anchor box
进行更有针对性的处理。
最后,关于anchor box
的选择,一般是手工指定anchor box
形状,覆盖多种不同的类型。
但有一个更高级的版本,使用k-平均算法,将两类对象的形状聚类,自动选择anchor box
。
候选区域
在计算机视觉领域,有时候会看到候选区域(Region proposals)的概念,是另一个目标检测的方法。
首先回顾之前使用的滑动窗口检测方法。
滑动窗口会选择不同的区域,作为输入一个一个进行预测,当然也可以使用卷积网络。
这种算法的一个缺点是:在很多没有对象的区域进行计算,浪费了计算成本和时间。
例如上图框选的两个区域,没有任何对象,但是算法仍然会在这两个区域进行计算。
因此研究人员提出了R-CNN
算法,全称为convolutional neural network with region
,即带区域的卷积神经网络。
核心思想是:首先选出一些可能存在对象的候选区域,然后只在这些候选区域上运行检测算法。
而不是针对每个滑动窗口运行检测算法,只选择一些窗口,在这些少数窗口上运行。
选出候选区域的方式是运行图像分割算法。
然后根据各类色块进行候选区域的选择。
这种方法就是所谓的分割算法,先找出各类色块,然后在每个色块上放置边界框,作为候选区域,运行检测算法,这样需要处理的区域可能比滑动窗口发方法要少得多,可以减少卷积网络的运行时间。
因此R-CNN
算法主要分为两步:
- 使用图像分割算法,得到候选区域色块
- 分别将候选区域色块作为输入运行检测算法
注意:R-CNN
算法不会信任输入的边界框(也就是输入之前在色块上放置的边界框),它也会输出一个边界框坐标,这样得到的边界框比较精确。
这就是R-CNN
的概念,但是R-CNN
算法运行的很慢,所以有一系列的研究工作改进算法。
包括:
Fast R-CNN
:和R-CNN
基本一样,R-CNN
是逐一对区域进行计算,是串行的,Fast R-CNN
使用卷积实现R-CNN
,运行速度更快Faster R-CNN
:使用卷积神经网络,而不是传统的分割算法来得到候选区域色块
不过大部分的R-CNN
算法实现还是比YOLO算法慢得多。