原文 | One-stage object detection
中文 | 小小将:深入理解one-stage目标检测算法
今天看了上面这篇专栏文章,收获颇丰,整理一下要点。
【目录】
- 1. One-stage目标检测算法流程
- 为什么要对检测器实施约束?
- 2. anchor是什么?有什么作用?
- 3. 选择先验框的策略
- 3.1 yolo选择先验框的策略
- 3.2 SSD选择先验框的策略
- 4. 怎样由最后的feature map转换成bbox的?
- 四个坐标值
- 20个类别概率
- 一个置信度
- 5. 网络是怎样学习的?
- 5.1 卷积预测
- 5.2 损失函数的设计
- 5.3 使用优化器来训练
- 5.4 训练策略&技巧
1. One-stage目标检测算法流程
输入: VOC数据集的一张图片(20个类别),输入尺寸设置为416×416,(SSD是300×300)
输出:13×13×125特征图(降采样了32倍,416/32=13)
【Attention】一般来说,如果数据集有c类,而模型有k个检测器(k是特征图上每个位置anchor的数量),那么网格需要有k×(4+1+c)个输出通道。
图1
最后得到的13×13特征图可以看成是13×13个单元格,每个单元格都有5个独立的物体检测器(anchor的k=5),每个检测器都预测一个边界框。
检测器的位置是固定的,它只能检测中心点落在该单元内的物体(这一限制是为了保证检测器不发生混乱,所以加以约束)。每个检测器产生25个值:
- 20个值表征类别概率 (使用softmax可以获取该检测器中目标所属的类别,也可以使用sigmoid进行多标签分类)
- 1个值表示置信度 (表明该检测器包含目标的可能性,介于0-1之间)
- 4个值表示边界框坐标
由于每个单元格都有5个检测器,因此共有5×25=125个输出通道。
该模型总是预测固定数量的边界框:13×13个单元乘以5个检测器给出845个预测结果。显然,绝大多数的预测框都是无用信息。可以
通过置信度来忽略掉一些预测框,再使用NMS可以去除重叠框。为什么要对检测器实施约束?
为了让模型更容易学习,将每个检测器分配到图像中的固定位置去学习,每个检测器只负责学习附近的物体,而不会预测较远的问题,是onet-stage目标检测算法的技巧。(如果没有这样的约束,所有检测器都会在整张图中去预测,很容易出现混乱)
2. anchor是什么?有什么作用?
上面的分析可知,网格的划分对检测框的位置进行了约束,那么,是否可以对检测框的形状也进行约束呢?答案是肯定的。这就是anchor的任务。
anchor是提前设定好的一系列检测框(大小和尺寸都是提前定好了的),我们称为先验框,我们的检测器去学习任意形状或大小的物体或许很难,
检测器仅预测相比先验框的偏移值,训练更容易,因为预测值全为零时等价于输出先验框,平均上更接近真实物体。如果没有先验框,每个检测器都必须从头开始学习不同的边界框形状,这相当困难。
3. 选择先验框的策略
先验框只是一个高度和宽度列表,在前面说了,每个网格单元预测5个先验框,这5个先验框分别使用不同的大小和长宽比,这样就可以尽可能的覆盖多种形状的目标。先验框数量和形状的选取规则是什么呢?
3.1 yolo选择先验框的策略
yolo系列通过
在所有训练图像的所有边界框上进行k-means聚类来选择先验框(k=5,因此它找到五个最常见的物体形状)。因此,YOLO的先验框适合当前训练(和测试)的数据集。
k-means算法中数据点是数据集中所有真实边界框的宽度和高度值。如果我们在Pascal VOC数据集的框中运行k-means,将得到5个簇:(k选为5并不是因为它效果最好,而是速度和精度之间折中的结果)
(PS:本文说的yolo普遍指yolo-v3)
图2
3.2 SSD选择先验框的策略
SSD不使用k-means来确定先验框。SSD在六个不同尺度(38*38、19*19、10*10、5*5、3*3、1*1)的特征图上都选取了先验框,在不同特征图上,每个网格单元选取的先验框数量是不同的(分别为4、6、6、6、4、4),对于
先验框的尺寸,是使用数学公式计算得到的。因此SSD的先验框与数据集无关(SSD称它们为“default boxes”)。
另一个小差异是:YOLO的先验框只是宽度和高度,但SSD的先验框也有x,y位置。其实,YOLO也包含位置,只是默认先验框位置始终位于网格单元格的中心(对于SSD,先验框也是在网格中心)。
4. 怎样由最后的feature map转换成bbox的?
对于网络输出的13×13×125特征图,我们要将它们转换成边界框。
从图1可以看出,对于一个检测框,它的输出对应到特征图上就是橙色那一条线,有25个输出值:
4.1 四个表示坐标值
网络输出的不是绝对坐标,而是相对于先验框的4个偏移,通常为(
delta_x, delta_y,delta_w, delta_h
)的形式,前两个表示检测框中心与先验框中心点的偏移(先验框的中心点就是网格单元的中心),后两个表示检测框的宽高相对于先验框的缩放值。所以,要将这个坐标映射回真实图像中:
真实宽度
box_w[i, j, b] = anchor_w[b] * exp(delta_w[i, j, b]) * 32
真实高度
box_h[i, j, b] = anchor_h[b] * exp(delta_h[i, j, b]) * 32
这里的i,j表示网格单元的序号(第几行第几列),b表示检测器的序号(对于每个网格单元,有5个检测器)。若delta_w>0,那么exp(delta_w)大于1,此时预测框比先验框大。特别地,delta_w=0,那么exp(delta_w)为1,此时预测框与先验框一样大小。顺便说一下,这里之所以要乘以32是因为先验框坐标是13x13网格上的,网格上各个像素点等价于原始416x416图像上的32个像素点。
对于真实位置的计算,yolo和SSD算法不同,yolo如下:
真实位置x
box_x[i, j, b] = (i + sigmoid(delta_x[i, j, b])) * 32
真实位置y
box_y[i, j, b] = (j + sigmoid(delta_y[i, j, b])) * 32
SSD为:
真实位置x
box_x[i, j, b] = (anchor_x[b] + delta_x[i, j, b]*anchor_w[b]) * image_w
真实位置y
box_y[i, j, b] = (anchor_y[b] + delta_y[i, j, b]*anchor_h[b]) * image_h
区别主要在两点:
- yolo使用了sigmoid函数,将
和delta_x
限制在0~1之间,保证检测器只预测中心点在该单元格中的物体。而SSD中,物体的中心可以落在单元格的外面。delta_y
- SSD预测的坐标是相对于先验框中心而不是单元格中心。实际上一般情况下两个中心是重合的,但是SSD和YOLO采用的坐标系不一样。 SSD的先验框坐标是归一化到[0,1],这样它们独立于网格大小,yolo的先验框坐标是网格的坐标(没有归一化) (SSD之所以这样是采用了不同大小的网格)
【Attention】
只有在预测的时候,才需要将预测框的坐标映射回原图。在训练的时候,是将ground truth通过log运算也转换为相对于先验框的偏移值,两个偏移值在做loss。
4.2 20个表示类别概率
对于每个检测框,网络为其输出了20个类别值(对于VOC数据集),这个值
需要经过softmax或者sigmoid得到概率分布,使用sigmoid时变成了一个multi-label多标签分类器,每个预测框可以同时有多个类别,YOLOv3使用的就是sigmoid。
4.3 一个置信度
对于yolo,除了坐标之外,模型每个边界框预测了一个置信度,一般采用sigmoid函数得到0~1的概率值。
对于SSD,没有预测置信度,而是给分类器增加了一个特殊的类:背景。如果分类结果是背景,那么意味着检测器没有找到物体,这实际上等价于YOLO给出一个较低的置信度。
5. 网络是怎样学习的?
5.1 卷积预测
13×13的网格是卷积层得到的. 卷积实际上是在整个输入图像上以较小的窗口(卷积核)进行滑动,卷积核在每个位置是共享的。我们的样例模型最后的卷积层包含125个卷积核。
这125个卷积核在13×13特征图中的每个位置上滑动,并在每个位置上进行预测。然后,我们将这125个数字解释为5个预测边界框。最初,在每个网格位置预测到的125个数字将是完全随机和无意义的,但是随着训练,损失函数将引导模型学习做出更有意义的预测。
尽管每个网格单元中有5个检测器,但对于845个检测器来说,这个模型实际上只学习了总共5个检测器,而不是每个网格单元都学习5个唯一的检测器。这是因为卷积层的权重在每个位置都相同,因此在网格单元之间共享。但模型为每个先验框学习一个检测器,它们在图像上滑动,以获得845个预测值,网格上每个位置有5个。因此,尽管我们总共只有5个独特的检测器,但由于卷积的缘故,这些检测器与它们在图像中的位置无关,因此无论它们位于何处,都可以检测到物体。每个位置的输入像素与检测器所学习到的权重,决定了该位置的最终边界框预测。
这也解释了为什么模型总是预测边界框相对于网格单元中心的位置。由于该模型具有卷积性质,无法预测绝对坐标,而卷积核在图像上滑动,因此它们的预测总是相对于在特征图中的当前位置。
为了使得网络的输出接近我们想要的结果,需要设计一个损失函数,将预测框与真实框进行比较。
5.2 损失函数的设计
需要将ground truth与检测器进行匹配,常用的匹配策略是:当检测器与ground truth的IOU大于一个阈值时,就给该检测器分配一个正类标签,类别为ground truth的物体类别。
一个ground truth可以分配给多个检测器,但是一个检测器只能与一个ground truth匹配(如果检测器与多个ground truth的IOU都超过了阈值,选IOU最大的那个ground truth进行匹配)。
与ground truth成功匹配的检测器为正例,与所有ground truth都没匹配上的检测器就是负例。
根据网络输出的三部分:分类概率、置信度、坐标,我们设计的损失函数也要能覆盖这三个方面,即在:分类错误、置信度太低、坐标偏差的时候,需要使用损失函数来惩罚。
(1)对于负例损失函数仅包含置信度,因为没有真实框,所以不需计算类别损失和坐标损失。我们希望负例的置信度尽可能低(置信度表示检测器是否认为有一个物体的中心在这个网格单元中),理想的置信度应该为0.
SSD没有置信度损失,因为它将背景作为一个特殊的类来处理。如果预测的是背景类,那个检测器被认为没有检测到物体。
(2)对于正例对于yolo,有三部分loss:
置信度loss+分类loss+坐标loss,其中坐标loss权重最大。(平方和误差(sum-squared error,SSE))对于SSD,有两部分loss:(MultiBoxLoss)
分类loss+坐标回归loss(分别使用的是softmax loss和Smooth L1 loss)5.3 使用优化器来训练
选择SGD对模型进行训练。
5.4 训练策略&技巧
- 难例挖掘(Hard negative mining) 。前面已经说过大部分检测器是不负责检测任何物体的。这意味着正例数量要远少于负例。YOLO采用一个超参数
来处理这种情况,但是SSD采用难例挖掘:它不是计算所有负例的损失,而是只计算那些预测结果最错的部分损失(即置信度较高的负例)。no_object_scale