专业创新实践报告
题目 YOLO v3算法详解以及和Faster-RCNN的比较
YOLO V3算法详解以及和Faster-RCNN的比较
参考来源:目标检测 - 飞桨AI Studio
图像识别和分类:计算机不同于人类,计算机只能够“看到”的是图像被编码之后的数字。例如下图1(右边),人类可以一眼看出图片中有一只狗,而计算机看到的是编码数字(左边)。图像分类任务的目的就是识别图像为哪一个类别。
图1计算机处理得到像素
常规图像分类流程图如下:
可以描述为:对输入的图片进行特征提取,利用提取到的特征预测分类概率,根据训练样本标签来建立起分类损失函数,并以此开启训练,以实现图像分类的目的。
图像分类的结果:将图3-1 识别为狗,右图3-2识别为猫。
图3-1 狗 图 3-2 猫
目标检测的实现依赖于图像分类,它是一种图像分类技术。目标检测通常分为通用型目标检测和单一型目标检测:①通用型:主要是将一幅图像中的所有类别全部检测出来,并标注出其类别和位置。②单一型:主要是将固定的类别检测出来,并标注出其类别和位置。例如人脸检测、文本检测、车牌检测等。
从目标检测的类型中可以看出,目标检测和图像分类有很大的不同。目标检测需要检测出一幅图像中的所有目标,并标注它的位置和类别。如下图4所示,标识出了dog、cat以及它们的位置;而在图像分类的任务中,对于一张图图像来说,提取特征的过程并没有体现出不同目标之间的区别,也没法分别标示出每个物体和类别和它的位置。所以,目标检测是基于图像分类的成功经验,将图像上可能包含目标物体的区域当成一幅单独的图像处理,使用图像分类的模型对该区域进行类别检测,这些区域也称为候选区域(candidate area),使用其他方法表示目标的位置。
图4 目标检测(检测猫,狗的类别和位置)
目标检测问题的关键就在候选区域的生成,最暴力的方法就是穷举:列出图像上的所有区域。穷举法虽然可能可以得到正确的预测结果,但其计算量是非常大,在实际应用中很难实现。2013年,目标检测的任务迎来了突破:Ross Girshick 等人于首次将CNN的方法应用在目标检测任务上,采用传统图像算法selective search产生候选区域,这一算法迎来了重大的突破,这就是对目标检测领域影响深远的区域卷积神经网络(R-CNN)模型。2015年,Ross Girshick 又对此方法进行了改进,提出了Fast RCNN模型,将不同区域的物体共用卷积层的计算,这大大缩减了计算量,并且提高了处理的速度,还引入了调整目标物体位置的回归方法,进一步提高了位置预测的准确性。同年,Shaoqing Ren 等人提出了Faster RCNN模型,提出了RPN(Regional Proposal)的方法来产生物体的候选区域,也第一次提出了anchor boxes这一概念,anchor boxes是学习卷积神经网络用于目标识别过程中最重要且最难理解的一个概念,此后在SSD、YOLOv2、YOLOv3等优秀的目标识别模型中得到了广泛的应用,这一方法里面不再需要使用传统的图像处理算法来产生候选区域,进一步提升了处理速度。
本次实验,我们主要是使用了YOLOv3模型架构来实现对人的检测。我们将拓展展开 YOLO v3标检测方法的具体的实现和Faster-RCNN的框架介绍。主要是介绍单一型的目标检测。
YOLO v1提出了YOLO算法的通用架构,YOLO v2改进了设计并利用预定义的锚框来生成正负样本来训练,而YOLO v3又在YOLO v2的基础上进一步完善了模型架构和训练过程,它的网络架构如下图5所示:
图5 YOLO v3的网络架构
从图中看出,YOLO v3检测目标的步骤为:
详细步骤在本文中将展开描述。
可以看出,在YOLO V3中主要采用了三个改进:
YOLO v3 使用的 Darknet53 在53 层网络的基础上,又在上面再堆叠了 53 层,为 YOLO v3 提供了一个 106 层的全卷积底层架构。它的检测通过在网格中三个不同位置的三个不同大小的特征图上应用 1 x 1 检测内核来完成的。Darknet53的架构图如下图6:
图6 Darknet53的架构图
Convolutional是指Conv2d+BN+LeakyReLU。比如说对于尺寸[640*640]的原图,在代码中将C0,C1,C2的形状大小输出可得: 的形状为[1,1024,20,20]、的形状为[1,512,40,40]、的形状为[1,256,80,80]。
形状通过步幅来确定,对于该尺寸为[640*640]的原图:步幅为32时,640/32=20,即对于形状的参数20。同时,也可以看出C1的步幅是16,C2的步幅是8。以输入的尺寸是640x640为例,预测的三个特征层大小分别是20,40,80。分别检测小、中、大的目标,以此实现多尺度的检测。如下图7所示:
图7 多尺度目标检测(猫和花朵)
下采样使得图片尺寸减半,具体实现方式是使用stirde=2的卷积,在本次的实验代码中为:
1. self.downsample = (in_channels != out_channels)
2. if self.downsample:
3. self.down_sample_layer = _conv2d(in_channels, out_channels, 1, stride=stride)
2个上采样的代码为:
self.conv1 = _conv_bn_relu(in_channel=backbone_shape[-2], out_channel=backbone_shape[-2]//2, ksize=1)
self.upsample1 = P.ResizeNearestNeighbor((feature_shape[2]//16, feature_shape[3]//16))
self.backblock1 = YoloBlock(in_channels=backbone_shape[-2]+backbone_shape[-3],
out_chls=backbone_shape[-3],
out_channels=out_channel)
self.conv2 = _conv_bn_relu(in_channel=backbone_shape[-3], out_channel=backbone_shape[-3]//2, ksize=1)
self.upsample2 = P.ResizeNearestNeighbor((feature_shape[2]//8, feature_shape[3]//8))
self.backblock2 = YoloBlock(in_channels=backbone_shape[-3]+backbone_shape[-4],
out_chls=backbone_shape[-4],
out_channels=out_channel)
self.concat = P.Concat(axis=1)
在目标检测中,需要确定目标物体的位置,使用边界框来描述其位置信息。一般有两种常规的格式表示边界框的位置:
①使用(x1,y1,x2,y2)描述框体的位置。其中(x1,y1)为矩形框左上角的坐标,(x2,y2)是矩形右下角的坐标。常见的人脸目标检测中,一般矩形框的边平行于X,Y坐标轴,所以这两个左上和右下的坐标可以确定一个完整的矩形框。
图8 三个矩形框
例如:在我们本次的目标检测实战中,检测出的一个结果如图 。图8中的三个框的边界框可表示为(粗略表示):
左边红色框:(301.85,978.47,712.16,682.52)
中间红色框:(850.22,943.61,1089.73,567.28)
右边红色框:(962.51,718.34,1677.18,0.003)
②使用(x,y,w,h)表示框体的位置,(x,y)表示矩形框体中心点的坐标,w是矩形框体的宽度,h是矩形框体的高度。同样矩形框也是平行于X,Y轴,有一个中心点、宽以及高也可以完整绘制把矩形框位置绘制出来。
在检测任务中,训练集的标签中标识了真实框体的位置信息,这样的边界框称为真实框(ground truth box),预测的边界框信息通过训练的标签信息的预测模型得到。
对于单一类别的目标检测,其表现为一个五维的向量。在本次实验提供的代码中,通常这样获得向量中的每个值:
1. box_xy = prediction[:, :, :, :, :2] ##位置信息:cx,cy
2. box_wh = prediction[:, :, :, :, 2:4] ##尺度信息:cw,wh
3. box_confidence = prediction[:, :, :, :, 4:5] #类别信息:x
4. box_probs = prediction[:, :, :, :, 5:] #含目标的概率:P
P表示box中存在检测的目标的概率,在代码中对应box_confidence;
用于给出预测框的位置(第二种表示矩形框的位置),在代码中对应box_xy,box_wh。依据这个可以画出图像上的预测框。
对于我们本次的目标实战,只检测人,所以一个网格只检测一种目标,标签信息包含一个五维的向量。若想要检测n种目标,则返回的标签中应带有,x1-xn的值为0或者1,表示属于哪一种类别。代码中box_probs即为类别信息,且只有一个值,这表示只在一个网格上检测出一种目标,若还想多检测一种类别,则应该在这个向量的基础上再加一个同等维度的向量。
举例:想在本次实验的图像中即检测出人,又检测出狗(可能在同一个网格中),则返回的标签信息应包含一个14维的向量((1+4+2)*2=14);若是不在同一个网格中检测出多类目标,则返回的标签信息应包含一个7维的向量(1+4+2=7)。
锚框是在目标检测模型中,以某种规则在图片上生成一系列框体,把这些锚框当成可能的候选区域。它可以解决一个窗口只能检测一个目标和无法解决多尺度的问题。算法会以一个中心产生一系列锚框,因为包含了尺寸比例和长宽比,所以生成的锚框范围不会超出图片尺寸之外,如下图9:
图9 生成一系列锚框(蓝色A标识的为锚框,G标识的为真实框)
模型会对这些候选区域是否包含物体进行预测,如果包含目标物体,则继续预测出物体所属的类别。不包含目标物体,则不用继续检测。从图中可以看出,锚框A1跟真实框G1最接近。
锚框的尺寸确定通常有三种方法:①人为经验选取②k-means聚类③作为超参数进行学习
因为锚框位置是固定的,所以它通常就不大可能刚好跟真实的边界框重合,需要在锚框的基础上进行微调才能形成能准确描述物体位置的预测框,模型需要预测出微调的幅度。在训练过程中,模型通过学习不断的调整参数,最终能学会如何判别出锚框所代表的候选区域是否包含目标物体,如果包含物体的话,判断物体属于哪个类别,以及物体真实边界框相对于锚框位置需要调整的幅度。总结下来,预测框就是在锚框的基础上进行微调,具体步骤会在下文给出。
若是在每个地方都生成锚框,计算量太大,所以算法会对原图进行下采样处理,得到feature map,在feature map中生成锚框。YOLO v3和YOLO v2都有应用Anchor box,而YOLOv1中没有应用Anchor box。
Iou这个概念是用来来描述两个框之间的重叠程度。两个框可以看成是两个像素的集合,它们的交并比等于两个框重合部分的面积除以它们合并起来的面积。下图a中红色区域是两个框的重合面积,图b中蓝色区域是两个框的相并面积。用这两个面积相除即可得到它们之间的交并比。
公式:iou = intersect_area / (box1_area + box2_area - intersect_area)
其中intersect_area两两框之间的交面积(交集),box1_area为其中一个矩形的面积,box2_area为另一个矩形的面积。所以(box1_area + box2_area - intersect_area)代表了两两框体的并面积(并集),可以使用下图10可视化理解IoU:
图10 并交比的可视化计算
在本次实验中,有编写计算IoU的类,其核心代码如下:
1.###计算交集
2.intersect_area = P.Squeeze(-1)(intersect_wh[:, :, :, :, :, 0:1]) * \
3.P.Squeeze(-1)(intersect_wh[:, :, :, :, :, 1:2])
4.###计算两个box的面积
5.box1_area = P.Squeeze(-1)(box1_wh[:, :, :, :, :, 0:1]) * P.Squeeze(-1)(box1_wh[:, :, :, :, :, 1:2])
6.box2_area = P.Squeeze(-1)(box2_wh[:, :, :, :, :, 0:1]) * P.Squeeze(-1)(box2_wh[:, :, :, :, :, 1:2])
7.###IoU
8.iou = intersect_area / (box1_area + box2_area - intersect_area)
YOLO3的核心思想是把图片划分为不同的n*m的网格,体现的思想就是分而治之:即每个网格点负责一个区域的检测,若物体的中心点落在了这个区域范围内,则这个物体就由这个网格点来确定。可以划分为不同尺度的网格,大网格预测的是大体量的目标物体,小网格预测的是小体量的目标物体。
图11 YOLO v3的训练流程图
流程分为两部分,左边部分是在图片上划分小方块,并产生一系列的候选区域,根据候选区域与原图上的真实框的接近程度来确定为正样本和负样本。
右边部分则是使用卷积神经网络提取图片的特征,前面我们已经在介绍锚框的时候也提过可以通过特征图减少计算量,对候选区域的位置和类别进行预测。
所以,每个预测框就是其中的一个样本(正负样本以及无关样本的确定需要用到非极大值抑制,下文会进一步阐述),根据样本和真实框的位置、形状、标签、目标置信度之间差距,可以建立起损失函数。
正负样本均为预测框,所以YOLO v3损失函数包括了三部分:
①位置和形状预测的损失
②目标置信度的损失
③类别的损失。
其中目标置信的损失包含了含目标物体的box的置信度预测损失和不含目标物体的box的置信度预测损失。损失的计算公式如下,其含义已在图12中标注出来:
图12 损失函数的计算公式及其标注
在本次给出的实现代码中体现如下,先计算三部分的损失:
1.##位置的损失
2.xy_loss = self.reduce_sum(xy_loss, ())
3.##尺度的损失
4.wh_loss = self.reduce_sum(wh_loss, ())
5.##置信度的损失
6.confidence_loss = self.reduce_sum(confidence_loss, ())
7.##类别的损失
8.class_loss = self.reduce_sum(class_loss, ())
三个部分的loss值相加即为最终的loss值:
1.loss = xy_loss + wh_loss + confidence_loss + class_loss
得到特征图后,YOLO-V3算法会在每个候选区域的中心,生成一系列锚框。例如下图13,以左上角为中心,生成一系列锚框。
图13 以同一点为中心,生成一系列锚框
在代码中,可以看到YOLO-V3的模型参数中的锚框的参数:
可以从代码中看到在每个中心都有生成9个不同尺度的锚框。对应的是3个不同尺度(n*n,2n*2n,3n*3n)来分别预测大物体、中物体和小物体。
那如何将锚框接近我们的预测框呢? 又该如何使得预测得到的锚框和真实的框尽量的重合?这里将具体展开锚框的微调原理和过程。
前面我们已经提到YOLO的核心思想是将图片划分为许多的网格,如图下所示,分为15*20(480/32=15,640/32=20)个大小一样的网格方块。假设,图像中第10行,第4列的小方块位置附近有三个已生成的锚框,如下图14所示,用红色框线标注出来。
图14 三个锚框的标注
锚框的中心点的位置,即为:(4.5,10.5),锚框所在方块的左上角的坐标为(4,10),假设其中一个锚框的大小为(350,250)。可以通过下面的方式生成预测框的中心坐标和大小:
根据下面的公式进行位置的微调得到预测框的位置b_x,b_y.
sigmoid函数图像如下图15所示:从图像上我们可以观察到一些直观的特性,函数的取值在0-1之间,且在0.5处为中心对称,并且越靠近x=0的取值斜率越大。
图15 sigmoid函数图像
代码中如下:
1. grid_x = P.Cast()(F.tuple_to_array(range_x), ms.float32)
2. grid_y = P.Cast()(F.tuple_to_array(range_y), ms.float32)
3. # Tensor of shape [grid_size[0], grid_size[1], 1, 1] representing the coordinate of x/y axis for each grid
4. grid_x = self.tile(self.reshape(grid_x, (1, 1, -1, 1, 1)), (1, grid_size[0], 1, 1, 1))
5. grid_y = self.tile(self.reshape(grid_y, (1, -1, 1, 1, 1)), (1, 1, grid_size[1], 1, 1))
6. # Shape is [grid_size[0], grid_size[1], 1, 2]
7. grid = self.concat((grid_x, grid_y))
8. box_xy = prediction[:, :, :, :, :2]
9. box_wh = prediction[:, :, :, :, 2:4]
10. ###使用sigmoid作为微调
11. box_xy = (self.sigmoid(box_xy) + grid) / P.Cast()(F.tuple_to_array((grid_size[1], grid_size[0])), ms.float32)
12. box_wh = P.Exp()(box_wh) * self.anchors / self.input_shape
grid_x和grid_y为随机生成的实数,使用sigmoid微调来使锚框逼近预测框,相当于通过缩放anchor box来更好的预测到真实的box的长宽。
若
图16 微调锚框后得到的预测框以及真实标注的真实框
可以看出预测框和真实框之间存在偏差。在训练样本中,已存在真实框的位置信息,可调整预测框的b_x,b_y,b_h,b_w,使之边为真实框的位置和尺度,由此也可以得到我们训练的目标值t_x,t_y,t_w,t_h根据网络输出的值和目标值就可以建立我们的损失函数,通过学习网络参数,就可以使得输出值接近最终的目标值。
观察下图17,可以看到只有一张人脸,但却有好几个预测框标注,且它们的置信度都不一样。从内到外分别为:0.33、0.69、0.63、0.64,这些框体是存在冗余的,而且从图中我们也可看出第二大的预测框更加准确(置信度为0.69),所以需要去除冗余的框,并留下最准确的预测框。
图17 预测框冗余
YOLO3使用非极大值抑制(non-maximum suppression, nms)消除冗余框,它使用IoU(具体计算公式参考上文)来衡量预测框是否对应同一个物体。初始时,所有预测框的objectness均为1,表示存在目标。算法只留下某个类别得分最高的预测框,若其他预测框和它的IoU大于阈值,则直接把其他预测框丢弃,并将其objectness(是否存在目标)标签设置为-1,不参与损失函数的计算,而不是变为0,当做负样本。
IoU的阈值需要提前设置,属于超参数,一般可以设置为0.5或者0.65。简单的列举下IoU的值与两个锚框之间位置和大小的关系如下:
图18 IoU值和框体的位置形状关系
从图中可以明显看出,IoU很大就代表两个预测框非常的接近,所以我们保留置信度大的预测框,同时减少冗余。在本次实验中,从代码我们可以看出,IoU的阈值为0.5,如下:
1. for box_index, box in enumerate(boxes):
2. bbox_pred = [box[1], box[0], box[3], box[2]]
3. count_pred[classes[box_index]] += 1
4. for anno in gt_anno:
5. class_ground = anno[4]
6. if classes[box_index] == class_ground:
7. ###calc_iou的函数,上面已经分析过计算公式;
8. iou = calc_iou(bbox_pred, anno)
9. ###阈值为0.5;
10. if iou >= 0.5:
11. count_correct[class_ground] += 1
12. break
代码的详细步骤如下:
如上图 若想这幅图像只保留尽可能准确的框,可以适当减小IoU的值,但是可能会使的其他预测框丢弃过多,导致无法标注出全部的真实目标物体。
正确例子的具体的演示如下图19:
图19 检测人像和狗的目标
人像的三个预测框置信度分别为(0.9, 0.7, 0.65)。置信度最高的为0.9的白色预测框,与所有预测框进行IoU计算,因为置信度为0.7和0.65的黄色框与该预测框的IoU超过了阈值,所以丢弃。同样,狗处的预测框也通过非极大值抑制最终保留置信度为0.65的红色预测框。
保留下来的预测框最终的保留为正样本(objectness=1),如下图20所示。被丢弃的含有目标的预测框(objectness=-1),表示其既不属于正样本,也不属于负样本,不会参与损失函数的计算。所有其他不含目标的预测框,其objectness标签均设置为0,表示其为负样本。YOLO v3采用了回归的方法。
图20 最终保留的预测框
由上,我们也可以总结出YOLO V3对于不同锚框的objectness的处理如下图21所示:
图21 objectness的处理
1.若该锚框与真实框IoU最大,将其objectness置为1,标注其为真样本,必然参与损失值计算。
2.若该锚框存在目标物体,与真实框的IoU超过了阈值,且objectness没有标识为1,则objectness置为-1,不必计算位置和类别
3.对于其余的预测框,不包含目标物体,则objectness置为0,标识为负样本,也不需要计算位置和类别,没有意义。
在网络架构和预测框的生成中,我们提到了通过特征图来匹配预测框的生成来减少不必要的计算。对于任意一个预测框,需要5+C个参数表示需检测出多少个类别,具体的计算参考上文边界框的生成)参数来确定对其位置尺寸、是否含有目标、目标所属类别。在每个网格处都生成k个预测框,则预测值的数目应该为
的全连接层,会使得计算量过大。以640*480大小的图像为例,当 步幅为32时,会得到20*15的特征图(640/32=20,480/32=15),因为最小的网格数目也是20*15,所以,可以将网格与特征图上的像素点对应起来。总结,对特征图使用多次卷积使得与每个预测框所需要的预测值对应。在下图2表现为使得像素点(i,j)与与第i行第j列的小方块区域所需要的预测值关联起来。
图22 关联特征图和预测框
①计算含有目标的概率:表现预测框中objectness=1的概率
②位置和尺度
③相应类别
在上文中已经提高过了具体含义和代码的计算,如下:
可以通过Netron来可视化模型,有一个在线网址https://netron.app/
图23 Netron在线主页
我从网上下载了一个已经训练了50个epoch对七类别的昆虫进行目标检测的YOLO v3的模型(yolo_epoch50.pdparams),可视化其模型如下图24(很小一部分),因为模型参数和层数过多,只截出了一部分。
图24 yolo_epoch50.pdparams模型解析
可以通过通模型的可视化来展开更多的研究和发现。
将在 YOLO v3多分类目标昆虫检测..ipynb文件中展开解释,其中包含了对各个部分:锚框,预测框,IoU,非极大值抑制,网络架构,特征提取的具体代码展示,参考Paddle-百度架构师手把手教深度学习。
在介绍目标检测的发展史时,我们有提到Faster-RCNN第一次使用了RPN,使得模型效率大大提升。Faster -RCNN是典型的two-stage目标检测算法,two-stage检测算法将检测问题划分为两个阶段,首先产生候选区域,再对位置精修后进行候选区域分类。
Faster-RCNN是一种卷积神经网络目标检测方法,基于R-CNN和Fast RCNN,Faster RCNN将特征抽取(feature extraction)、proposal提取、bounding box regression(rect refine)、classification都整合在了一个网络中,使得综合性能有较大提高,它的模型基础架构如下图所示:
图25 Fater RCNN的基础架构
从架构图可以看出,Faster RCNN 整体网络可以分为4个主要内容,如下图所示:
图26 Faster-RCNN的网络构成
VGG16模型中的 Faster-RCNN的网络结构图如下:
图27 VGG16模型中的 Faster-RCNN的网络结构
文字描述如下:
输入的图像大小为任意P*Q,经过处理将其大小固定为MxN。将MxN的图像送入卷积网络(该卷积网络包括13个conv层、13个relu层、4个pooling层),经过4个池化层,所以最后卷积层输出的尺寸大小为[M/16,N/16],可以得到特征图Feature Map;在RPN网络中先经过3x3卷积,再分别生成foreground anchors与bounding box regression偏移量,然后计算出候选区;而Roi Pooling层则利用候选区从特征图中提取候选特征,送入后续全连接和softmax网络分类。
Faster-RCNN和YOLO v3比较:
YOLO v1采用的是全连接的网络,YOLO v2,v3采用的是全卷积的网络。V4改进了V3的损失函数,因为平方误差并不适那么合做回归。
YOLO v1没有Anchor box这个概念,而其以后的YOLO算法都引入了Anchor box。
在算法效率方面上,YOLO v1的召回率很低,对于召回率和准确度的理解需要借助下表1:
表1 真实值和预测值
预测值 |
|||
1(决定P) |
0(决定N) |
||
真实值 |
1 |
TP |
FN |
0 |
FP |
TN |
YOLO v2使用网络Darknet-19(由19个卷积层)作为backbon。YOLO v2与v1相比准确度下降了,但是召回率大大提升。
YOLO v3采用Darkent-53作为backbon。引入了多尺度预测,其算法可以输出是哪个大小不同的特征值。
总的来说,本次实验获得了以下两点结论: