卷积神经网络三:目标检测和yolo算法

1 目标定位

对象检测,它是计算机视觉领域中一个新兴的应用方向,相比前两年,它的性能越来越好。在构建对象检测之前,我们先了解一下对象定位,首先我们看看它的定义。

image-20221116221357281

图片分类任务我们已经熟悉了,就是算法遍历图片,判断其中的对象是不是汽车,这就是图片分类。这节课我们要学习构建神经网络的另一个问题,即定位分类问题。这意味着,我们不仅要用算法判断图片中是不是一辆汽车,还要在图片中标记出它的位置,用边框或红色方框把汽车圈起来,这就是定位分类问题。其中“定位”的意思是判断汽车在图片中的具体位置。这周后面几天,我们再讲讲当图片中有多个对象时,应该如何检测它们,并确定出位置。比如,你正在做一个自动驾驶程序,程序不但要检测其它车辆,还要检测其它对象,如行人、摩托车等等,稍后我们再详细讲。

本周我们要研究的分类定位问题,通常只有一个较大的对象位于图片中间位置,我们要对它进行识别和定位。而在对象检测问题中,图片可以含有多个对象,甚至单张图片中会有多个不同分类的对象。因此,图片分类的思路可以帮助学习分类定位,而对象定位的思路又有助于学习对象检测,我们先从分类和定位开始讲起。

图片分类问题你已经并不陌生了,例如,输入一张图片到多层卷积神经网络。这就是卷积神经网络,它会输出一个特征向量,并反馈给softmax单元来预测图片类型。

image-20221116221622336

如果你正在构建汽车自动驾驶系统,那么对象可能包括以下几类:行人、汽车、摩托车和背景,这意味着图片中不含有前三种对象,也就是说图片中没有行人、汽车和摩托车,输出结果会是背景对象,这四个分类就是softmax函数可能输出的结果。

这就是标准的分类过程,如果你还想定位图片中汽车的位置,该怎么做呢?我们可以让神经网络多输出几个单元,输出一个边界框。具体说就是让神经网络再多输出4个数字,标记为 b x , b y , b h , b w b_x ,b_y,b_h,b_w bx,by,bh,bw,这四个数字是被检测对象的边界框的参数化表示。
image-20221116221932249

我们先来约定本周课程将使用的符号表示,图片左上角的坐标为(0,0),右下角标记为(1,1)。要确定边界框的具体位置,需要指定红色方框的中心点,这个点表示为 ( b x , b y ) (b_x ,b_y) (bx,by),边界框的高度为 b h b_h bh ,宽度为 b w b_w bw 。因此训练集不仅包含神经网络要预测的对象分类标签,还要包含表示边界框的这四个数字,接着采用监督学习算法,输出一个分类标签,还有四个参数值,从而给出被检测对象的边界框位置。

在这个例子中,大概是
b x = 0.5 b_x=0.5 bx=0.5 ,(因为它表示汽车位于图片水平方向的中间位置)
b y = 0.7 b_y=0.7 by=0.7 (表示汽车位于距离图片底部\frac{3}{10})
b h = 0.3 b_h=0.3 bh=0.3(因为红色方框的高度是图片高度的0.3倍),
b w = 0.4 b_w=0.4 bw=0.4(红色方框的宽度是图片宽度的0.4倍),

下面我再具体讲讲如何为监督学习任务定义目标标签y。

请注意,这有四个分类,神经网络输出的是这四个数字和一个分类标签,或分类标签出现的概率

image-20221116223416238

它是一个向量,第一个组件 p c p_c pc表示是否含有对象,如果对象属于前三类,则 p c = 1 p_c=1 pc=1,如果是背景,则图片中没有要检测的对象,则 p c = 0 p_c=0 pc=0 。我们可以这样理解 p c p_c pc ,它表示被检测对象属于某一分类的概率,背景分类除外。

如果检测到对象,就输出被检测对象的边界框参数 b x , b y , b h , b w b_x ,b_y,b_h,b_w bx,by,bh,bw。最后,如果存在某个对象,那么 p c = 1 p_c=1 pc=1 ,同时输出 c 1 , c 2 , c 3 c_1,c_2,c_3 c1,c2,c3,表示该对象属于1-3类中的哪一类,是行人,汽车还是摩托车。鉴于我们所要处理的问题,我们假设图片中只含有一个对象,所以针对这个分类定位问题,图片最多只会出现其中一个对象。

下面看一个例子

image-20221116224940636

假如这是一张训练集图片,标记为X,即上图的汽车图片。而在 y 当中,第一个元素 p c = 1 p_c=1 pc=1 ,因为图中有一辆车, b x , b y , b h , b w b_x ,b_y,b_h,b_w bx,by,bh,bw会指明边界框的位置,所以标签训练集需要标签的边界框。图片中是一辆车,所以结果属于分类2,因为定位目标不是行人或摩托车,而是汽车,所以 c 1 = 0 , c 2 = 1 , c 3 = 0 c_1 =0, c_2=1,c_3=0 c1=0,c2=1c3=0 c 1 , c 2 , c 3 c_1, c_2,c_3 c1,c2c3 中最多只有一个等于1。

