🎯 目标检测 Object detection
前两节课中,我们介绍的是利用 CNN 模型进行图像分类。本节将继续深入介绍目标定位和目标检测(包含多目标检测)。
一、目标定位 Object localization
在构建对象检测之前,我们先了解一下对象定位,首先我们看看它的定义:
图片分类任务就是算法遍历图片,判断其中的对象是不是汽车,这就是图片分类。
这节课我们要学习构建神经网络的另一个问题,即定位分类问题。这意味着,我们不仅要用算法判断图片中是不是一辆汽车,还要在图片中标记出它的位置,用边框或红色方框把汽车圈起来。其中“定位”的意思是判断汽车在图片中的具体位置。
本节我们要研究的分类定位问题,通常只有一个较大的对象位于图片中间位置,我们要对它进行识别和定位。而在对象检测问题中,图片可以含有多个对象,甚至单张图片中会有多个不同分类的对象。因此,图片分类的思路可以帮助学习分类定位,而对象定位的思路又有助于学习对象检测,我们先从分类和定位开始讲起。
图片分类问题你已经并不陌生了,例如,输入一张图片到多层卷积神经网络,它会输出一个特征向量,并反馈给softmax 单元来预测图片类型:
如果你正在构建汽车自动驾驶系统,那么对象可能包括以下几类:行人、汽车、摩托车和背景,这意味着图片中不含有前三种对象,也就是说图片中没有行人、汽车和摩托车,输出结果会是背景对象,这四个分类就是 softmax 函数可能输出的结果:
这就是标准的分类过程,如果你还想定位图片中汽车的位置,该怎么做呢?我们可以让神经网络多输出几个单元,输出一个边界框。具体说就是让神经网络再多输出 4 个数字,标记为 $b_{x}$, $b_{y}$ (目标中心位置坐标),$b_{h}$ 和 $b_{w}$ (标所在矩形区域的高和宽),这四个数字是被检测对象的边界框的参数化表示。
一般设定图片左上角为原点 (0, 0),右下角为(1, 1)。在模型训练时,bx、by、bh、bw 都由人为确定其数值。例如上图中,我们设定 bx=0.5,by=0.7,bh=0.3,bw=0.4:
原始图片经过 CONV 卷积层后,Softmax 层输出 8 x 1 向量:
除了包含上述一般 CNN 分类 3 x 1向量(class label)之外,还包含了 bx, by,bh 和 bw,还包含了 Pc,表示矩形区域是目标对象的概率,数值在 0~1 之间,且越大概率越大。如果对象属于前三类(6 行人、7 汽车、8 摩托车),则 $p_c = 1$,如果是背景,则图片中没有要检测的对象,则 $p_c = 1$。
比如对于上图的汽车图片,输出为:
若 Pc=0,表示没有检测到目标,则输出 label 后面的 7 个参数都可以忽略:
对于损失函数 Loss function,若使用平方误差形式,有两种情况:
如果图片中存在定位对象,那么$y_{1} = 1$,所以 $y_{1} =p_{c} = 1$,损失值就是不同元素的平方和。
另一种情况是,$y_{1} = 0$,也就是$p_{c} = 0$,损失值是$\left(\hat{y_1} - y_{1}\right)^{2}$,因为对于这种情况,我们不用考虑其它元素,只需要关注神经网络输出$p_{c}$的准确度。
二、特征点检测 Landmark detection
除了使用矩形区域检测目标类别和位置外,我们还可以仅对目标的关键特征点坐标进行定位,这些关键点被称为 landmarks
。
例如人脸识别,可以对人脸部分特征点坐标进行定位检测,并标记出来,如下图所示:
该网络模型共检测人脸上 64 处特征点,加上是否为 face 的标志位,输出 label 共有 64×2+1 = 129 个值。通过检测人脸特征点可以进行情绪分类与判断,或者应用于 AR 领域等等。
除了人脸特征点检测之外,还可以检测人体姿势动作,如下图所示:
三、目标检测 Object detection
目标检测的一种简单方法是滑动窗算法 sliding windows。这种算法首先在训练样本集上搜集相应的各种目标图片和非目标图片。注意训练集图片尺寸较小,尽量仅包含相应目标,如下图所示:
然后,使用这些训练集构建CNN模型,使得模型有较高的识别率。
最后,在测试图片上,选择大小适宜的窗口、合适的步进长度,进行从左到右、从上倒下的滑动。每个窗口区域都送入之前构建好的CNN模型进行识别判断。若判断有目标,则此窗口即为目标区域;若判断没有目标,则此窗口为非目标区域。
滑动窗算法的优点是原理简单,且不需要人为选定目标区域(检测出目标的滑动窗即为目标区域)。
但是其缺点也很明显,首先滑动窗的大小和步进长度都需要人为直观设定。滑动窗过小或过大,步进长度过大均会降低目标检测正确率。而且,每次滑动窗区域都要进行一次CNN网络计算,如果滑动窗和步进长度较小,整个目标检测的算法运行时间会很长。所以,滑动窗算法虽然简单,但是性能不佳,不够快,不够灵活。
四、滑动窗口的卷积实现 Convolutional implementation of sliding windows
滑动窗算法可以使用卷积方式实现,以提高运行速度,节约重复运算成本。
首先,单个滑动窗口区域进入 CNN 网络模型时,包含全连接层:
假设对象检测算法输入一个 14×14×3 的图像。过滤器大小为 5×5,数量是 16,14×14×3 的图像在过滤器处理之后映射为 10×10×16。然后通过参数为 2×2 的最大池化操作(Max Pooling),图像减小到 5×5×16。然后添加一个连接 400 个单元的全连接层,接着再添加一个全连接层,最后通过 softmax 单元输出 $y$。
💡 OK,现在我们讲解如何将这些全连接层转化为卷积层:
它的前几层和之前的一样,而对于全连接层,我们可以用 5×5 的过滤器来实现,数量是 400个,输入图像大小为 5×5×16,用 5×5 的过滤器对它进行卷积操作,过滤器实际上是5×5×16,因为在卷积过程中,过滤器会遍历这16个通道,所以这两处的通道数量必须保持一致,输出结果为 1×1。假设应用 400 个这样的 5×5×16 过滤器,输出维度就是 1×1×400,我们不再把它看作一个含有 400 个节点的集合,而是一个 1×1×400 的输出层。从数学角度看,它和全连接层是一样的,因为这400个节点中每个节点都有一个 5×5×16 维度的过滤器,所以每个值都是上一层这些 5×5×16 激活值经过某个任意线性函数的输出结果。
我们再添加另外一个卷积层,这里用的是 1×1 卷积,假设有 400 个 1×1 的过滤器,在这 400 个过滤器的作用下,下一层的维度是 1×1×400,它其实就是上个网络中的第二个全连接层。
最后经由 1×1 过滤器的处理,得到一个 softmax 激活值,通过卷积网络,我们最终得到这个 1×1×4
的输出层,代表共有 1x1 个窗口结果,每个窗口的输出有 4
个元素(行人,汽车,摩托车,背景),比如 {0 1 0 0}
表示这个窗口的识别结果是汽车。
🚩 掌握了卷积知识,我们再看看如何通过卷积实现滑动窗口对象检测算法:
假设向滑动窗口卷积网络输入14×14×3 的图片,为了简化演示和计算过程,这里我们依然用 14×14 的小图片。和前面一样,神经网络最后的输出层,即softmax单元的输出是1×1×4,我画得比较简单,严格来说,14×14×3应该是一个长方体,第二个 10×10×16 也是一个长方体,但为了方便,我只画了正面。所以,对于 1×1×400 的这个输出层,我也只画了它 1×1 的那一面,所以这里显示的都是平面图,而不是 3D 图像:
假设输入给卷积网络的图片大小是 14×14×3,测试集图片是 16×16×3,现在给这个输入图片加上黄色条块,在最初的滑动窗口算法中,你会把这片蓝色区域输入卷积网络生成0或1分类。接着滑动窗口,步幅为2个像素,向右滑动2个像素,将这个绿框区域输入给卷积网络,运行整个卷积网络,得到另外一个标签0或1。然后将这个橘色区域输入给卷积网络,卷积后得到另一个标签,最后对右下方的紫色区域进行最后一次卷积操作。我们在这个 16×16×3的小图像上滑动窗口,卷积网络运行了 4 次,于是输出了 4 个标签。2x2x4
表示共有 2 x 2
个窗口结果(分类结果),每个窗口的输出有 4
个元素(行人,汽车,摩托车,背景),即分别有 4
类输出值的可能性:
可以发现,这 4 次卷积操作中很多计算都是重复的。所以执行滑动窗口的卷积时使得卷积网络在这 4 次前向传播过程中共享很多计算
下面我们再看一个更大的图片样本,假如对一个28×28×3的图片应用滑动窗口操作,如果以同样的方式运行前向传播,最后得到 8×8×4 的结果。跟上一个范例一样,以 14×14 区域滑动窗口,步幅为 2:
之前的滑动窗算法需要反复进行 CNN 正向计算,例如16 x 16 x 3的图片需进行4次,28 x 28 x3的图片需进行64次。👍 而利用卷积操作代替滑动窗算法,则不管原始图片有多大,只需要进行一次CNN正向计算,因为其中共享了很多重复计算部分,这大大节约了运算成本。值得一提的是,💡 窗口步进长度与选择的 MAX POOL 大小有关。如果需要步进长度为4,只需设置 MAX POOL 为 4 x 4 即可。
五、YOLO 算法
滑动窗口法存在问题就是不能输出最精准的边界框。本节我们将了解如何得到更精准的边界框。
YOLO(You Only Look Once)算法可以解决这类问题,生成更加准确的目标区域(如上图红色窗口)。
YOLO算法首先将原始图片分割成 n x n
网格,每个网格代表一块区域。为简化说明,下图中将图片分成 3 x 3
网格:
然后,利用上一节卷积形式实现滑动窗口算法的思想,对该原始图片构建 CNN 网络,得到的的输出层维度为 3 x 3 x 8
。其中,3 x 3
对应 9 个网格,每个网格的输出包含 8
个元素:
如果目标中心坐标 $(b_x,b_y)$ 不在当前网格内,则当前网格 $Pc=0$ ;相反,则当前网格$Pc=1$(即只看中心坐标是否在当前网格内)。判断有目标的网格中,$b_x,b_y,b_h,b_w$ 限定了目标区域。值得注意的是,当前网格左上角坐标设定为 (0, 0)
,右下角坐标设定为 (1, 1)
,$(b_x,b_y)$ 范围限定在 $[0,1]$ 之间,但是 $b_h,b_w$ 可以大于 1。因为目标可能超出该网格,横跨多个区域,如上图所示。目标占几个网格没有关系,目标中心坐标必然在一个网格之内。
划分的网格可以更密一些。网格越小,则多个目标的中心坐标被划分到一个网格内的概率就越小,这恰恰是我们希望看到的。
六、交并比 Intersection over union
IoU,即交集与并集之比,可以用来评价目标检测区域的准确性。
如上图所示,红色方框为真实目标区域,蓝色方框为检测目标区域。两块区域的交集为绿色部分,并集为紫色部分。蓝色方框与红色方框的接近程度可以用IoU比值来定义:
IoU可以表示任意两块区域的接近程度。IoU 值介于0~1之间,且越接近1表示两块区域越接近。
七、非极大值抑制 Non-max suppression
YOLO算法中,可能会出现多个网格都检测出到同一目标的情况,例如几个相邻网格都判断出同一目标的中心坐标在其内。
上图中,三个绿色网格和三个红色网格分别检测的都是同一目标。那如何判断哪个网格最为准确呢?方法是使用非最大值抑制算法。
非最大值抑制(Non-max Suppression)做法很简单,图示每个网格的Pc值可以求出,Pc值反映了该网格包含目标中心坐标的可信度。首先选取Pc最大值对应的网格和区域,然后计算该区域与所有其它区域的IoU,剔除掉IoU大于阈值(例如0.5)的所有网格及区域。这样就能保证同一目标只有一个网格与之对应,且该网格Pc最大,最可信。接着,再从剩下的网格中选取Pc最大的网格,重复上一步的操作。最后,就能使得每个目标都仅由一个网格和区域对应。如下图所示:
总结一下非最大值抑制算法的流程:
1. 剔除 Pc 值小于某阈值(例如 0.6)的所有网格;
2. 选取 Pc 值最大的网格,利用 IoU,摒弃与该网格交叠较大的网格;
3. 对剩下的网格,重复步骤 2。
八、Anchor Boxes
到目前为止,我们介绍的都是一个网格至多只能检测一个目标。那对于多个目标重叠的情况,例如一个人站在一辆车前面,该如何使用YOLO算法进行检测呢?方法是使用不同形状的Anchor Boxes。
如下图所示,同一网格出现了两个目标:人和车。为了同时检测两个目标,我们可以设置两个Anchor Boxes,Anchor box 1检测人,Anchor box 2检测车:
也就是说,每个网格多加了一层输出。原来的输出维度是 3 x 3 x 8
,现在是 3 x 3 x 2 x 8
(也可以写成3 x 3 x 16的形式)。这里的 2 表示有两个Anchor Boxes,用来在一个网格中同时检测多个目标。每个Anchor box都有一个Pc值,若两个Pc值均大于某阈值,则检测到了两个目标。
在使用YOLO算法时,只需对每个 Anchor box 使用上一节的非最大值抑制即可。Anchor Boxes 之间并行实现。
顺便提一下,Anchor Boxes 形状的选择可以通过人为选取,也可以使用其他机器学习算法,例如 k 聚类算法对待检测的所有目标进行形状分类,选择主要形状作为 Anchor Boxes。
九、候选区域 Region proposals
之前介绍的滑动窗算法会对原始图片的每个区域都进行扫描,即使是一些空白的或明显没有目标的区域,例如下图所示。这样会降低算法运行效率,耗费时间。
为了解决这一问题,尽量避免对无用区域的扫描,可以使用 Region Proposals 的方法。具体做法是先对原始图片进行分割算法处理,然后只对分割后的图片中的块进行目标检测。
Region Proposals 共有三种方法:
R-CNN: 滑动窗的形式,一次只对单个区域块进行目标检测,运算速度慢。
Fast R-CNN: 利用卷积实现滑动窗算法,类似第 4 节做法。
Faster R-CNN: 利用卷积对图片进行分割,进一步提高运行速度。
比较而言,Faster R-CNN 的运行速度还是比 YOLO 慢一些。