00.Anchor的概念

即SSD算法中的Default Box

在基于anchor的目标检测网络中,一个至关重要的步骤就是科学的设置anchor,可以说Anchor设置的合理与否,极大的影响着最终模型检测性能的好坏。本文作者以通俗易懂的语言介绍Anchor是什么,以及如何科学的设置anchor。

对于目标检测新手来说,一个比较常见的误区就是拿到模型,直接无修改的在自己的数据集上训练,最终可能导致模型虽然有一定效果,但是mAP指标可能低于别人的十几个点,这里很有可能就是anchor设置的不合理。笔者记得在之前面试应届生的时候,也有不少学生不能清晰的表达anchor是什么,以及怎么科学的设置。笔者开源人脸口罩检测模型和数据后,也有不少同行和学生在自己的网络上训练,通过与他们的交流,我发现很多人都遇到了不知道anchor怎么设置,或者设置不科学的问题。

今天,我们讲解一下anchor,因为这篇文章面向新手,所以笔者争取用质朴(讲人话)的语言进行简单的介绍,同时,我们也将相关代码上传到了Github,大家可以在自己的数据集上可视化数据分布。

在介绍Anchor之前,我们先介绍一下传统的人脸识别算法,是怎么检测出图片中的人脸的。以下图为例,如果我们要检测图中小女孩的人脸位置,一个比较简单暴力的方法就是滑窗,我们使用不同大小、不同长宽比的候选框在整幅图像上进行穷尽式的滑窗,然后提取窗口内的特征(例如Haar、LBP、Hog等特征),再送入分类器(SVM、Adaboost等)判断该窗口内包含的是否为人脸。这种方法简单易理解,但是这类方法受限于手动设计的特征,召回率和准确率通常不是很高。

43_14788_1cc84441b86226b (1).png

在深度学习时代,大名鼎鼎的RCNN和Fast RCNN依旧依赖滑窗来产生候选框,也就是Selective Search算法,该算法优化了候选框的生成策略,但仍旧会产生大量的候选框,导致即使Fast RCNN算法,在GPU上的速度也只有三、四帧每秒。直到Faster RCNN的出现,提出了RPN网络,使用RPN直接预测出候选框的位置。RPN网络一个最重要的概念就是anchor,启发了后面的SSD和YOLOv2等算法,虽然SSD算法称之为default box,也有算法叫做prior box,其实都是同一个概念,他们都是anchor的别称。

01.Anchor的基本概括

那么,anchor到底是什么呢?如果我们用一句话概括——就是在图像上预设好的不同大小,不同长宽比的参照框。(其实非常类似于上面的滑窗法所设置的窗口大小)

下图来自《动手学深度学习》中的例子,假设一个256x256大小的图片,经过64、128和256倍下采样,会产生4x4、2x2、1x1大小的特征图,我们在这三个特征图上每个点上都设置三个不同大小的anchor。当然,这只是一个例子,实际的SSD模型,在300x300的输入下,anchor数量也特别多,其在38x38、19x19、10x10、5x5、3x3、1x1的六个特征图上,每个点分别设置4、6、6、6、6、4个不同大小和长宽比的anchor,所以一共有38x38x4+ 19x19x6+ 10x10x6+ 5x5x6+ 3x3x4+ 1x1x4= 8732个anchor。

43_14788_b31216a83a1608f.png

借助神经网络强大的拟合能力,我们不再需要计算Haar、Hog等特征,直接让神经网络输出,每个anchor是否包含(或者说与物体有较大重叠,也就是IoU较大)物体,以及被检测物体相对本anchor的中心点偏移以及长宽比例。 以下图为例:

43_14788_5b096840c2d45a1.png

一般的目标检测网络可能有成千上万个anchor,例如标准SSD在300x300输入下有8732个anchor,在500x500下anchor数量过万。我们拿上图中的三个anchor举例,神经网络的输出,也就是每个anchor认为自己是否含有物体的概率,物体中心点与anchor自身的中心点位置的偏移量,以及相对于anchor宽高的比例。因为anchor的位置都是固定的,所以就可以很容易的换算出来实际物体的位置。以图中的小猫为例,红色的anchor就以99%的概率认为它是一只猫,并同时给出了猫的实际位置相对于该anchor的偏移量,这样,我们将输出解码后就得到了实际猫的位置,如果它能通过NMS(非最大抑制)筛选,它就能顺利的输出来。但是,绿色的anchor就认为它是猫的概率就很小,紫色的anchor虽然与猫有重叠,但是概率只有26%。

其实SSD的推理很简单,就是根据anchor进行位置解码,然后进行NMS过程,就完成了(更详细的推理介绍,请查看我们这篇文章如何在浏览器运行深度神经网络?以人脸口罩识别为例进行讲解)。在训练的时候,也就是给每张图片的物体的Bounding Box,相对于anchor进行编码,如果物体的Bounding Box与某个anchor的IoU较大,例如大于0.5就认为是正样本,否则是负样本(当然,也有算法将大于0.7的设为正样本,小于0.3的算负样本,中间的不计算损失)。

以SSD作者给出的示例图为例,图中有一只猫和一只狗,这只猫在8x8的特征图上所设置anchor中,有两个(蓝色部分)与猫的IoU较大,可以认为是正样本,而对于狗,在4x4的特征图上的设置的anchor,有一个(红色部分)与狗的IoU较大,可以认为是正样本。其他的,都算作负样本。在实际中,因为anchor非常密集,所以SSD算法中,会有多个anchor与物体的IoU大于阈值,所以可能多个anchor都是对应同一个物体的正样本(例如这只猫,就可能有不止2个匹配的正样本)。

43_14788_c7fd3125e75f4ba.png

看到这里大家应该比较明白了,在训练的时候,需要anchor的大小和长宽比与待检测的物体尺度基本一致,才可能让anchor与物体的IoU大于阈值,成为正样本,否则,可能anchor为正样本的数目特别少,就会导致漏检很多。

43_14788_698f2d9ac4a97b7.png

我们举个例子,如果您要检测道路两边的电线杆,电线杆的宽高比可能不止1:10,而且特别细。如果您设置的anchor长宽比为1:1、1:2、 2:1、 1:3、 3:1这五个不同的长宽比,那可能到导致在训练的时候,没有哪个anchor与电线杆的IoU大于0.5,导致全部为负样本,那么,这样的anchor设置,模型怎么可能检测出来电线杆呢?(虽然我们在实现SSD算法的时候,即使某个物体与所有anchor的IoU都不大于0.5的阈值,也会可怜可怜它,给它强行分配一个IoU最大的anchor,即使IoU只有0.3, 但是这样,每个物体只能分配一个,而且宽高偏移量还比较大,导致回归不准)。

到这里,大家应该知道了,对于目标检测,anchor合理设置大小和宽高比,可以说非常重要。那么,如何科学的设置anchor呢?

10.科学的设置Anchor

在FasterRCNN的RPN网络部分,anchor为三个尺度{128, 256, 512},三个比例{1:1, 1:2, 2:1},所以一共9组anchor。在SSD论文中,作者使用6组定位层,每个定位层分别有6个anchor(不过第一和最后一个定位层只有4个)。一个尺度,分别有1:1、1:2、2:1、1:3、3:1五个不同宽高比,再加一个后面特征图的anchor尺度与该特征图的尺度相乘再开根号。同样是1:1比例,所以一共5+1=6组anchor。

关于anchor的宽度,是尺度(scale,简写为s)乘以长宽比(aspect ratio,简写为ar)的根方,而高度,则是除以ar的根方。在SSD中,作者提到了anchor尺度大小(scale)的计算方法,也就是从最小的0.2,到最大的0.9,中间四个定位层的大小是等间隔采样得到的。

但是,但是,大家查看SSD开源的代码,作者给出的得anchor大小并不是这么计算得到的。而是30、60、111、162、213、264(再加一个315)这7个尺度。我想,这应该是作者根据数据集的物体框大小的分布而设置的。因为上面我们介绍了,anchor只有跟你要检测的物体的大小和长宽比更贴近,才能让模型的效果更好。

YOLOv3在三个不同尺度,每个尺度三个不同大小的anchor,一共九组。这位退出CV圈的Joseph Redmon大神则是在YOLOv2版本开始使用kmeans方法聚类得到合适的anchor。可见,三大框架的作者,在实际的公开数据集上,都是根据数据的实际分布来设置的,所以,我们在自己的数据集上训练目标检测网络时,一定!不要!拿到开源代码就是一顿跑,拿起键盘就是干。

Last modification:April 12th, 2021 at 08:07 am
If you think my article is useful to you, please feel free to appreciate