这是图片中只有一个检测对象的情况,如果图片中没有检测对象呢?如果训练样本是这样一张图片呢?

image-20221116225419242

这种情况下, p c = 0 p_c=0 pc=0,y 的其它参数将变得毫无意义,这里我全部写成问号,表示“毫无意义”的参数,因为图片中不存在检测对象,所以不用考虑网络输出中边界框的大小,也不用考虑图片中的对象是属于 c 1 , c 2 , c 3 c_1, c_2,c_3 c1,c2c3中的哪一类。针对给定的被标记的训练样本,不论图片中是否含有定位对象,构建输入图片 x和分类标签 y的具体过程都是如此。这些数据最终定义了训练集。

最后,我们介绍一下神经网络的损失函数,其参数为类别 y 和网络输出 y ^ \hat{y} y^ ,如果采用平方误差策略,则 L ( y ^ , y ) = L(\hat{y},y)= L(y^,y)=
image-20221116225822041

注意这里y有8个元素,损失值等于每个元素相应差值的平方和。这是 y 1 = 1 y_1=1 y1=1的情况,另一种情况 y 1 = 0 y_1=0 y1=0

image-20221116230112663

L ( y ^ , y ) = ( y 1 ^ − y 1 ) 2 L(\hat{y},y)=(\hat{y_1}-y_1)^2 L(y^,y)=(y1^y1)2因为对于这种情况,我们不用考虑其它元素,只需要关注神经网络输出 p c p_c pc的准确度。

为了让大家了解对象定位的细节,这里我用平方误差简化了描述过程。实际应用中,你可以对 c 1 , c 2 , c 3 c_1,c_2,c_3 c1,c2,c3应用对数损失函数,对边界框坐标应用平方差或类似方法,对 p c p_c pc应用逻辑回归函数,甚至采用平方预测误差也是可以的。

以上就是利用神经网络解决对象分类和定位问题的详细过程,结果证明,利用神经网络输出批量实数来识别图片中的对象是个非常有用的算法。下节课,我想和大家分享另一种思路,就是把神经网络输出的实数集作为一个回归任务,这个思想也被应用于计算机视觉的其它领域,也是非常有效的.

2 特征点检测

上节课,我们讲了如何利用神经网络进行对象定位,即通过输出四个参数值 b x , b y , b h , b w b_x ,b_y,b_h,b_w bx,by,bh,bw 给出图片中对象的边界框。更概括地说,神经网络可以通过输出图片上特征点的 ( x , y ) 坐标来实现对目标特征的识别,我们看几个例子。

image-20221116232049279

假设你正在构建一个人脸识别应用,出于某种原因,你希望算法可以给出眼角的具体位置。眼角坐标为(x,y)你可以让神经网络的最后一层多输出两个数字 l x , l y l_x,l_y lx,ly,作为眼角的坐标值。

image-20221116232236993

如果你想知道两只眼睛的四个眼角的具体位置,那么从左到右,依次用四个特征点来表示这四个眼角。对神经网络稍做些修改,输出第一个特征点 ( l 1 x , l 1 y ) (l_{1x},l_{1y}) (l1x,l1y),第二个特征点, ( l 2 x , l 2 y ) (l_{2x},l_{2y}) (l2x,l2y)依此类推,这四个脸部特征点的位置就可以通过神经网络输出了。
image-20221116232435548

也许除了这四个特征点,你还想得到更多的特征点输出值,这些(图中眼眶上的红色特征点)都是眼睛的特征点,你还可以根据嘴部的关键点输出值来确定嘴的形状,从而判断人物是在微笑还是皱眉,也可以提取鼻子周围的关键特征点。为了便于说明,你可以设定特征点的个数,假设脸部有64个特征点,有些点甚至可以帮助你定义脸部轮廓或下颌轮廓。选定特征点个数,并生成包含这些特征点的标签训练集,然后利用神经网络输出脸部关键特征点的位置。

image-20221116232622151

具体做法是,准备一个卷积网络和一些特征集,将人脸图片输入卷积网络,输出face=1或0,1表示有人脸,0表示没有人脸,然后输出 ( l 1 x , l 1 y ) (l_{1x},l_{1y}) (l1x,l1y)……直到 ( l 64 x , l 64 y ) (l_{64x},l_{64y}) (l64x,l64y)。这里我用 l l l代表一个特征,这里有129个输出单元,其中face=1表示图片中有人脸,因为有64个特征,64×2=128,所以最终输出128+1=129个单元,由此实现对图片的人脸检测和定位。这只是一个识别脸部表情的基本构造模块,如果你玩过Snapchat或其它娱乐类应用,你应该对AR(增强现实)过滤器多少有些了解,Snapchat过滤器实现了在脸上画皇冠和其他一些特殊效果。检测脸部特征也是计算机图形效果的一个关键构造模块,比如实现脸部扭曲,头戴皇冠等等。当然为了构建这样的网络,你需要准备一个标签训练集,也就是图片 x 和 y 标签的集合,这些特征点都是人为辛苦标注的。

image-20221116233045590

最后一个例子,如果你对人体姿态检测感兴趣,你还可以定义一些关键特征点,如胸部的中点,左肩,左肘,腰等等。然后通过神经网络标注人物姿态的关键特征点,再输出这些标注过的特征点,就相当于输出了人物的姿态动作。当然,要实现这个功能,你需要设定这些关键特征点,从胸部中心点 ( l 1 x , l 1 y ) (l_{1x},l_{1y}) (l1x,l1y)一直往下,直到 ( l 32 x , l 32 y ) (l_{32x},l_{32y}) (l32x,l32y)

一旦了解如何用二维坐标系定义人物姿态,操作起来就相当简单了,批量添加输出单元,用以输出要识别的各个特征点( x , y )的坐标值。要明确一点,特征点1的特性在所有图片中必须保持一致,就好比,特征点1始终是右眼的外眼角,特征点2是右眼的内眼角,特征点3是左眼内眼角,特征点4是左眼外眼角等等。所以标签在所有图片中必须保持一致,假如你雇用他人或自己标记了一个足够大的数据集,那么神经网络便可以输出上述所有特征点,你可以利用它们实现其他有趣的效果,比如判断人物的动作姿态,识别图片中的人物表情等等。

3 目标检测

学过了对象定位和特征点检测,今天我们来构建一个对象检测算法。这节课,我们将学习如何通过卷积网络进行对象检测,采用的是基于滑动窗口的目标检测算法。

image-20221118161516662

假如你想构建一个汽车检测算法,步骤是,首先创建一个标签训练集,也就是 x 和 y 表示适当剪切的汽车图片样本。出于我们对这个训练集的期望,你一开始可以使用适当剪切的图片,就是整张图片 x几乎都被汽车占据,你可以照张照片,然后剪切,剪掉汽车以外的部分,使汽车居于中间位置,并基本占据整张图片(还有没有汽车的负样本)有了这个标签训练集,你就可以开始训练卷积网络了,输入这些适当剪切过的图片,卷积网络输出 y ,0或1表示图片中有汽车或没有汽车。训练完这个卷积网络,就可以用它来实现滑动窗口目标检测,具体步骤如下。

image-20221118161909698

假设这是一张测试图片,首先选定一个特定大小的窗口,比如图片下方这个窗口,将这个红色小方块输入卷积神经网络,卷积网络开始进行预测,即判断红色方框内有没有汽车。

滑动窗口目标检测算法接下来会继续处理第二个图像,

image-20221118161959991

即红色方框稍向右滑动之后的区域,并输入给卷积网络,因此输入给卷积网络的只有红色方框内的区域,再次运行卷积网络,然后处理第三个图像,依次重复操作,直到这个窗口滑过图像的每一个角落。

为了滑动得更快,我这里选用的步幅比较大,思路是以固定步幅移动窗口,遍历图像的每个区域,把这些剪切后的小图像输入卷积网络,对每个位置按0或1进行分类,这就是所谓的图像滑动窗口操作。

重复上述操作,不过这次我们选择一个更大的窗口,截取更大的区域,并输入给卷积神经网络处理,你可以根据卷积网络对输入大小调整这个区域,然后输入给卷积网络,输出0或1。

image-20221118162329934

再以某个固定步幅滑动窗口,重复以上操作,遍历整个图像,输出结果。

然后第三次重复操作,这次选用更大的窗口。image-20221118162501346

如果你这样做,不论汽车在图片的什么位置,总有一个窗口可以检测到它。

image-20221118162617765

比如刚好有一个圈住黑色汽车的这个方框,然后预测结果为1。这种算法叫作滑动窗口目标检测,因为我们以某个步幅滑动这些方框窗口遍历整张图片,对这些方形区域进行分类,判断里面有没有汽车。

滑动窗口目标检测算法也有很明显的缺点,就是计算成本,因为你在图片中剪切出太多小方块,卷积网络要一个个地处理。如果你选用的步幅很大,显然会减少输入卷积网络的窗口个数,但是粗糙间隔尺寸可能会影响性能。反之,如果采用小粒度或小步幅,传递给卷积网络的小窗口会特别多,这意味着超高的计算成本。

所以在神经网络兴起之前,人们通常采用更简单的分类器进行对象检测,比如通过采用手工处理工程特征的简单的线性分类器来执行对象检测。至于误差,因为每个分类器的计算成本都很低,它只是一个线性函数,所以滑动窗口目标检测算法表现良好,是个不错的算法。然而,卷积网络运行单个分类人物的成本却高得多,像这样滑动窗口太慢。除非采用超细粒度或极小步幅,否则无法准确定位图片中的对象。

不过,庆幸的是,计算成本问题已经有了很好的解决方案,大大提高了卷积层上应用滑动窗口目标检测器的效率,关于它的具体实现,我们下节课再讲。

4 卷积的滑动窗口实现

上节课,我们学习了如何通过卷积网络实现滑动窗口对象检测算法,但效率很低。这节课我们讲讲如何在卷积层上应用这个算法。

为了构建滑动窗口的卷积应用,首先要知道如何把神经网络的全连接层转化成卷积层

image-20221119100553437

假设对象检测算法输入一个14×14×3的图像,图像很小,不过演示起来方便。接下来演示如何把上面的全连接层转化成卷积层。

image-20221119100832987

400个5×5×16的卷积核,输出维度就是1×1×400,我们不再把它看作一个含有400个节点的集合,而是一个1×1×400的输出层。从数学角度看,它和全连接层是一样的,因为这400个节点中每个节点都有一个5×5×16维度的过滤器,所以每个值都是上一层这些5×5×16激活值经过某个任意线性函数的输出结果。

然后在跟一个1×1的卷积

image-20221119101309938

其实这个1×1的卷积和上面蓝色圈里的全连接一样,最后经由1×1过滤器的处理,得到一个softmax激活值

image-20221119101448848

以上就是用卷积层代替全连接层的过程。

掌握了卷积知识,我们再看看如何通过卷积实现滑动窗口对象检测算法。(画得比较简单,严格来说,14×14×3应该是一个长方体,第二个10×10×16也是一个长方体,但为了方便,我只画了正面。所以,对于1×1×400的这个输出层,我也只画了它1×1的那一面,所以这里显示的都是平面图,而不是3D图像。)

image-20221119102516289

测试集图片是16×16×3,在最初的滑动窗口算法中,你会把上面的蓝色区域(红圈)输入卷积网络生成0或1分类。接着滑动窗口步幅为2个像素,向右滑动

image-20221119144957680

把绿色区域的输入卷积网络,运行整个卷积网络,得到另一个标签0或1。继续将橘色区域输入卷积网络,卷积后得到另一个标签

image-20221119150112955

最后对右下方的紫色区域进行最后一次卷积操作

image-20221119150420711

网络运行了四次于是输出了四个标签。结果发现这四次卷积操作中有很多计算都是重复的,所以卷积窗口的卷积应用使得4次操作共享很多计算

image-20221119151233354

如果你想了解具体的计算步骤,以绿色方块为例,假设你剪切出这块区域(绿色阴影),传递给卷积网络,第一层的激活值就是这块区域(12×12×16中的绿色阴影),最大池化后的下一层的激活值是这块区域(6×6×16中的绿色阴影)这块区域对应着后面几层输出的右上角方块。

image-20221119152249257

所以该卷积操作的原理是我们不需要把输入图像分割成四个子集,分别执行前向传播,而是把它们作为一张图片输入给卷积网络进行计算,其中的公共区域可以共享很多计算,就像这里我们看到的这个4个14×14的方块一样。

下面再看一个更大的图片样本

image-20221119152859418

假如对一个28×28×3的图片应用滑动窗口操作,如果以同样的方式运行前向传播,最后得到8×8×4的结果。跟上一个范例一样,以14×14区域滑动窗口,首先在这个区域应用滑动窗口,其结果对应输出层的左上角部分。接着以大小为2的步幅不断地向右移动窗口,直到第8个单元格,得到输出层的第一行。然后向图片下方移动,最终输出这个8×8×4的结果。因为最大池化参数为2,相当于以大小为2的步幅在原始图片上应用神经网络。

总结一下滑动窗口的实现过程

image-20221119164503083

总结一下滑动窗口的实现过程,在图片上剪切出一块区域,假设它的大小是14×14,把它输入到卷积网络。继续输入下一块区域,大小同样是14×14,重复操作,直到某个区域识别到汽车。

但是正如在前面所看到的,我们不用依靠连续的卷积操作来识别图片中的汽车,比如,我们可以对大小为28×28的整张图片进行卷积操作,一次得到所有预测值,如果足够幸运,神经网络便可以识别出汽车的位置。

以上就是在卷积层上应用滑动窗口算法的内容,它提高了整个算法的效率。不过这种算法仍然存在一个缺点,就是边界框的位置可能不够准确。下节课,我们将学习如何解决这个问题。

5 Bounding Box预测

在这里插入图片描述

可能上面第四幅图这个框就是最匹配的了,但是看起来最完美的边界框甚至不是方形,稍微有点长方形(红色)

image-20221119191613795

长宽比有点向水平方向延伸,有没有办法让这个算法输出更精准的边界框呢?其中一个能得到更精准边界框的算法是YOLO算法,YOLO(You only look once)是这么做的

image-20221119192234784

比如你的输入图像是100×100的,然后在图像上放一个网格,为了介绍起来简单一点,我用3×3的网格 实际实现时会用更精细的网络(可能是19×19),基本思路是

image-20221119192709333

基本思路是,采用图像分类和定位算法,前面介绍过的,逐一应用在图像的9个格子中。

image-20221119193039846

这张图里有9个格子,所以对于每个格子都有这么一个向量。这张图有两个对象,YOLO算法做的就是,取两个对象的中点,然后将这个对象分配给包含对象中点的格子(所以第一辆车分给绿色框,第二辆车分给黄色框)所以即使中心的格子同时有两辆车的一部分,我们就假装中心格子没有任何我们感兴趣的对象,所以对于中心格子,分类标签 y和上面紫色的y标签类似(?就是don’t care)。

image-20221119213859415

所以绿色和黄色框对应的标签y如上图右侧。所以对于这里9个格子中任何一个,你都会得到一个8维输出向量,因为这里是3×3的网格,所以有9个格子,总的输出尺寸是3×3×8,所以目标输出是3×3×8。
image-20221119214159824

因为这里有3×3格子,然后对于每个格子,你都有一个8维向量 y ,所以目标输出尺寸是3×3×8。

如果你现在要训练一个输入为100×100×3的神经网络

image-20221119214403977

现在这是输入图像,然后你有一个普通的卷积网络,卷积层,最大池化层等等,最后你会有这个,选择卷积层和最大池化层,这样最后就映射到一个3×3×8输出尺寸。所以你要做的是,有一个输入 x ,就是这样的输入图像,然后你有这些3×3×8的目标标签 y 。当你用反向传播训练神经网络时,将任意输入 x 映射到这类输出向量 y 。

所以这个算法的优点在于神经网络可以输出精确的边界框,所以测试的时候,你做的是喂入输入图像 x ,然后跑正向传播,直到你得到这个输出 y 。然后对于这里3×3位置对应的9个输出,我们在输出中展示过的,你就可以读出1或0(pc),你就知道9个位置之一有个对象。如果那里有个对象,那个对象是什么(c1,c2,c3),还有格子中这个对象的边界框是什么(bx,by,bh,bw)。只要每个格子中对象数目没有超过1个,这个算法应该是没问题的。

一个格子中存在多个对象的问题,我们稍后再讨论。我们这里用的是比较小的3×3网格,实践中你可能会使用更精细的19×19网格,所以输出就是19×19×8。这样的网格精细得多,那么多个对象分配到同一个格子得概率就小得多。

重申一下,把对象分配到一个格子的过程是,你观察对象的中点,然后将这个对象分配到其中点所在的格子,所以即使对象可以横跨多个格子,也只会被分配到9个格子其中之一,就是3×3网络的其中一个格子,或者19×19网络的其中一个格子。在19×19网格中,两个对象的中点处于同一个格子的概率就会更低。

所以要注意,首先这和图像分类和定位算法非常像,我们在第一节课讲过的,就是它显式地输出边界框坐标,所以这能让神经网络输出边界框,可以具有任意宽高比,并且能输出更精确的坐标,不会受到滑动窗口分类器的步长大小限制。其次,这是一个卷积实现,你并没有在3×3网格上跑9次算法,或者,如果你用的是19×19的网格,19平方是361次,所以你不需要让同一个算法跑361次。相反,这是单次卷积实现,但你使用了一个卷积网络,有很多共享计算步骤,在处理这3×3计算中很多计算步骤是共享的,或者你的19×19的网格,所以这个算法效率很高。

b x , b y , b h , b w b_x,b_y,b_h,b_w bx,by,bh,bw该如何编码

image-20221119221528053

每个格子左上角为(0,0)右下为(1,1), b x , b y b_x,b_y bx,by肯定在0-1之间,但是 b h , b w b_h,b_w bh,bw可能大于1,比如下面这种情况

image-20221119222103436

6 交并比

你如何判断对象检测算法运作良好呢?在节中,你将了解到并交比函数,可以用来评价对象检测算法。在下一节中,我们用它来插入一个分量来进一步改善检测算法

image-20221121114637586

在对象检测任务中,你希望能够同时定位对象,所以如果实际边界框是这样的,你的算法给出这个紫色的边界框,那么这个结果是好还是坏?所以交并比(loU)函数做的是计算两个边界框交集和并集之比。两个边界框的并集是这个区域,就是属于包含两个边界框区域(绿色阴影表示区域),而交集就是这个比较小的区域(橙色阴影表示区域),那么交并比就是交集的大小,这个橙色阴影面积,然后除以绿色阴影的并集面积。

一般约定,在计算机检测任务中,如果 loU≥ 0.5 ,就说检测正确,如果预测器和实际边界框完美重叠,loU就是1。

所以这是衡量定位精确度的一种方式,你只需要统计算法正确检测和定位对象的次数,你就可以用这样的定义判断对象定位是否准确。再次,0.5是人为约定,没有特别深的理论依据,如果你想更严格一点,可以把阈值定为0.6。有时我看到更严格的标准,比如0.6甚至0.7,但很少见到有人将阈值降到0.5以下。

7 非极大值抑制

到目前为止你们学到的对象检测中的一个问题是,你的算法可能对同一个对象做出多次检测,所以算法不是对某个对象检测出一次,而是检测出多次。非极大值抑制这个方法可以确保你的算法对每个对象只检测一次,我们讲一个例子。

image-20221121115544967

假设你需要在这张图片里检测行人和汽车,你可能会在上面放个19×19网格,理论上这辆车只有一个中点(黄色点),所以它应该只被分配到一个格子里,左边的车子也只有一个中点(绿色点),所以理论上应该只有一个格子做出有车的预测。

实践中当你运行对象分类和定位算法时,对于每个格子都运行一次,其他格子(下图黄色和绿色)也可能会认为这辆车的中心在其内部

image-20221121115756764

我们分步介绍一下非极大抑制是怎么起效的,

image-20221121120521777

因为你要在361个格子上都运行一次图像检测和定位算法,那么可能很多格子都会举手说我的 pc,我这个格子里有车的概率很高,而不是361个格子中仅有两个格子会报告它们检测出一个对象。所以当你运行算法的时候,最后可能会对同一个对象做出多次检测,所以非极大值抑制做的就是清理这些检测结果。这样一辆车只检测一次,而不是每辆车都触发多次检测。

image-20221121120938629

所以具体上,这个算法做的是,首先看看每次报告每个检测结果相关的概率 pc,首先看概率最大的那个,这个例子(右边车辆)中是0.9,然后就说这是最可靠的检测,所以我们就用高亮标记,就说我这里找到了一辆车。这么做之后,非极大值抑制就会逐一审视剩下的矩形,**所有和这个最大的边框有很高交并比,高度重叠的其他边界框,那么这些输出就会被抑制。**所以这两个矩形分别是0.6和0.7,这两个矩形 pc 和淡蓝色矩形重叠程度很高,所以会被抑制,变暗,表示它们被抑制了。
image-20221121121112771

接下来,逐一审视剩下的矩形,找出概率最高, pc最高的一个,在这种情况下是0.8,我们就认为这里检测出一辆车(左边车辆),然后非极大值抑制算法就会去掉其他loU值很高的矩形。所以现在每个矩形都会被高亮显示或者变暗,如果你直接抛弃变暗的矩形,那就剩下高亮显示的那些,这就是最后得到的两个预测结果。

所以这就是非极大值抑制,非最大值意味着你只输出概率最大的分类结果,但抑制很接近,但不是最大的其他预测结果,所以这方法叫做非极大值抑制。

看下算法细节

image-20221121200334398

这里简化一下只识别是否有车(没有c1,c2,c3了),并去除低概率的边框

image-20221121200729928

然后重复上面紫色边框的步骤,找到最大的pc做为输出,然后抑制和它交并比 >=0.5的边框,重复这个过程,直到每个边界框都判断过了。

上面只介绍了算法检测单个对象的情况,如果你尝试同时检测三个对象,比如说行人、汽车、摩托,那么输出向量就会有三个额外的分量。事实证明,正确的做法是独立进行三次非极大值抑制,对每个输出类别都做一次。

这就是非极大值抑制,如果你能实现我们说过的对象检测算法,你其实可以得到相当不错的结果。但结束我们对YOLO算法的介绍之前,最后我还有一个细节想给大家分享,可以进一步改善算法效果,就是anchor box的思路,我们下节再介绍。

8 Anchor box

到目前为止,对象检测中存在的一个问题是每个格子只能检测出一个对象,如果你想让一个格子检测出多个对象,你可以这么做,就是使用anchor box这个概念,我们从一个例子开始讲吧

image-20221122084417274

假设你有这样一张图片,对于这个例子,我们继续使用3×3网格,注意行人的中点和汽车的中点几乎在同一个地方,两者都落入到同一个格子中。所以对于那个格子,如果 y 输出这个向量(图片下的)你可以检测这三个类别,行人、汽车和摩托车,它将无法输出检测结果,所以我必须从两个检测结果中选一个。

image-20221122084714206

而anchor box的思路是,这样子,预先定义两个不同形状的anchor box,或者anchor box形状,你要做的是把预测结果和这两个anchor box关联起来。一般来说,你可能会用更多的anchor box,可能要5个甚至更多,但对于这个,我们就用两个anchor box,这样介绍起来简单一些。
你要做的是定义类别标签,用的向量是左侧y重复两次(变成右侧的y)

绿色方框标记的参数是和anchor box 1关联的8个参数,后面的8个参数是和anchor box 2相关联。因为行人的形状更类似于anchor box 1的形状,而不是anchor box 2的形状,所以你可以用这8个数值(前8个参数) p c = 1 p_c=1 pc=1代表这里有个行人,用 b x , b y , b h , b w b_x,b_y,b_h,b_w bx,by,bh,bw包住行人的边界框,用c1,c2,c3的编码说明对象是行人。

