对于图像分类和定位问题,通常只有一个较大的对象位于图片中间位置,我们要对它进行识别和定位。而在目标检测问题中,图片可以含有多个对象,甚至单张图片中会有多个不同分类的对象。因此,图片分类的思路可以帮助学习分类定位,而目标定位的思路又有助于学习目标检测,我们先从分类和定位开始讲起。
对于解决图像分类问题的步骤,可简单描述为输入一张图片到多层卷积神经网络,它会输出一个特征向量,并反馈给softmax单元来预测图片类型。假设分类的对象包括以下几类:行人、汽车、摩托车和背景,则softmax函数会输出这四个分类的预测结果。
上述内容是标准的分类过程,要想再定位图片中汽车的位置,则可以让神经网络多输出4个单元,分别标记为 b x b_x bx, b y b_y by, b h b_h bh和 b w b_w bw,即输出一个被检测对象的边界框(Bounding Box)的参数化表示。这里的 b x b_x bx和 b y b_y by表示边界框的中心点坐标, b h b_h bh和 b w b_w bw表示边界框的高度和宽度。
接下来具体讲讲如何为监督学习任务定义目标标签 y y y。假如上图的汽车图像是一张训练集图片,标记为 x x x,经过神经网络输出的是边界框参数和一个分类标签,或分类标签出现的概率。目标标签 y y y的定义如下: y = [ p c b x b y b h b w c 1 c 2 c 3 ] y=\begin{bmatrix} p_c \\ b_x \\ b_y \\ b_h \\ b_w \\ c_1 \\ c_2 \\ c_3 \end{bmatrix} y=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡pcbxbybhbwc1c2c3⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤
它是一个向量,其中第一个组件 p c p_c pc表示是否含有对象,如果对象属于前三类(行人、汽车、摩托车),则 p c = 1 p_c=1 pc=1;如果是背景,即图片中没有要检测的对象,则 p c = 0 p_c=0 pc=0。第二到第五个组件 b x b_x bx、 b y b_y by、 b h b_h bh和 b w b_w bw表示边界框的各项参数。第六到第八个组件 c 1 c_1 c1、 c 2 c_2 c2和 c 3 c_3 c3分别表示该对象属于1-3类中的哪一类,是行人,汽车还是摩托车。如果该对象为行人,则 c 1 = 1 c_1=1 c1=1,其余为0;如果该对象为汽车,则 c 2 = 1 c_2=1 c2=1,其余为0;如果该对象为摩托车,则 c 3 = 1 c_3=1 c3=1,其余为0。
如果检测到对象,则 p c = 1 p_c=1 pc=1,同时输出被检测对象的边界框参数 b x b_x bx、 b y b_y by、 b h b_h bh、 b w b_w bw以及该对象类别 c 1 c_1 c1、 c 2 c_2 c2和 c 3 c_3 c3。如果图片中没有检测对象,则 p c = 0 p_c=0 pc=0,此时 y y y的其它参数将变得毫无意义。
这里将标签 y y y的预测输出定义为 y = [ y 1 y 2 y 3 y 4 y 5 y 6 y 7 y 8 ] y=\begin{bmatrix} y_1 \\ y_2 \\ y_3 \\ y_4 \\ y_5 \\ y_6 \\ y_7 \\ y_8 \end{bmatrix} y=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡y1y2y3y4y5y6y7y8⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤。损失函数可以定义为 L ( y ^ , y ) = { ( y 2 ^ − y 2 ) 2 + ( y 3 ^ − y 3 ) 2 + ( y 4 ^ − y 4 ) 2 + ( y 5 ^ − y 5 ) 2 y 1 = 1 ( y 1 ^ − y 1 ) 2 y 1 = 0 L(\hat{y},y)=\begin{cases} (\hat{y_2}-y_2)^2+(\hat{y_3}-y_3)^2+(\hat{y_4}-y_4)^2+(\hat{y_5}-y_5)^2 & y_1=1 \\ (\hat{y_1}-y_1)^2 & y_1=0 \\ \end{cases} L(y^,y)={(y2^−y2)2+(y3^−y3)2+(y4^−y4)2+(y5^−y5)2(y1^−y1)2y1=1y1=0通常做法是对边界框坐标应用平方差或类似方法,对 p c p_c pc应用逻辑回归函数,甚至采用平方预测误差,而不需要对 c 1 c_1 c1、 c 2 c_2 c2、 c 3 c_3 c3和softmax激活函数应用对数损失函数。
上一节讲了如何利用神经网络进行目标定位,即通过输出四个参数值 b x b_x bx、 b y b_y by、 b h b_h bh和 b w b_w bw给出图片中对象的边界框。更概括地说,神经网络可以通过输出图片上特征点坐标 ( x , y ) (x,y) (x,y)来实现对目标特征的识别。
以人脸识别为例,假设你希望算法可以给出眼角的具体位置坐标 ( x , y ) (x,y) (x,y),你可以让神经网络的最后一层多输出两个数字 l x l_x lx和 l y l_y ly,作为眼角的坐标值。假设你想知道两只眼睛的四个眼角的具体位置,那么从左到右,依次输出四个特征点坐标来表示这四个眼角,例如输出第一个特征点 ( l 1 x , l 1 y ) (l_{1x},l_{1y}) (l1x,l1y),第二个特征点 ( l 2 x , l 2 y ) (l_{2x},l_{2y}) (l2x,l2y),以此类推,这四个脸部特征点的位置就可以通过神经网络输出了。
除了以上所述的特征点,你还可以根据嘴部的特征点输出值来确定嘴的形状,从而判断人物是在微笑还是皱眉。如果你对人体姿态检测感兴趣,你还可以定义一些关键特征点,如胸部的中点,左肩,左肘,腰等,然后通过神经网络标注人物姿态的关键特征点,再输出这些标注过的特征点,就相当于输出了人物的姿态动作。
以构建一个汽车检测算法为例,大致的步骤如下:
创建一个标签训练集, x x x表示汽车图片样本, y y y表示对应的图片标签(建议可以适当剪切图片,使汽车居于中间位置并基本占据整张图片)
利用这个标签训练集训练卷积网络,即输入这些适当剪切过的图片,经过卷积神经网络输出预测结果 y ^ \hat{y} y^,0和1分别表示图片中有汽车或没有汽车。
训练完这个卷积神经网络,就可以用它来使用滑动窗口法实现目标检测。
滑动窗口的目标检测算法具有明显的缺点,就是计算成本。你需要在图片中剪切出很多的小方块,并且一个个输入给卷积神经网络进行处理。如果选用的步幅很大,则会减少输入卷积网络的窗口个数,但是粗粒度可能会影响检测性能。反之,如果采用细粒度或小步幅,传递给卷积网络的小窗口会特别多,但这意味着超高的计算成本。
为了构建滑动窗口的卷积应用,首先要知道如何把神经网络的全连接层转化成卷积层。
假设输入一个 14 14 14× 14 14 14× 3 3 3的图像,使用16个大小为 5 5 5× 5 5 5的过滤器进行卷积操作后,输出大小为 10 10 10× 10 10 10× 16 16 16的图像,经过一个窗口大小为 2 2 2× 2 2 2的最大池化层,图像尺寸减小到 5 5 5× 5 5 5× 16 16 16。然后将其展开成一个一维数组,接着再添加一个具有400个单元的全连接层,最后通过softmax单元输出4个分类的概率,这4个分类分别对应是行人、汽车、摩托车和背景。
接下来将全连接层转化成卷积层。前面的卷积层部分保持不变,输出图像的尺寸为 5 5 5× 5 5 5× 16 16 16,使用400个维度大小为 5 5 5× 5 5 5× 16 16 16的过滤器对它进行卷积操作,其输出维度为 1 1 1× 1 1 1× 400 400 400。这里我们不再把它看作一个含有400个节点的集合,而是一个 1 1 1× 1 1 1× 400 400 400的输出层。从数学角度看,它和全连接层是一样的,因为这400个节点中每个节点都有一个维度为 5 5 5× 5 5 5× 16 16 16的过滤器,所以每个值都是上一层这些 5 5 5× 5 5 5× 16 16 16激活值经过某个任意线性函数的输出结果。
然后使用400个 1 1 1× 1 1 1的过滤器进行卷积操作,则下一层的输出维度是 1 1 1× 1 1 1× 400 400 400。最后再通过4个 1 1 1× 1 1 1的过滤器处理,得到一个softmax激活值。经过卷积神经网络的作用,最终得到这个 1 1 1× 1 1 1× 4 4 4的输出层。
假设输入给卷积神经网络的图片大小是 14 14 14× 14 14 14× 3 3 3,测试集图片是 16 16 16× 16 16 16× 3 3 3。在最初的滑动窗口算法中,首先将这片蓝色区域输入到神经网络中,输出预测的分类结果0或1,然后向右滑动2个像素,将这个绿框区域输入到神经网络,得到另外一个分类标签0或1。接下来继续将这个橘色区域输入给神经网络,卷积后得到另一个分类标签,最后对右下方的紫色区域进行一次卷积操作。最终,在输出层这4个子方块中,左上角子方块对应的是输入图像左上部分 14 14 14× 14 14 14区域的输出(红色箭头标识),右上角子方块对应的是输入图像右上部分 14 14 14× 14 14 14区域的输出(绿色箭头标识),左下角子方块对应的是输入图像左下部分 14 14 14× 14 14 14区域的输出(橘色箭头标识),右下角子方块对应的是输入图像右下部分 14 14 14× 14 14 14区域的输出(紫色箭头标识)。
结果发现,这4次卷积操作中很多计算都是重复的,即在执行滑动窗口的卷积过程中,卷积网络在这4次前向传播过程中共享了很多计算。因此我们不需要把输入图像分割成四个子集,分别执行前向传播,而是把它们作为一张图片输入给卷积神经网络进行计算,其中的公共区域可以共享很多计算。这样一来就提高了整个算法的效率,但是这种算法仍然存在一个缺点,就是边界框的位置可能不够准确。
假设输入图像的尺寸是 100 100 100× 100 100 100,将其划分成 3 3 3× 3 3 3网格。如果定义训练标签,需要对9个格子逐一定义一个训练标签 y = [ p c b x b y b h b w c 1 c 2 c 3 ] y=\begin{bmatrix} p_c \\ b_x \\ b_y \\ b_h \\ b_w \\ c_1 \\ c_2 \\ c_3 \end{bmatrix} y=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡pcbxbybhbwc1c2c3⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤,其中 p c p_c pc表示这个格子中是否有对象,如果这个格子里有对象,那么就会给出边界框坐标 b x b_x bx、 b y b_y by、 b h b_h bh和 b w b_w bw, c 1 c_1 c1、 c 2 c_2 c2和 c 3 c_3 c3表示识别的三个类别,不包括背景类别。YOLO算法的做法是,取两个对象的中点,然后将这个对象分配给包含对象中点的格子。即使中心格子同时有两辆车的一部分,也假装中心格子没有任何我们感兴趣的对象。
如果训练一个输入为 100 100 100× 100 100 100× 3 3 3的卷积神经网络,将图像输入给神经网络,因为这里划分成 3 3 3× 3 3 3网格,每一个格子都会得到一个8维输出向量,因此最终得到一个 3 3 3× 3 3 3× 8 8 8的特征向量。
交并比(loU)函数做的是计算两个边界框交集和并集之比,其中两个边界框的并集就是图中的绿色阴影部分),而交集就是图中的橙色阴影部分,因此交并比的计算就是把橙色阴影面积除以绿色阴影面积。
一般来说,在计算机检测任务中,如果 I o U ≥ 0.5 IoU\geq0.5 IoU≥0.5就算检测正确,如果预测器和实际边界框完美重叠,即交集等于并集,loU就是1。人们定义loU这个概念是为了评价你的目标定位算法是否精准,这是衡量定位精确度的一种方式,你只需要统计算法正确检测和定位目标的次数,就可以用这样的定义来判断对象定位是否准确。
假设在上面这张图片里检测行人和汽车,将其划分成 19 19 19× 19 19 19的网格,理论上每辆车只有一个中点,即只有一个格子做出有车的预测。但实际上每个格子都运行一次图像检测和定位算法,神经网络可能会认为这辆车的中点在1号格子内部,也可能在2号或3号格子内部,因此可能会对同一个对象做出多次检测,而非极大值抑制的作用就是清理这些多余的检测结果。
实现非最大值抑制的步骤大致如下:
Anchor Box是为了解决同一个网格中有多个目标对象的情况。而现实情况中,你的网格划分越细,这种情况发生的就越少。
以上图为例,仍然使用 3 3 3× 3 3 3网格对图像进行划分,可以看到行人的中点和汽车的中点几乎在同一个网格中,而原先定义的标签 y y y无法同时输出两个预测结果。
因此重新定义一个类别标签(如上图),其中 p c p_c pc表示这个格子中是否有对象, b x b_x bx、 b y b_y by、 b h b_h bh和 b w b_w bw表示标记对象的边界框坐标, c 1 c_1 c1、 c 2 c_2 c2和 c 3 c_3 c3分别表示识别的三个类别,不包括背景类别。绿色方框标记的元素与Anchor box 1对应,橙色方框标记的元素与Anchor box 2对应。
Anchor Box的思路是,首先根据对象形状定义多个不同形状的Anchor Box,然后找出与每个Anchor Box交并比最大的实际边界框,将实际边界框的标签作为Anchor Box的标签,并且计算Anchor Box相对于真实边界框的偏移量。在本例子中Anchor Box的个数为2,输出标签 y y y的维度是 3 3 3× 3 3 3× 16 16 16或 3 3 3× 3 3 3× 2 2 2× 8 8 8。
目前Anchor Box的选择主要有如下三种方式: