图像处理算法发展迅速,卷积神经网络扮演越来越重要的角色。本文基于Andrew Ng 教授的深度学习专项课程第四门课程的第三周内容来详细介绍卷积神经网络(CNN)中的主要检测算法,包括对象识别定位、如何提升检测精度,YOLO算法,语义分割等概念。
对象的识别与定位
计算机视觉的核心挑战之一是如何使机器能够“看到”和“理解”图像中的内容。不同于人类直观地识别和定位物体,机器需要经过训练,利用算法和数学模型达到类似的效果。这就涉及到了对象定位、Landmark Detection和对象检测的领域。
对象定位 (Object Localization)
目标检测在计算机视觉中是一个研究热点,近年来取得了显著进展。要深入了解目标检测,首先必须理解目标定位的基本概念。简单来说,其任务是在图像中找到一个特定的对象并确定其位置。
在目标定位任务中,需要通过神经网络输出一组参数来描述边界框的位置。这些参数包括边界框的中心坐标 (bx, by),高度 bh 和宽度 bw。通过监督学习。为了训练我们的模型进行对象定位,我们的训练集除了要有图像的类别标签外,还需要提供物体的边界框信息。这样,我们的模型就可以学习如何同时输出类别标签和边界框参数。
在初步的图像处理中,我们可能只对识别对象感兴趣,这称为“分类”。当我们进一步要求模型告诉我们对象在图像中的位置时,这就进入了“定位”的领域。显然,对象定位比单纯的分类更具挑战性,因为它需要更精确地理解图像中的内容。
在分类与定位问题中,我们通常只假设图像中有一个主要的目标物体,这意味着在每个训练样本中最多只有一个边界框。
Landmark Detection
那么在神经网络如何定位目标呢,一般来说,我们训练神经网络输出四个参数(即bx,by,bh,bw)来确定物体的边界框位置。但在更复杂的情况下,网络可以输出图像中重要点的X和Y坐标,这些点被称为“标记点”或landmarks。
假设我们进行面部识别应用的构建,如果我们想要神经网络识别出眼角的位置,可以让神经网络的最后一层输出两个参数lx和ly,表示眼角的坐标。如果我们需要识别出双眼的四个角点,我们可以让神经网络输出更多的参数,例如$l_{1x}$, $l_{1y}$ (表示第一个点),$l_{2x}, l_{2y}$ (表示第二个点),以此类推。
我们可以定义图像中更多的标记点,例如眼睛周围,嘴角,鼻翼等位置。 对于一个面部,我们可以定义例如64个标记点,并让神经网络输出这些点的坐标。 比如,可以让神经网络输出参数$l_{1x}, l_{1y},…,l_{64x}, l_{64y}$来标记这些点。
标记点可以用于识别面部表情。 标记点可以用于人体姿势检测。例如,我们可以标记出胸部中心,左肩,左手肘,手腕等关键位置,然后让神经网络输出这些位置的坐标。
但我们使用标记点要注意,标记点的标签必须在不同的图像之间保持一致。例如,标签1总是指的眼角的这个角点,标签2总是指的眼角的那个角点。此外,我们需要一个足够大的带有标签的数据集来训练神经网络。数据集中的标签应该包含所有我们关心的标记点。
对象检测 (Object Detection)
与对象定位不同,对象检测的目标是识别并定位图像中的所有对象。这不仅涉及到确定哪些对象存在,还要为每个对象确定一个边界框。对象检测在许多实际应用中都很重要,如自动驾驶汽车(检测其他车辆、行人和障碍物)、安全摄像头(检测入侵者或其他潜在威胁)等。
假设我们想建立一个车辆检测算法,我们可以首先创建一个标签训练集,其中包含一些仅包含汽车的图片(例如,图片中的汽车占据了图片的大部分区域)作为正样本,而不包含汽车的图片作为负样本。有了这些标签训练集后,我们可以训练一个ConvNet,该ConvNet输入一个图像,并输出0或1,表示图像中是否有车。
训练好ConvNet后,我们可以将其用于滑动窗口检测。首先选择一个窗口大小,然后将测试图像中的一个小矩形区域输入到ConvNet中,让ConvNet进行预测。然后在测试图像中移动窗口,再次将窗口中的图像输入到ConvNet进行预测。 这个过程一直持续到我们滑过了测试图像中的每一个位置,通过多次调整窗口的大小并重复上述步骤,我们最终可以找到那些包含汽车的窗口。
滑动窗口检测的主要缺点是计算成本高,因为我们需要将图像中的许多不同的矩形区域独立地通过ConvNet。此外,如果我们使用一个非常粗糙的步长,那么我们需要通过ConvNet的窗口数量会减少,但这可能会损害性能。相反,如果我们使用一个非常精细的步长,那么通过ConvNet的小区域的数量就会非常多,这将导致非常高的计算成本。滑动窗口对象检测可以更有效地进行卷积实现,以解决高计算成本的问题。
滑动窗口算法的卷积实现
上面说到滑动窗口算法运行速度太慢,那么我们现在来聊聊如何卷积实现该算法。
假设你的目标检测算法输入的是14x14x3的图像,然后使用16个5x5的滤波器将其映射为10x10x16的体积。接着,通过2x2的最大池化减小体积到5x5x16,然后通过全连接层连接到400个单位,然后再接一个全连接层,最后使用softmax单元输出Y。
我们现在要做的是将这些全连接层转变成卷积层。我们可以将全连接层实现为一个5x5的滤波器,使用400个5x5的滤波器。这将产生一个1x1x400的输出体积,可以被视为1x1x400的卷积层。
接下来,我们将实现一个1x1的卷积。如果你有400个1x1的滤波器,那么下一层将再次是1x1x400的体积。这给出了下一个全连接层。最后,我们将有另一个1x1的滤波器,后面跟着一个softmax激活函数,以得到一个1x1x4的体积,取代网络原先的四个数字输出。
有了这些转换后,我们将看到如何使用卷积实现滑动窗口目标检测。假设你的滑动窗口卷积网络接收14x14x3的图像,然后有如下的神经网络,最终输出一个1x1x4的体积,这是你的softmax的输出。
原始的滑动窗口算法,你可能会将图像的蓝色区域输入卷积网络,然后稍微向下移动一点(这里我们设步幅为两个像素),然后你可能会将绿色矩形输入卷积网络,并运行整个卷积网络以获得另一个标签。然后你可能会将这个橙色区域输入卷积网络,并再次运行它以获得另一个标签。然后再运行一次,用右下角的紫色矩形。在这个16x16x3的图像上运行滑动窗口,你需要运行四次卷积网络以获得四个标签。但实际上,这四个卷积网络所做的大量计算是高度重复的。
滑动窗口的卷积实现让这四个卷积网络部分可以共享很多计算。具体来说,你可以使用相同的5x5滤波器,运行卷积网络,得到12x12x16的输出体积。然后进行最大池化,你将得到6x6x16的体积。通过使用400个5x5的滤波器,你将得到2x2x400的体积。现在,我们不再得到1x1x400的体积,而是得到2x2x400的体积。再次进行1x1的卷积,你将得到另一个2x2x400的体积。最后,你将得到2x2x4的输出体积,而不是1x1x4。
如果你现在希望在28x28x3的图像上运行滑动窗口,那么如果你以相同的方式运行前向传播,你将得到8x8x4的输出。因为最大池化为2,这相当于在原始图像上以步幅为2运行神经网络。
回顾一下,实现滑动窗口的方法:你将一个区域(例如14x14)切割出来并运行卷积网络,然后切割下一个区域进行运行,依次类推,直到成功识别出目标(如汽车)。但现在,你可以一次性对整个图像(可能是28x28)进行预测,通过一次大型卷积网络的前向传播,希望能够识别出汽车的位置。这就是如何卷积实现滑动窗口,使整个过程更加高效。
然而,这种算法仍有一个弱点,那就是边界框的位置可能不太准确。滑动窗口预测的边界框无法完美匹配对象位置而且真实边界框形状可能不是正方形,需要矩形框。为了提高准确性,我们可以使用YOLO算法,这个我们将在后文里面进行学习。
提高检测精度的关键技术
Intersection Over Union(IoU)
如何判断你的对象检测算法是否表现良好? 我们首先来看看一个叫做"交并比(Intersection Over Union,简称IoU)"的函数来评估你的对象检测算法。IoU计算的是两个边界框(bounding boxes)的交集与并集的比例。 并集是包含在两个边界框中的区域,交集是两个边界框重叠的部分。 IoU等于交集的大小除以并集的大小。
IoU 用于评估对象检测算法的准确性。如果IoU大于0.5,则通常认为预测的边界框是正确的。如果预测的边界框与实际的边界框完全重合,那么IoU为1。如果想要更严格的评估,可以将阈值提高到0.6或更高的数字,但是很少看到人们将阈值设定在0.5以下。IoU也是衡量两个边界框相似度的一种方式。
非极大值抑制(Non-max Suppression)
在目标检测中,你可能会遇到一个问题,即算法可能会多次检测到同一对象。非极大值抑制是确保你的算法只检测每个对象一次的方法。当你运行对象分类和定位算法时,可能会有许多网格单元认为它们内包含了一个对象。所以,你可能会有多个对每个对象的检测结果。
非极大值抑制清理这些检测结果,让你最后得到的是每个汽车只有一个检测结果,而不是每个汽车都有多个检测结果。首先,它查看每个检测结果相关联的概率。它首先选择最大的一个,比如说0.9,然后将其标记为最有信心的检测结果。然后,非极大值抑制会查看所有剩余的矩形,抑制所有与刚才输出的矩形有高重叠(高IoU)的矩形。接下来,你再次查看剩余的矩形,找到概率最高的一个,这次是0.8,然后确定这个矩形。非极大值抑制的部分就是再次去除所有与新确定的矩形有高IoU的矩形。
举个例子, 在19x19的网格上,你将得到一个19x19x8的输出体积。在这个例子中,我们简化为只检测汽车。为了实施非极大值抑制,你首先可以丢弃所有与Pc(存在对象的概率)小于或等于某个阈值(比如0.6)的预测。接下来,只要还有任何未处理的矩形,你就重复选择Pc最高的矩形,并将其作为预测输出。然后,丢弃任何剩余的、与刚才输出的矩形有高重叠(高IoU)的矩形。
如果你试图检测三个对象(比如行人、汽车和摩托车),则输出向量将有三个额外的组件。实际上,正确的做法是对每个输出类别独立进行三次非极大值抑制。
Anchor Boxes
之前我们在对象检测中遇到的问题是,每个网格单元只能检测到一个对象。那如果一个网格单元想要检测多个对象怎么办呢?我们可以使用锚定框的概念来解决这个问题。
假设我们有一个图像,仍然以3x3的网格为例。注意行人和汽车的中点几乎在同一位置,且都落在同一网格单元中。 对于这个网格单元,如果输出Y是这个向量,我们在此检测三个类别:行人、汽车和摩托车,它将无法输出两个检测结果。所以我们必须选择两个检测结果中的一个进行输出。 利用锚定框的思想,你要做的是预定义两种不同的形状,称为锚定框或锚定框形状。现在,你可以将两个预测结果与两个锚定框相关联。 一般情况下,你可能会使用更多的锚定框,可能五个甚至更多。但为了让描述更容易,这里只使用两个锚定框。
你将在向量左边的位置定义交叉标签,基本上你将其重复两次。因此,你会得到$p_c, b_x, b_y,b_h,b_w,c_1,c_2,c_3$,这些是与锚定框1相关的八个输出。然后你重复下去到$c_1,c_2,c_3$,这是与锚定框2相关的其他八个输出。 因为行人的形状与锚定框1和2的形状更类似,你可以使用这八个数字来编码,即PC为1,表示有一个行人。然后,你可以使用此来编码围绕行人的边界框,再用此来编码该对象是行人。 然后,因为围绕汽车的框与锚定框2的形状比锚定框1更类似,你可以使用这个来编码第二个对象是汽车,并让边界框等所有参数与检测到的汽车关联。
在你使用锚定框之前,你所做的是为训练集中的每个对象和训练集图像分配对应对象中点的网格单元。所以,输出Y是3x3x8,因为你有一个3x3的网格,对于每个网格位置,我们有PC,边界框,C1,C2,C3的输出向量。 有了锚定框后,你现在做的是每个对象被分配到之前的同一网格单元,分配到包含对象中点的网格单元,但是它被分配到网格单元和与对象形状的最高IoU的锚定框。 因此,现在的输出Y将是3x3x16。如果你有更多的对象,那么Y的维度会更高。
YOLO 算法
上面说过滑动窗口算法存在边界框的位置可能不太准确的问题,这里我们介绍一种算法来提高预测性,叫做YOLO(You Only Look Once)算法。
YOLO在图像上放网格,对每个网格使用分类和定位算法。
对每个网格单元定义标签Y:是否有对象,边界框坐标,类别概率,将对象的中心点分配到包含中心点的网格,输入图像,通过卷积网络预测每个网格的标签Y,直接输出不同形状的精确边界框。
边界框坐标bx,by,bw,bh的编码相对于网格单元的归一化坐标,bx,by在[0,1]内,bx,bw可大于1且有多种编码方式,对准确性有帮助。
我们再看看如何将目标检测的各个组成部分融合,形成YOLO目标检测算法。
- 构建训练集:假设你想训练一个算法来检测三个物体:行人、汽车和摩托车。此外,还需要显式地有完整的背景类,所以这里只是类标签。
- 如果你使用两个anchor boxes,那么输出y将是3x3(使用的是3x3的网格单元),然后乘以2(锚点数目),再乘以8(因为这是该数目的维度)。这8其实是5加上类的数量。这个5是因为你有Pc和bounding boxes,这是5,然后是C1、C2、C3。这个维度等于类的数量。
- 你可以将其视为3x3x2x8,或者3x3x16。因此,为了构建训练集,你需要遍历每一个网格单元并形成相应的目标向量y。
- 训练一个ConvNet,输入图像可能是100x100x3,然后ConvNet最终会输出此输出体积,例如在我们的例子中,是3x3x16或者3x3x2x8。实际应用中,可能更像19x19x16,或者如果你使用更多的anchor boxes,可能是19x19x5x8,因为5x8是40,所以会是19x19x40。
- 算法预测:给定一个图像,你的神经网络会输出3x3x2x8的体积,其中每个网格单元都会得到一个向量。
- 如果网格单元内没有对象,那么你的神经网络应该会在这里输出0,其它值可能会有一些输出,但是基本上会被忽略,因为神经网络告诉你这里没有物体,所以输出的是一个边界框还是车都没关系,这些数字基本上就是一些噪声。
- 对于有物体的网格单元,神经网络的输出应该会是一个相对准确的边界框。
- 使用非最大值抑制(Non-max suppression)进行预测:
- 如果你使用两个anchor boxes,那么每个非网格单元会得到两个预测的边界框。一些有着非常低的可能性,但是你仍然会得到每个九个网格单元的两个预测边界框。
- 然后,你可以除去那些低概率的预测。
- 最后,如果你有三个类要检测,你需要检测行人、汽车和摩托车。你需要做的是,对于每一个类,独立地进行非最大抑制。
Semantic Segmentation with U-Net
在此之前,我们已经学习了物体识别(通过图片输入,识别图片内容,例如是否为猫)和物体检测(在已找到的物体周围划定一个边界框)。 这一节中,我们将进一步学习更为复杂的一系列算法 - 语义分割。其目标是在检测到的物体周围精确绘制轮廓,以确定哪些像素属于物体,哪些像素不属于。在商业应用中,语义分割也具有广泛的用途。
举例来说,如果你正在构建一个自驾车,并看到一张输入图像,你可能想要检测其他车辆的位置。如果你使用物体检测算法,目标可能是在其他车辆周围绘制边界框。但是,如果你希望学习算法判断出图像中每一个像素,那么你可能需要使用语义分割算法,其目标可能是输出一个像素地图,例如用深绿色标记可驾驶的道路。语义分割的一种用途是被一些自驾车团队用于判断哪些像素是安全的,因为它们代表了一个可驾驶的表面。
在医学影像中,给定一个胸部X光片,你可能希望诊断出某人是否有某种疾病,但更有帮助的是,如果你可以在图像中分割出确切的像素,它们对应于患者的某个身体部位。例如在左边的图像中,肺部,心脏,和锁骨(颈骨)被用不同的颜色标记出来。这种分割可以使得更容易发现不规则性,并诊断严重疾病,也可以帮助医生规划手术。
为了简化,让我们使用从背景中分割出汽车的例子。如果你关心的只是在这张图片中分割出汽车,那么你可能决定有两个类别标签,一为汽车,零为非汽车。在这种情况下,分割算法(如Unet算法)的任务将是为图像中的每个像素输出一个或零,其中像素应标记为一,如果它是汽车的一部分,如果不是,应标记为零。
当然如果你想识别的不只是汽车,还有建筑物和道路,那么可以使用三个输出来标记。
Transpose Convolutions
转置卷积是单元(U-net)架构中的一个关键部分。其功能主要是将较小的输入(如2x2)扩展为较大的输出(如4x4)。在常规的卷积过程中,我们可能会输入一个6x6x3的图像,使用一系列3x3x3的过滤器进行卷积,如果我们有5个这样的过滤器,那么最终得到的输出是4x4x5的形状。相比之下,转置卷积看起来有些不同。我们可能会输入一个2x2的激活函数,然后与3x3的过滤器进行卷积,最后得到一个4x4的输出,这比原始输入的尺寸要大。
为了从2x2转变为4x4,我们选择使用3x3的过滤器。过滤器的大小是fxf,我们选择3x3,并且这就是我们将要使用的过滤器。我们也将使用p=1的填充,并在输出中应用这种1p的填充。最后的参数是步长s,我们选择s=2。在常规卷积中,你会将过滤器放在输入上方,然后进行乘法和加法运算。在转置卷积中,你会将过滤器放在输出上方。
神经网络的后半部分使用了转置卷积,将表示的尺寸扩大到原始输入图像的大小。U-Net 架构有一个修改,使其工作得更好,那就是从早期层到后期层的跳过连接,像这样,这个早期的激活块就直接复制到这个后期的块。
为何使用跳过连接 我们为什么要这么做呢?原来,这个接下来的最后一层决定哪个区域是猫,有两种信息是有用的。一种是高级的空间,高级的上下文信息,它从这个前一层得到这种信息。希望神经网络已经搞清楚了,在图像的右下角或者图像的右边部分,有一些类似猫的东西,但是缺少的是非常详细的精细的空间信息。因为这一组激活函数的空间分辨率较低,高度和宽度只是较低。跳过连接允许神经网络获取这个非常高分辨率的低级特征信息,它可以捕获每个像素位置,这个像素有多少毛发质地的东西?并使用跳过连接直接暂停到这个后来的层。这样,这个层就既有低分辨率的,但是高级的空间,高级的上下文信息,也有低级的,但是更详细的质地样的信息,以便决定某个像素是否是猫的一部分。
U-Net的输入是一个具有h高度、w宽度和3个RGB通道的图像。U-Net首先通过标准的前馈神经网络卷积层进行处理。然后,U-Net通过Max池化来减少图像的高度和宽度,同时增加通道数,使得该部分的网络表示具有更高的抽象程度。 U-Net的下一步是应用转置卷积层(用绿色箭头表示)来增大网络的维度。 此外,U-Net还使用跳跃连接(用灰色箭头表示)将早期的激活层直接复制到后期的激活层。之后,U-Net再次应用几个标准的卷积层,接着是另一个转置卷积层以及另一个跳跃连接,重复该过程直到网络的维度恢复到原始输入图像的大小。 最后,U-Net通过一个1x1的卷积层(用洋红色箭头表示)将网络输出映射到分割图。
总结
本文总结了卷积神经网络(CNN)中的几种主要图像检测算法。首先介绍了对象定位、landmark detection和对象检测等基础概念。然后讲解了如何用卷积层实现滑动窗口检测算法,并使用IoU评估检测精度。
为进一步提高检测精度,又介绍了一些关键技术,包括非极大值抑制来去除重复检测,以及Anchor Boxes概念来实现单个网格检测多个对象。此外还说明了YOLO算法,它可以直接输出不同形状的精确边界框。
最后,我们学习了语义分割中的前沿算法U-Net,它使用转置卷积将特征图扩大,并加入跳过连接来结合不同语义层次的信息。U-Net算法可用于交通场景中的自动驾驶,以及医学图像的语义分割。
综上所述,本文系统地介绍了图像检测领域的核心概念和典型算法流程,具有很好的指导意义。这些算法方法构成了计算机视觉中目标检测与语义理解的基石,在众多领域中展现出巨大应用价值。随着深度学习技术的进一步发展,未来这些图像检测与理解技术还将取得更大的进步。
附录-相关文献
- Redmon J, Divvala S, Girshick R, et al. You only look once: Unified, real-time object detection[C]//Proceedings of the IEEE conference on computer vision and pattern recognition. 2016: 779-788.
- Novikov A A, Lenis D, Major D, et al. Fully convolutional architectures for multiclass segmentation in chest radiographs[J]. IEEE transactions on medical imaging, 2018, 37(8): 1865-1876.
- Dong H, Yang G, Liu F, et al. Automatic brain tumor detection and segmentation using U-Net based fully convolutional networks[C]//Medical Image Understanding and Analysis: 21st Annual Conference, MIUA 2017, Edinburgh, UK, July 11–13, 2017, Proceedings 21. Springer International Publishing, 2017: 506-517.
- Ronneberger O, Fischer P, Brox T. U-net: Convolutional networks for biomedical image segmentation[C]//Medical Image Computing and Computer-Assisted Intervention–MICCAI 2015: 18th International Conference, Munich, Germany, October 5-9, 2015, Proceedings, Part III 18. Springer International Publishing, 2015: 234-241.