然后是车子,因为车子的边界框比起anchor box 1更像anchor box 2的形状,所以用后面八位编码与anchor box关联

image-20221122091712699

现在每个对象都和之前一样分配到同一个格子中,分配到对象中点所在的格子中,以及分配到和对象形状交并比最高的anchor box中。

image-20221122091941417

红色是检测对象,紫色的是anchor box,所以选择竖着的这个anchor box (交并比最大)

不管选的是哪一个,这个对象不只分配到一个格子,而是分配到一对,即(grid cell,anchor box)对,这就是对象在目标标签中的编码方式。所以现在y的输出就是3×3×16

image-20221122100234312

所以这个中下格子对应的标签如右侧所示(有两个对象所以两个anchor)

如果只有car,标签如下

image-20221122100455858

现在还有一些额外的细节,如果你有两个anchor box,但在同一个格子中有三个对象,这种情况算法处理不好,你希望这种情况不会发生,但如果真的发生了,这个算法并没有很好的处理办法,对于这种情况,我们就引入一些打破僵局的默认手段。还有这种情况,两个对象都分配到一个格子中,而且它们的anchor box形状也一样,这是算法处理不好的另一种情况,你需要引入一些打破僵局的默认手段,专门处理这种情况,希望你的数据集里不会出现这种情况,其实出现的情况不多,所以对性能的影响应该不会很大。

这就是anchor box的概念,我们建立anchor box这个概念,是为了处理两个对象出现在同一个格子的情况,实践中这种情况很少发生,特别是如果你用的是19×19网格而不是3×3的网格,两个对象中点处于361个格子中同一个格子的概率很低,确实会出现,但出现频率不高。也许设立anchor box的好处在于anchor box能让你的学习算法能够更有征对性,特别是如果你的数据集有一些很高很瘦的对象,比如说行人,还有像汽车这样很宽的对象,这样你的算法就能更有针对性的处理,这样有一些输出单元可以针对检测很宽很胖的对象,比如说车子,然后输出一些单元,可以针对检测很高很瘦的对象,比如说行人。

最后,你应该怎么选择anchor box呢?人们一般手工指定anchor box形状,你可以选择5到10个anchor box形状,覆盖到多种不同的形状,可以涵盖你想要检测的对象的各种形状。还有一个更高级的版本,我就简单说一句,你们如果接触过一些机器学习,可能知道后期YOLO论文中有更好的做法,就是所谓的k-平均算法,可以将两类对象形状聚类,如果我们用它来选择一组anchor box,选择最具有代表性的一组anchor box,可以代表你试图检测的十几个对象类别,但这其实是自动选择anchor box的高级方法。如果你就人工选择一些形状,合理的考虑到所有对象的形状,你预计会检测的很高很瘦或者很宽很胖的对象,这应该也不难做。

所以这就是anchor box,在下一节中,我们把学到的所有东西一起融入到YOLO算法中。

9 YOLO算法

你们已经学到对象检测算法的大部分组件了,在这个视频里,我们会把所有组件组装在一起构成YOLO对象检测算法。

image-20221123142740268

我们先看看如何构造你的训练集,假设你要训练一个算法去检测三种对象,行人、汽车和摩托车,你还需要显式指定完整的背景类别。这里有3个类别标签,如果你要用两个anchor box,那么输出 y 就是3×3×2×8,其中3×3表示3×3个网格,2是anchor box的数量,8是向量维度,要构造训练集,你需要遍历9个格子,然后构成对应的目标向量y 。

第一个格子里面没有需要检测的对象(行人,车子,摩托)所以对应的格子的目标y就是上图右边的蓝色箭头

image-20221123144118026

下面中间的绿色格子,因为车子的边框(红色),假设水平方向更长和anchor2(右侧的紫色框)的交并比更大,那么车子就和向量的下半部分相关。

image-20221123144526419

所以你这样遍历9个格子,遍历3×3网格的所有位置,你会得到这样一个向量,得到一个16维向量,所以最终输出尺寸就是3×3×16。和之前一样,简单起见,我在这里用的是3×3网格,实践中用的可能是19×19×16,或者需要用到更多的anchor box,可能是19×19×5×8,即19×19×40,用了5个anchor box。这就是训练集,然后你训练一个卷积网络,输入是图片,可能是100×100×3,然后你的卷积网络最后输出尺寸是,在我们例子中是3×3×16或者3×3×2×8。

神经网络的预测如下

image-20221123144849416

最后还要跑一下非最大值抑制

image-20221123145533742

我们看看一张新的测试图像,这就是运行非极大值抑制的过程。如果你使用两个anchor box,那么对于9个格子中任何一个都会有两个预测的边界框,其中一个的概率 pc很低。但9个格子中,每个都有两个预测的边界框,比如说我们得到的边界框是是这样的,注意有一些边界框可以超出所在格子的高度和宽度.

接下来你抛弃概率很低的预测,去掉这些连神经网络都说这里很可能什么都没有,所以你需要抛弃这些

