须知:本文为《Convolutional Neural Networks》第三周目标检测的学习笔记。作者建议各位读者结合Coursera上Andrew NG(吴恩达)教授的DeeLearning.ai课程一同学习,理解效果更佳。
文章目录
目标定位(Object localization)
- 图片分类:判断其中的对象是不是汽车。
- 目标定位:不仅要用算法判断图片中是不是一辆汽车,还要在图片中标记出它的位置,用方框把汽车圈出来。
- 目标检测:当图片中有多个对象时,检测出它们并确定出位置。
图片分类的思路可以帮助学习分类定位,而对象定位的思路又有助于学习对象检测,我们先从分类和定位开始讲起。
对于图像分类分体,输入一张图片到多层卷积神经网络,它会输出一个特征向量,并反馈给softmax单元来预测图片类型。如果你正在构建汽车自动驾驶系统,那么对象可能包括以下几类:行人、汽车、摩托车和背景。这四个分类就是 softmax 函数可能输出的结果。
此时,如果你我们想定位图片中汽车的位置,该怎么做呢?
我们可以让神经网络多输出几个单元,输出一个边界框。具体说就是让神经网络再多输出4个数字,标记为 ,这四个数字是被检测对象的边界框的参数化表示。
图片左上角的坐标为(0,0),右下角标记为(1,1)。要确定边界框的具体位置,需要指定红色方框的中心点,这个点表示为 ,边界框的高度为 ,宽度为 。因此训练集不仅包含神经网络要预测的对象分类标签,还要包含表示边界框的这四个数字,接着采用监督学习算法,输出一个分类标签,还有四个参数值,从而给出检测对象的边框位置。
此例中,
理想值是(0.5,0.7),因为它表示汽车位于图 片水平方向的中间位置,垂直方向距离图片底部 3/10的位置;
约为0.3,因为红色方框的高度是图片高度的 0.3倍;
约为0.4,红色方框的宽度是图片宽度的 0.4 倍
如何为监督学习任务定义目标标签y?
目标标签的定义如下:
它是一个向量,第一个组件表示是否含有对象,如果对象属于前三类(行人、汽车、摩托车),则,如果是背景,则图片中没有要检测的对象,则。我们可以这样理解,它表示被检测对象属于某一分类的概率,背景分类除外。
如上图的图片,因为图中有一辆汽车,所以输出为:
这是图片中只有一个检测对象的情况,如果图片中没有检测对象呢?
这种情况下,, 的其它参数将变得毫无意义,这里我全部写成问号,表示“毫无意义”的参数,因为图片中不存在检测对象,所以不用考虑网络输出中边界框的大小,也不用考虑图片中的对象是属于中的哪一类。
此时如果我们用平方误差作为神经网络的损失函数,其参数为类别和网络输出,则
特征点检测
我们发现利用神经网络可以进行目标定位,即通过输出四个参数值给出图片中对象的边界框。更概括地说,神经网络可以通过输出图片上特征点的坐标来实现对目标特征的识别。
假设你正在构建一个人脸识别应用,出于某种原因,你希望算法可以给出眼角的具体位置。眼角坐标为,你可以让神经网络的最后一层多输出两个数字,作为眼角的坐标值。如果你想知道两只眼睛的四个眼角的具体位置,那么从左到右,依次用四个特征点来表示这四个眼角。对神经网络稍做些修改,输出第一个特征点,第二个特征点,第三个特征点,第四个特征点,这四个脸部特征点的位置就可以通过神经网络输出了。
也许除了这四个特征点,你还想得到更多的特征点输出值,根据嘴部的关键点输出值来确定嘴的形状,从而判断人物是在微笑还是皱眉,也可以提取鼻子周围的关键特征点。为了便于说明,你可以设定特征点的个数,假设脸部有 64 个特征点,有些点甚至可以帮助你定义脸部轮廓或下颌轮廓。选定特征点个数,并生成包含这些特征点的标签训练集,然后利用神经网络输出脸部关键特征点的位置。
具体做法是,准备一个卷积网络和一些特征集,将人脸图片输入卷积网络,输出 1 或 0,1 表示有人脸,0 表示没有人脸,然后输出。这里我用代表一个特征,这里有129个输出单元,其中1表示图片中有人脸,因为有64个特征,64×2=128,所以最终输出128+1=129个单元,由此实现对图片的人脸检测和定位。
用同样的方法也可推广到定义关键特征点实现人体姿态检测。
目标检测
如何通过卷积网络进行对象检测,最初采用的是基于滑动窗口的目标检测算法。
假设这是一张测试图片,首先选定一个特定大小的窗口,比如图片下方这个窗口,将这个红色小方块输入卷积神经网络,卷积网络开始进行预测,即判断红色方框内有没有汽车。
滑动窗口目标检测算法接下来会继续处理第二个图像,即红色方框稍向右滑动之后的区域,并输入给卷积网络,因此输入给卷积网络的只有红色方框内的区域,再次运行卷积网络,然后处理第三个图像,依次重复操作,直到这个窗口滑过图像的每一个角落。
为了滑动得更快,选用的步幅可以比较大,思路是以固定步幅移动窗口,遍历图像的每个区域,把这些剪切后的小图像输入卷积网络,对每个位置按 0 或 1 进行分类,这就是所谓的图像滑动窗口操作。
下面我分别还更大的滑动窗口去截取更大的区域,并输入给卷积神经网络处理,你可以根据卷积网络对输入大小调整这个区域,然后输入给卷积网络,输出 0或1。如果你这样做,不论汽车在图片的什么位置,总有一个窗口可以检测到它。
这种算法叫作滑动窗口目标检测,因为我们以某个步幅滑动这些方框窗口遍历整张图片,对这些方形区域进行分类,判断里面有没有汽车。
缺点:
- 计算成本。
因为你在图片中剪切出太多小方块,卷积网络要一个个地处理。如果你选用的步幅很大,显然会减少输入卷积网络的窗口个数,但是粗糙间隔尺寸可能会影响性能。反之,如果采用小粒度或小步幅,传递给卷积网络的小窗口会特别多,这意味着超高的计算成本。
滑动窗口的卷积实现(Convolutional implementation of sliding windows)
由于计算成本的原因,在神经网络兴起之前,人们通常采用更简单的分类器进行对象检测,比如通过采用手工处理工程特征的简单的线性分类器来执行对象检测。至于误差,因为每个分类器的计算成本都很低,它只是一个线性函数,所以滑动窗口目标检测算法表现良好,是个不错的算法。然而,卷积网络运行单个分类人物的成本却高得多,像这样滑动窗口太慢。除非采用超细粒度或极小步幅,否则无法准确定位图片中的对象。
为了构建滑动窗口的卷积应用,首先要知道如何把神经网络的全连接层转化成卷积层。
对于上图的这样一个卷积网络,它的前三层都是一样的,第四层开始,也就是这个全连接层,我们可以用 5×5 的过滤器来实现,数量是 400 个(编号 1 所示),输入图像大小为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 **值经过某个任意线性函数的输出结果。
我们再添加另外一个卷积层(编号2所示),这里用的是1×1卷积,假设有 400 个 1×1的过滤器,在这400个过滤器的作用下,下一层的维度是 1×1×400,它其实就是上个网络中的这一全连接层。最后经由1×1过滤器的处理,得到一个 softmax**值,通过卷积网络,我们最终得到这个 1×1×4 的输出层,而不是这 4 个数字(编号 3 所示)。
以上就是用卷积层代替全连接层的过程,结果这几个单元集变成了 1×1×400 和 1×1×4 的维度。
下面就是如何通过卷积实现滑动窗口对象检测算法。
假设向滑动窗口卷积网络输入14×14×3的图片,为了简化演示和计算过程,这里我们依然用14×14的小图片。和前面一样,神经网络最后的输出层,即softmax单元的输出是1×1×4。
假设输入给卷积网络的图片大小是14×14×3,测试集图片是16×16×3,现在给这个输入图片加上黄色条块,在最初的滑动窗口算法中,你会把这片蓝色区域输入卷积网络(红色笔标记)生成0或1分类。接着滑动窗口,步幅为 2个像素,向右滑动2个像素,将这个绿框区域输入给卷积网络,运行整个卷积网络,得到另外一个标签0或1。继续将这个橘色区域输入给卷积网络,卷积后得到另一个标签,最后对右下方的紫色区域进行最后一次卷积操作。我们在这个16×16×3的小图像上滑动窗口,卷积网络运行了 4 次,于是输出了了 4 个标签。
结果发现,这 4 次卷积操作中很多计算都是重复的。所以执行滑动窗口的卷积时使得卷积网络在这4次前向传播过程中共享很多计算,卷积网络运行同样的参数,使得相同的 5×5×16过滤器进行卷积操作,得到 12×12×16 的输出层。然后执行同样的最大池化,输出结果6×6×16。照旧应用 400 个 5×5的过滤器,得到一个2×2×400的输出层,现在输出层为 2×2×400,而不是1×1×400。应用1×1过滤器得到另一个2×2×400的输出层。再做一次全连接的操作,最终得到2×2×4的输出层,而不是1×1×4。最终,在输出层这4个子方块中,蓝色的是图像左上部分14×14的输出(红色箭头标识),右上角方块是图像右上部分的对应输出(绿色箭头标识)的,左下角方块是输入层左下部分对应输出(橘色箭头标识),右下角这个方块是卷积网络处理输入层右下角14×14区域(紫色箭头标识)的结果。
以紫色方块为例,假设你剪切出这块区域(编号1),传递给卷积网络,第一层的**值就是这块区域(编号2),最大池化后的下一层的**值是这块区域(编号3),这块区域对应着后面几层输出的右上角方块(编号 4,5,6)。
所以该卷积操作的原理是我们不需要把输入图像分割成四个子集,分别执行前向传播,而是把它们作为一张图片输入给卷积网络进行计算,其中的公共区域可以共享很多计算,就像这里我们看到的这个 4 个 14×14 的方块一样。
下面我们再看一个更大的图片样本,假如对一个28×28×3的图片应用滑动窗口操作,如果以同样的方式运行前向传播,最后得到8×8×4的结果。跟上一个范例一样,以14×14区域滑动窗口,首先在这个区域应用滑动窗口,其结果对应输出层的左上角部分。接着以大小为2的步幅不断地向右移动窗口,直到第8个单元格,得到输出层的第一行。然后向图片下方移动,最终输出这个8×8×4的结果。因为最大池化参数为2,相当于以大小为 2 的步幅在原始图片上应用神经网络。
总结一下滑动窗口的实现过程,在图片上剪切出一块区域,假设它的大小是 14×14,把它输入到卷积网络。继续输入下一块区域,大小同样是 14×14,重复操作,直到某个区域识别到汽车。
但是正如在前一页所看到的,我们不能依靠连续的卷积操作来识别图片中的汽车,比如,我们可以对大小为28×28的整张图片进行卷积操作,一次得到所有预测值,如果足够幸运,神经网络便可以识别出汽车的位置。
Bounding Box预测(Bounding box predictions)
当我们用以上算法完成滑动窗口法的卷积实现,效率提高了,但仍然存在问题,那就是我们不能输出最精准的边界框。
在滑动窗口法中,你取这些离散的位置集合,然后在它们上运行分类器,在这种情况下,这些边界框没有一个能完美匹配汽车位置,也许最后一个框是最匹配的了。还有看起来这个真实值,最完美的边界框甚至不是方形,稍微有点长方形,长宽比有点向水平方向延伸,有没有办法让这个算法输出更精准的边界框呢?
YOLO algorithm
YOLO(You only look once)是一个可以得到更精准边界框
具体实现
比如你的输入图像是100×100的,然后在图像上放一个网格。为了介绍起来简单一些,在这里用3×3网格,实际实现时会用更精细的网格,比如7x7,19×19。基本思路是使用图像分类和定位算法,然后将算法应用到9个格子上。更具体一点,你需要这样定义训练标签,所以对于 9个格子中的每一个指定一个标签y:
同之前讲到的一样,y是8维的.
=0或1取决于这个绿色格子中是否有图像。
对应于边界框坐标。
代表三个类别:如行人、汽车、摩托车。
我们看看最上方的三个格子,里面什么也没有,所以每个格子的标签向量
中间一行的格子里,有两个对象,YOLO的做法是,取两个对象的中点,然后将这个对象分配给包含对象中点的格子。所以左边的汽车就分配到第二行左面格子(绿色标注)上,右侧的这辆车(车型:神鹰)分配给第二行右面的格子(橙色标注),标签向量均为。所以即使第二行中间的格子同时有两辆车的一部分,我们就假装中心格子没有任何我们感兴趣的对象,所以对于中心格子,分类标签y和最上面的格子一样。所以对于这里9个格子中任何一个,你都会得到一个8维输出向量,因为这里是 3×3 的网格,所以有9个格子,总的输出尺寸是3×3×8,所以目标输出是 3×3×8。因为这里有3×3格子,然后对于每个格子,你都有一个 8 维向量y,所以目标输出尺寸是 3×3×8.
因此,如果训练一个神经网络,输入为100×100×3,通过卷积层,最大池化层等等,最后映射到一个3×3×8输出尺寸。用反向传播训练神经网络时,将任意输入映射到这类输出向量。
这个算法的优点在于神经网络可以输出精确的边界框,所以测试的时候,你做的是喂入输入图像,然后跑正向传播,直到你得到这个输出。
优点 :运行速度很快,效率很高
它显式地输出边界框坐标,所以这能让神经网络输出边界框,可以具有任意宽高比,并且能输出更精确的坐标,不会受到滑动窗口分类器的步长大小限制。其次,这是一个卷积实现,你并没有在3×3网格上跑9次算法,或者,如果你用的是 19×19 的网格,19平方是361次,所以你不需要让同一个算法跑 361 次。相反,这是单次卷积实现,但你使用了一个卷积网络,有很多共享计算步骤,在处理这3×3计算中很多计算步骤是共享的。
关于YOLO的更多详细讲解,详见 “目标检测:YOLO系列 学习笔记”
交并比(Intersection over union)
How to evlauat object localization ?
通过并交比函数,可以用来评价Object Detection算法。
在对象检测任务中,你希望能够同时定位对象,所以如果实际边界框是红色框,你的算法给出这个紫色的边界框,那么这个结果是好还是坏?所以交并比(loU)函数做的是计算两个边界框交集和并集之比。两个边界框的并集是这个区域,就是属于包含两个边界框区域(绿色阴影表示区域),而交集就是这个比较小的区域(橙色阴影表示区域),那么交并比就是交集的大小,这个橙色阴影面积,然后除以绿色阴影的并集面积。
- 交并比:$IOU =\frac{A\bigcap B}{A\bigcup B} $
一般约定,在计算机检测任务中,0.5是阈值,用来判断预测的边界框是否正确。如果IOU≥0.5,就说检测正确,如果预测器和实际边界框完美重叠,loU就是1,因为交集就等于并集。如果你希望边界框更精确一点,你可以将loU定为0.6或者更大的数字。
非极大值抑制(Non-max suppression)
我们发现,算法可能对同一个对象做出多次检测,也就是说算法不是对某个对象检测出一次,而是检测出多次。如何解决这个问题呢?
非极大值抑制这个方法可以确保你的算法对每个对象只检测一次。
何为NMS?
非极大值抑制,简称为NMS算法,英文为Non-Maximum Suppression。其思想是搜素局部最大值,抑制极大值。
这个局部代表的是一个邻域,邻域有两个参数可变,一是邻域的维数,二是邻域的大小。这里不讨论通用的NMS算法(参考论文《Efficient Non-Maximum Suppression》对1维和2维数据的NMS实现),而是用于目标检测中提取分数最高的窗口的。NMS算法在不同应用中的具体实现不太一样,但思想是一样的。非极大值抑制,在计算机视觉任务中得到了广泛的应用,例如边缘检测、人脸检测、目标检测(DPM,YOLO,SSD,Faster R-CNN)等。
为什么要用NMS?
目标检测的过程中在同一目标的位置上会产生大量的候选框,这些候选框相互之间可能会有重叠,此时我们需要利用非极大值抑制找到最佳的目标边界框,消除冗余的边界框。
上图是人脸检测的候选框结果,滑动窗口提取特征后,经分类器分类识别之后,每个边界框有一个置信度得分(confidence score),滑动窗口会导致很多窗口与其他窗口存在包含或者大部分交叉的情况。如果不使用非极大值抑制,就会有多个候选框出现。右图是使用非极大值抑制之后的结果,符合我们人脸检测的预期结果。
而对于刚才的例子而言,会出现这样的情况:
如何实现NMS?
前提:目标边界框列表及其对应的置信度得分列表,设定阈值,阈值用来删除重叠较大的边界框。
IoU:intersection-over-union,即两个边界框的交集部分除以它们的并集。
流程
- 根据置信度得分进行排序
- 选择置信度最高的比边界框添加到最终输出列表中,将其从边 界框列表中删除
- 计算所有边界框的面积
- 计算置信度最高的边界框与其它候选框的IoU。
- 删除IoU大于阈值的边界框
- 选取剩余置信度最大的框,重复上述过程,直至边界框列表为空。
举个例子来理解:先假设有6个矩形框,根据分类器的类别分类概率做排序,假设从小到大属于目标的概率分别为A、B、C、D、E、F。
(1)从最大概率矩形框F开始,分别判断A~E与F的重叠度IOU是否大于某个设定的阈值;
(2)假设B、D与F的重叠度超过阈值,那么就扔掉B、D;并标记第一个矩形框F,是我们保留下来的。
(3)从剩下的矩形框A、C、E中,选择概率最大的E,然后判断E与A、C的重叠度,重叠度大于一定的阈值,那么就扔掉;并标记E是我们保留下来的第二个矩形框。
就这样一直重复,找到所有被保留下来的矩形框。
对于多对象检测,输出标签中就会有多个分量。正确的做法是:对每个输出类别分别独立进行一次非最大值抑制。
代码实现
import numpy as np
#随机定义了4个窗口,置信度分别是[0.5, 0.7, 0.6, 0.7]
dets = np.array([
[204, 102, 358, 250, 0.5],
[257, 118, 380, 250, 0.7],
[280, 135, 400, 250, 0.6],
[255, 118, 360, 235, 0.7]])
thresh = 0.3
def nms(dets, thresh):
x1 = dets[:, 0]
y1 = dets[:, 1]
x2 = dets[:, 2]
y2 = dets[:, 3]
scores = dets[:, 4]
areas = (x2 - x1 + 1) * (y2 - y1 + 1) # 每个boundingbox的面积
order = scores.argsort()[::-1] # boundingbox的置信度排序
keep = [] # 用来保存最后留下来的boundingbox
while order.size > 0:
i = order[0] # 置信度最高的boundingbox的index
keep.append(i) # 添加本次置信度最高的boundingbox的index
# 当前bbox和剩下bbox之间的交叉区域
# 选择大于x1,y1和小于x2,y2的区域
xx1 = np.maximum(x1[i], x1[order[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:]])
# 当前bbox和其他剩下bbox之间交叉区域的面积
w = np.maximum(0.0, xx2 - xx1 + 1)
h = np.maximum(0.0, yy2 - yy1 + 1)
inter = w * h
# 交叉区域面积 / (bbox + 某区域面积 - 交叉区域面积)
ovr = inter / (areas[i] + areas[order[1:]] - inter)
#保留交集小于一定阈值的boundingbox
inds = np.where(ovr <= thresh)[0]
order = order[inds + 1]
return keep
print nms(dets, thresh)
Anchor Boxes
到目前为止,目标检测中存在的一个问题是每个格子只能检测出一个对象,如果你想让一个格子检测出多个对象,就是使用 anchor box 。
同一网格出现了两个目标:人和车。注意行人的中点和汽车的中点几乎在同一个地方,两者都落入到同一个格子中。为了同时检测两个目标,我们可以设置两个Anchor Boxes,Anchor box 1检测人,Anchor box 2检测车。也就是说,每个网格多加了一层输出。原来的输出维度是 3x3x8,现在是3x3x2x8(也可以写成3x3x16的形式)。这里的2表示有两个Anchor Boxes,用来在一个网格中同时检测多个目标。每个Anchor box都有一个Pc值,若两个Pc值均大于某阈值,则检测到了两个目标。
在使用YOLO算法时,只需对每个Anchor box使用上一节的非最大值抑制即可。Anchor Boxes之间并行实现。
遇到的问题
如果我们使用了两个Anchor box,但是同一个格子中却有三个对象的情况,此时只能用一些额外的手段来处理;
同一个格子中存在两个对象,但它们的Anchor box 形状相同,此时也需要引入一些专门处理该情况的手段。
但是以上的两种问题出现的可能性不会很大,对目标检测算法不会带来很大的影响。
如何怎么选择 anchor box?
人们一般手工指定 anchor box 形状,你可以选择 5 到 10 个 anchor box形状,覆盖到多种不同的形状,可以涵盖你想要检测的对象的各种形状。也可以使用其他机器学习算法,如k-平均算法,可以将两类对象形状聚类,选择主要形状作为最具有代表性的一组 anchor box。
YOLO算法
现在我们把之前的讲解过得所有组件组装在一起构成 YOLO 对象检测算法。
训练集:
- 输入X:同样大小的完整图片;
- 目标Y:使用3×3网格划分,输出大小3×3×2×8,或者3×3×16
- 对不同格子中的小图,定义目标输出向量Y。
模型预测:
输入与训练集中相同大小的图片,同时得到每个格子中不同的输出结果:3×3×2×8。
运行非最大值抑制(NMS):
- 我们使用了2个Anchor box,那么对于每一个网格,我们都会得到预测输出的2个bounding boxes,其中一个很低;
- 抛弃概率值低的预测bounding boxes;
- 对每个对象(如行人、汽车、摩托车)分别使用NMS算法得到最终的预测边界框。
候选区域(Region proposals)
之前介绍的滑动窗算法会对原始图片的每个区域都进行扫描,即使是一些空白的或明显没有目标的区域,例如下图所示。这样会降低算法运行效率,耗费时间。
为了解决这一问题,尽量避免对无用区域的扫描,可以使用Region Proposals的方法。
R-CNN(Regions with convolutional networks),会在我们的图片中选出一些目标的候选区域,从而避免了传统滑动窗口在大量无对象区域的无用运算。所以在使用了R-CNN后,我们不会再针对每个滑动窗口运算检测算法,而是只选择一些候选区域的窗口,在少数的窗口上运行卷积网络。
具体实现:运用图像分割算法,将图片分割成许多不同颜色的色块,然后在这些色块上放置窗口,将窗口中的内容输入网络,从而减小需要处理的窗口数量。
Region Proposals共有三种方法:
- R-CNN: 给出候选区域,对每个候选区域进行分类识别,输出对象 标签 和 bounding box,从而在确实存在对象的区域得到更精确的边界框,但速度慢;
- Fast R-CNN: 给出候选区域,使用滑动窗口的卷积实现去分类所有的候选区域,但得到候选区的聚类步骤仍然非常慢;
- Faster R-CNN: 利用卷积对图片进行分割,进一步提高运行速度。
References:
- 非极大值抑制(Non-Maximum Suppression)
- 目标窗口检测算法-NMS非极大值抑制
- 吴恩达Coursera深度学习课程 DeepLearning.ai 提炼笔记(4-3)-- 目标检测
- DeeLearning.ai课程
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:吴恩达《Convolutional Neural Networks》目标检测笔记 - Python技术站