image-20221123151109278

最后如果你有三个对象检测类别,你希望检测行人,汽车和摩托车,那么你要做的是,对于每个类别单独运行非极大值抑制,处理预测结果所属类别的边界框,非极大值抑制来处理行人类别,用非极大值抑制处理车子类别,然后对摩托车类别进行非极大值抑制,运行三次来得到最终的预测结果。所以算法的输出最好能够检测出图像里所有的车子,还有所有的行人

image-20221123151334250

10 候选区域(选修)

如果你们阅读一下对象检测的文献,可能会看到一组概念,所谓的候选区域,这在计算机视觉领域是非常有影响力的概念。我把这个视频定为可选视频是因为我用到候选区域这一系列算法的频率没有那么高,但当然了,这些工作是很有影响力的,你们在工作中也可能会碰到。先复习一下滑动窗口

image-20221206215948204

你使用训练过的分类器,在这些窗口中全部运行一遍,然后运行一个检测器,看看里面是否有车辆,行人和摩托车。现在你也可以运行一下卷积算法,这个算法的其中一个缺点是,它在显然没有任何对象的区域浪费时间,对吧。

所以Ross Girshick,Jeff Donahue,Trevor Darrell,Jitendra Malik,在论文中提出一种叫做R-CNN的算法,意思是带区域的卷积网络,或者说带区域的CNN。这个算法尝试选出一些区域,在这些区域上运行卷积网络分类器是有意义的,所以这里不再针对每个滑动窗运行检测算法,而是只选择一些窗口,在少数窗口上运行卷积网络分类器

选出候选区域的方法是运行图像分割算法,分割的结果是下边的图像(最右侧),为了找出可能存在对象的区域。比如说,分割算法在这里得到一个色块,所以你可能会选择这样的边界框,然后在这个色块上运行分类器,就像这个绿色的东西。

image-20221123153127359

所以这个细节就是所谓的分割算法,你先找出可能2000多个色块,然后在这2000个色块上放置边界框,然后在这2000个色块上运行分类器,这样需要处理的位置可能要少的多,可以减少卷积网络分类器运行时间,比在图像所有位置运行一遍分类器要快。特别是这种情况,现在不仅是在方形区域中运行卷积网络,我们还会在高高瘦瘦的区域(深蓝色)运行,尝试检测出行人,然后我们在很宽很胖的区域(浅蓝色)运行,尝试检测出车辆,同时在各种尺度运行分类器。

image-20221123153520427

这就是R-CNN或者区域CNN的特色概念,现在看来R-CNN算法还是很慢的。所以有一系列的研究工作去改进这个算法,所以基本的R-CNN算法是使用某种算法求出候选区域,然后对每个候选区域运行一下分类器,每个区域会输出一个标签,有没有车子?有没有行人?有没有摩托车?并输出一个边界框,这样你就能在确实存在对象的区域得到一个精确的边界框。

澄清一下,R-CNN算法不会直接信任输入的边界框,它也会输出一个边界框$ b_x,b_y,b_h,b_w$这样得到的边界框比较精确,比单纯使用图像分割算法给出的色块边界要好,所以它可以得到相当精确的边界框。

现在R-CNN算法的一个缺点是太慢了,所以这些年来有一些对R-CNN算法的改进工作,Ross Girshik提出了快速的R-CNN算法,它基本上是R-CNN算法,不过用卷积实现了滑动窗法。最初的算法是逐一对区域分类的,所以快速R-CNN用的是滑动窗法的一个卷积实现,这和你在本周第四节(4 卷积的滑动窗口实现)中看到的大致相似,这显著提升了R-CNN的速度。
image-20221123153838043

事实证明,Fast R-CNN算法的其中一个问题是得到候选区域的聚类步骤仍然非常缓慢,所以另一个研究组,任少卿、何凯明、Ross Girshick和孙剑提出了更快的R-CNN算法(Faster R-CNN),使用的是卷积神经网络,而不是更传统的分割算法来获得候选区域色块,结果比Fast R-CNN算法快得多。不过我认为大多数更快R-CNN的算法实现还是比YOLO算法慢很多。

候选区域的概念在计算机视觉领域的影响力相当大,所以我希望你们能了解一下这些算法,因为你可以看到还有人在用这些概念。对我个人来说,这是我的个人看法而不是整个计算机视觉研究界的看法,我觉得候选区域是一个有趣的想法,但这个方法需要两步,首先得到候选区域,然后再分类,相比之下,能够一步做完,类似于YOLO或者你只看一次(You only look once)这个算法,在我看来,是长远而言更有希望的方向。但这是我的个人看法,而不是整个计算机视觉研究界的看法,所以你们最好批判接受。但我想这个R-CNN概念,你可能会想到,或者碰到其他人在用,所以这也是值得了解的,这样你可以更好地理解别人的算法。

猜你喜欢

转载自blog.csdn.net/m0_52118763/article/details/128211417
今日推荐