论文链接:https://arxiv.org/abs/1512.02325
caffe代码链接:https://github.com/weiliu89/caffe/tree/ssd
tensorflow代码链接:https://github.com/xiaohu2015/SSD-Tensorflow
pytorch代码链接:https://github.com/amdegroot/ssd.pytorch
摘要
我们提出了一种one-stage的多框预测检测器,称为SSD,它是将边框的输出空间离散化成一组不同长宽比和尺寸的边界框。在预测期间,网络会对每一个边框的每一个类别都进行评分,并且会调整原始的边框来使其更好的匹配目标形状。另外,网络结合来自不同分辨率的多个feature map的预测来自然地处理不同尺寸的目标。SSD与two-stage的检测器相比十分简单,因为它完全消除了区域候选框的提取和随后的像素及特征采样过程,将所有的计算封装到一个网络中。这使得SSD容易训练,并且直接集成到需要检测组件的系统中。在PASCAL VOC、COCO和ILSVRC数据集上也证实了SSD算法与其他two-stage相比具有相当的精度,而且由于它的训练和推理都是一个统一的网络,所以速度更快。对于 300x300的输入,SSD在VOC2007测试集上以59FPS的速度在Nvidia TitanX实现了74.3%的mAP;对于 500x500 的输入,SSD实现了76.9%的mAP,获得了与Faster R-CNN相当的mAP。与其他的one-stage的模型相比,SSD算法甚至采用更小的输入图像尺寸但却获得了更高的精度。
1. Introduction
本文的共享主要在于:
- 介绍了SSD,这是一种针对多类别的one-stage的检测器,并且比之前的YOLO模型更快,更重要的是它在精度上大大提高了,准确的来讲,它的精度可以与最先进的two-stage的检测器(包括Faster R-CNN)相比;
- SSD的核心在于每一组bounding box都预测类别分数和边框偏移量,并且使用小卷积核应用到特征图上;
- 为例实现高的检测精度,我们根据不同尺度的特征图做不同尺度的预测,并且通过长宽比明确区分预测;
- 这些设计导致了简单的end-to-end的训练和高精度,甚至在低分辨率的输入图像上也是如此,它进一步的在速度和精度之间做了权衡;
- 在不同模型不同输入尺寸下,在不同数据集上做了时间和精度分析,与一系列最先进的方法进行了对比;
2. SSD
本节将会细致的介绍下SSD算法,2.1节介绍SSD算法框架,2.2节介绍与之相关的训练技术的细节。
先来区分一下default box以及feature map cell的概念:
- feature map cell就是尺寸为 8x8 或者 4x4 的特征图上的一个个格子;
- default box就是每个格子上对应的一系列不同尺寸和长宽比的边框,也即Figure 1中的虚线框;
2.1 模型
我们提出的SSD算法是基于前馈传播卷积神经网络,它会产生一系列边框的得分和偏移量,紧接着后面采用非极大值抑制完成最后的步骤,前面的层是基于在高质量图像分类的标准结构,我们将之称为基础网络,然后添加了额外的辅助网络结构在基础结构后面,在我们的结构中没有使用全连接层(这点和YOLO不同),直接用卷积层做检测。
利用多尺度特征图做检测:我们在截取的基础网络后面添加额外的卷积层,使得特征图尺寸逐渐减小,并且使用多尺度的default box做预测,使用lower layers(大的特征图)来检测小目标,upper layers(小的特征图)检测大目标。
使用卷积层直接做检测:额外添加的特征层可以使用一系列卷积核产生一系列检测结果的集合,网络结构如Figure 2中的SSD所示,采用VGG16做基础模型,首先VGG16是在ILSVRC CLS-LOC数据集上预训练。然后借鉴了DeepLab-LargeFOV,分别将VGG16的全连接层fc6和fc7转换成 3×3的卷积层 Conv6和 1×1的卷积层Conv7,同时将池化层pool5由原来的stride=2的 2×2 变成stride=1的 3×3,为了配合这种变化,采用了一种Atrous Algorithm,其实就是Conv6采用空洞卷积(Dilation Conv),其在不增加参数与模型复杂度的条件下指数级扩大卷积的视野,其使用扩张率(dilation rate)参数来表示扩张的大小,如下图所示,(a)是普通的 3×3卷积,其视野就是 3×3 ,(b)是扩张率为1,此时视野变成 7×7, (c) 扩张率为3时,视野扩大为 15×15 ,但是视野的特征更稀疏了。Conv6采用 3×3大小但dilation rate=6的扩展卷积。
那么我们为什么要用空洞卷积呢?直接使用pooling层不好吗?这就要从空洞卷积诞生的背景说起了,在图像分割领域,图像输入到CNN(典型的网络比如FCN[3])中,FCN先像传统的CNN那样对图像做卷积再pooling,降低图像尺寸的同时增大感受野,但是由于图像分割预测是pixel-wise的输出,所以要将pooling后较小的图像尺寸upsampling到原始的图像尺寸进行预测(upsampling一般采用deconv反卷积操作,deconv可参见知乎答案如何理解深度学习中的deconvolution networks?),之前的pooling操作使得每个pixel预测都能看到较大感受野信息。因此图像分割FCN中有两个关键,一个是pooling减小图像尺寸增大感受野,另一个是upsampling扩大图像尺寸。在先减小再增大尺寸的过程中,肯定有一些信息损失掉了,那么能不能设计一种新的操作,不通过pooling也能有较大的感受野看到更多的信息呢?答案就是dilated conv。dilated的好处是不做pooling损失信息的情况下,加大了感受野,让每个卷积输出都包含较大范围的信息。在图像需要全局信息或者语音文本需要较长的sequence信息依赖的问题中,都能很好的应用dilated conv,比如图像分割、语音合成WaveNet、机器翻译ByteNet中。
参考:https://www.zhihu.com/question/54149221/answer/192025860
然后移除dropout层和fc8层,并新增一系列卷积层,在检测数据集上做finetuing。其中VGG16中的Conv4_3层将作为用于检测的第一个特征图。conv4_3层特征图大小是 38×38,但是该层比较靠前,其norm较大,所以在其后面增加了一个L2 Normalization层,以保证和后面的检测层差异不是很大,这个和Batch Normalization层不太一样,其仅仅是对每个像素点在channel维度做归一化,而Batch Normalization层是在[batch_size, width, height]三个维度上做归一化。归一化后一般设置一个可训练的放缩变量gamma,使用TF可以这样简单实现:
# l2norm (not bacth norm, spatial normalization)
def l2norm(x, scale, trainable=True, scope="L2Normalization"):
n_channels = x.get_shape().as_list()[-1]
l2_norm = tf.nn.l2_normalize(x, [3], epsilon=1e-12)
with tf.variable_scope(scope):
gamma = tf.get_variable("gamma", shape=[n_channels, ], dtype=tf.float32,
initializer=tf.constant_initializer(scale),
trainable=trainable)
return l2_norm * gamma
我们是利用Conv4_3、Conv7、Conv8_2、Conv9_2、Conv10_2、Conv11_2层作为检测所用的特征图,共提取了6个特征图,其大小分别是 (38, 38), (19, 19), (10, 10), (5, 5), (3, 3), (1, 1) 。对于 m × n × p m\times n\times p m×n×p的特征图(p是通道数),我们使用 3 × 3 × p 3 \times 3 \times p 3×3×p维度的卷积核做卷积,对特征图上的 m × n m\times n m×n个位置上的每一个位置对应得4或者6个default box预测得到边框回归偏移量和类别得分。
不同尺度和宽高比的default boxes:SSD的预测值也与YOLO不太一样。对于每个单元格的每个default box,其都输出一套独立的预测值(包括每个default box的对于每个类的得分和偏移量)。关于各个类别的置信度或者评分,值得注意的是SSD将背景也当做了一个特殊的类别,如果检测目标共有 c 个类别,SSD其实需要预测 c+1 个置信度值,其中第一个置信度指的是不含目标或者属于背景的评分。后面当我们说 c 个类别置信度时,里面包含背景那个特殊的类别,即真实的检测类别只有 c-1 个。在预测过程中,置信度最高的那个类别就是边界框所属的类别,特别地,当第一个置信度值最高时,表示边界框中并不包含目标。第二部分就是边界框的location,这里需要注意的是输出值是相对与default box的偏移量(下面会具体介绍)。那么当某个要做检测的特征图中每个单元格对应k个default box时,需要的卷积核个数为(c+4)k,这将会在
m
×
n
m\times n
m×n的特征图上产生(c+4)kmn个输出值,分别对应每个default box的评分和偏移量。对于Figure 2中的结构,我们总共有
38
×
38
×
4
+
19
×
19
×
6
+
10
×
10
×
6
+
5
×
5
×
6
+
3
×
3
×
4
+
1
×
1
×
4
=
8732
38×38×4+19×19×6+10×10×6+5×5×6+3×3×4+1×1×4=8732
38×38×4+19×19×6+10×10×6+5×5×6+3×3×4+1×1×4=8732个default box。
2.2 训练
训练SSD和训练一个有区域候选的典型检测器之间的关键区别在于,真实的ground truth信息需要被分配到固定检测器输出集合中的特定输出。一旦这个分配确定了,损失函数和反向传播就可以端到端的应用了。训练也涉及到选择default boxes的长宽比和大小尺寸,以及hard negative mining和数据增广策略。
匹配策略——确定正负样本:在训练期间,我们需要确定真实的ground truth与哪些default boxes相匹配,换句话来讲,与某个ground truth匹配的default box将负责预测这个目标。在YOLO中,ground truth的中心落在哪个单个格,就由该单元格内的与该ground truth的IOU最大的边界框来预测它(即每个网格中只有一个bounding box对该目标负责);但在SSD中不同,它的匹配原则有两点:首先,对于图片中的每个ground truth,找到与其IOU最大的default box,该default box与ground truth匹配,这样可以保证对于每个ground truth至少有一个default box与其匹配。通常称与ground truth匹配的default box为正样本,反之,若一个default box没有与任何ground truth进行匹配,那么该default box只能与背景匹配,就是负样本。一个图片中的ground truth是非常少的,而default box却很多,所以如果仅按照一个原则匹配,很多default box会是负样本,这将会导致正负样本及其不平衡,所以需要第二个原则。第二个原则是:对于剩余的未匹配的default box,如果某个ground box与其的IOU大于一个阈值(通常设置为0.5),那么该default box也与这个ground box匹配。这意味着某个ground box可以与多个default box匹配,但是反过来却不行,因为一个default box最多只能匹配一个ground truth,如果出现多个ground truth与某个default box的IOU大于阈值,那么这个default box只与IOU最大的那个ground truth进行匹配。第二个原则一定在第一个原则之后,仔细考虑一下这种情况,如果某个ground truth所对应的最大IOU小于阈值,并且所匹配的default box与另外一个ground truth的IOU大于阈值,那么这个default box应该与哪个ground box相匹配呢?答案是第一个,首先要确保某个ground truth一定有一个default box与其匹配。但是这种情况几乎不存在,因为default box很多,某个ground box的最大IOU肯定大于阈值,所以可能只实施第二个原则就好了,这里的Tensorflow版本就是只实施了第二个原则,PyTorch两个原则都实现了。下图为一个匹配示意图,其中绿色的GT是ground truth,红色为default box,FP是正样本,FP为负样本。
训练目标函数:SSD的训练目标函数来自MultiBox的目标函数,但是进行了扩展来处理多目标类别。
x
i
j
p
=
{
1
,
0
}
x_{ij}^{p}=\left \{ 1,0 \right \}
xijp={1,0}为一个指示参数,
x
i
j
p
=
1
x_{ij}^{p}=1
xijp=1代表第i个default box和第j个ground box相匹配,并且这个ground truth的类别为p,基于上面的匹配原则,我们可以知道
∑
i
x
i
j
p
⩾
1
\sum _{i}x_{ij}^{p}\geqslant 1
∑ixijp⩾1,故目标函数可以写成定位损失(loc)和置信度损失(conf)两部分的加权和:
N代表正样本的数量,如果N=0,我们将loss设置为0。定位损失为predicted box (l) 和ground box(g)之间的Smooth L1 loss,这里需要注意的是,我们的网络输出g和l都是偏移量。
由上面的式子可以发现,位置误差是仅针对正样本计算的,l就是网络的四个输出偏移量,
g
^
i
m
\hat{g}_{i}^{m}
g^im(也即我们定位损失中的真实标签)它是通过(2)式下面的四个公式得到的,这个地方参考了最早的R-CNN做边框回归的原理,我们default box位置用
d
=
(
d
c
x
,
d
c
y
,
d
w
,
d
h
)
d=(d^{cx},d^{cy},d^{w},d^{h})
d=(dcx,dcy,dw,dh)表示,其ground truth的位置用
g
=
(
g
c
x
,
g
c
y
,
g
w
,
g
h
)
g=(g^{cx},g^{cy},g^{w},g^{h})
g=(gcx,gcy,gw,gh)表示,由此就可以得到训练中位置偏移量的标签,我们通常将这个过程称为边界框的编码(encode),在推理阶段我们能得到的是相对于default box的偏移量l,需要将其解码(decode),从预测值l中得到边界框的真实位置
g
p
r
e
d
i
c
t
g_{predict}
gpredict:
g
p
r
e
d
i
c
t
c
x
=
d
w
∗
l
c
x
+
d
c
x
g_{predict}^{cx}=d^{w}*l^{cx}+d^{cx}
gpredictcx=dw∗lcx+dcx
g
p
r
e
d
i
c
t
c
y
=
d
h
∗
l
c
y
+
d
c
y
g_{predict}^{cy}=d^{h}*l^{cy}+d^{cy}
gpredictcy=dh∗lcy+dcy
g
p
r
e
d
i
c
t
w
=
d
w
e
x
p
(
l
w
)
g_{predict}^{w}=d^{w}exp(l^{w})
gpredictw=dwexp(lw)
g
p
r
e
d
i
c
t
h
=
d
h
e
x
p
(
l
h
)
g_{predict}^{h}=d^{h}exp(l^{h})
gpredicth=dhexp(lh)
然而,在SSD的源码实现中这里还有trick,那就是设置Variance超参数来调整检测值,通过bool参数variance_encoded_in_target来控制两种模式,当其为True时,表示variance被包含在预测值中,(2)式中下面四个转换公式就变为:
g
^
j
c
x
=
(
g
j
c
x
−
d
i
c
x
)
/
d
i
w
/
v
a
r
i
a
n
c
e
[
0
]
\hat{g}_{j}^{cx}=(g_{j}^{cx}-d_{i}^{cx})/d_{i}^{w}/variance[0]
g^jcx=(gjcx−dicx)/diw/variance[0]
g
^
j
c
y
=
(
g
j
c
y
−
d
i
c
y
)
/
d
i
h
/
v
a
r
i
a
n
c
e
[
1
]
\hat{g}_{j}^{cy}=(g_{j}^{cy}-d_{i}^{cy})/d_{i}^{h}/variance[1]
g^jcy=(gjcy−dicy)/dih/variance[1]
g
^
j
w
=
l
o
g
(
g
j
w
d
i
w
)
/
v
a
r
i
a
n
c
e
[
2
]
\hat{g}_{j}^{w}=log(\frac{g_{j}^{w}}{d_{i}^{w}})/variance[2]
g^jw=log(diwgjw)/variance[2]
g
^
j
h
=
l
o
g
(
g
j
h
d
i
h
)
/
v
a
r
i
a
n
c
e
[
3
]
\hat{g}_{j}^{h}=log(\frac{g_{j}^{h}}{d_{i}^{h}})/variance[3]
g^jh=log(dihgjh)/variance[3]
其中variance[0]、variance[1]、variance[2]、variance[3]这四个超参数是自己设置的,增强这个超参数的目的可能是需要对中心坐标的loss和长宽的loss做一个权衡,相应的预测阶段对边框位置的解码就有了一些变化。
置信度误差是多分类上的softmax loss:
这里的
α
\alpha
α经过交叉验证选取1。
选择default boxes的长宽比和大小尺寸:为了处理不同尺度的目标,一些文章,如 ICLR 2014, Overfeat: Integrated recognition, localization and detection using convolutional networks,还有 ECCV 2014, Spatial pyramid pooling in deep convolutional networks for visual recognition,他们将图像转换成不同的尺度,将这些图像独立的通过 CNN 网络处理,再将这些不同尺度的图像结果进行综合。但是其实,如果使用同一个网络中不同层上的 feature maps,也可以达到相同的效果,同时在所有物体尺度中共享参数。之前的工作,如 CVPR 2015, Fully convolutional networks for semantic segmentation,还有 CVPR 2015, Hypercolumns for object segmentation and fine-grained localization 就用了 CNN 前面的 layers,来提高图像分割的效果,因为越底层的 layers,保留的图像细节越多。文章 ICLR 2016, ParseNet: Looking wider to see better 也证明了以上的想法是可行的。因此,本文同时使用 lower feature maps、upper feature maps 来 predict detections。Figure 1中展示了本文中使用的两种不同尺度的 feature map——8×8的feature map,以及 4×4 的 feature map。
一个网络中不同层级的feature maps有不同的感受野,这里的感受野,指的是输出的 feature map 上的一个节点,其对应输入图像上尺寸的大小。在SSD 结构中,default boxes 不必与每一层 layer 的 receptive fields 对应。本文的设计中,feature map 中特定的位置,来负责图像中特定的区域,以及物体特定的尺寸。假如我们用 m 个 feature maps 来做 predictions,每一个 feature map 中 default box 的尺寸大小计算如下:
s
k
s_{k}
sk是default box相比于图片的比例,而
s
m
i
n
=
0.2
,
s
m
a
x
=
0.9
s_{min}=0.2,s_{max}=0.9
smin=0.2,smax=0.9,分别代表比例的最小值和最大值;参考了源码中的处理,我们来仔细说明 一下这块的取值,从上面的网络结构中,我们是利用Conv7,Conv8_2,Conv9_2,Conv10_2,Conv11_2作为检测所用的特征图,加上Conv4_3层,共提取了6个特征图,其大小分别是 (38, 38), (19, 19), (10, 10), (5, 5), (3, 3), (1, 1) ,但是每层特征图所取得尺度和长宽比是不同得,对于Conc4_3这层 特征图,我们一般是单独设置得,取尺度比例
s
=
s
m
i
n
/
2
=
0.1
s=s_{min}/2=0.1
s=smin/2=0.1,那么尺度为
300
×
0.1
=
30
300 \times 0.1=30
300×0.1=30(这里默认输入图片尺寸为
300
×
300
300 \times 300
300×300);对于后面几层尺度比例的取值,我们采用上面(4)式的计算方法,m=5,Conv7层中的尺度比例为
s
1
=
s
m
i
n
=
0.2
s_{1}=s_{min}=0.2
s1=smin=0.2,为了计算方便,我们在计算增加步长时先乘以100得到
s
m
a
x
−
s
m
i
n
m
−
1
(
k
−
1
)
=
⌊
⌊
0.9
∗
100
⌋
−
⌊
0.2
∗
100
⌋
5
−
1
⌋
)
=
17
\frac{s_{max}-s_{min}}{m-1}(k-1)=\left \lfloor \frac{\left \lfloor 0.9*100 \right \rfloor-\left \lfloor 0.2*100 \right \rfloor}{5-1} \right \rfloor)=17
m−1smax−smin(k−1)=⌊5−1⌊0.9∗100⌋−⌊0.2∗100⌋⌋)=17,那么实际上的尺度比例增加步长应该为0.17,由此得到Conv8_2,Conv9_2,Conv10_2,Conv11_2层的尺度比例分别为0.37、0.54、0.71、0.88,对应的尺度为60、111、162、213、264;再使用尺度比例乘以图片尺寸得到Conv4_3、Conv7、Conv8_2、Conv9_2、Conv10_2、Conv11_2的default box的尺度为30、60、111、162、213、264。对于长宽比,我们一般取
a
r
=
{
1
,
2
,
3
,
1
2
,
1
3
}
a^{r}=\left \{ 1,2,3,\frac{1}{2},\frac{1}{3} \right \}
ar={1,2,3,21,31},相对应的计算特定长宽比下的default box的宽度和高度的尺度比例公式为:
w
k
a
=
s
k
a
r
,
h
k
a
=
s
k
/
a
r
w_{k}^{a}=s_{k}\sqrt{a_{r}},h_{k}^{a}=s_{k}/\sqrt{a_{r}}
wka=skar,hka=sk/ar
默认情况下,每层特征图会有一个 a r = 1 a_{r}=1 ar=1且尺度比例为 s k s_{k} sk的default box,除此之外,还会设置一个尺度为 s k ′ = s k s k + 1 且 a r = 1 s_{k}^{'}=\sqrt {s_{k}s_{k+1}}且a_{r}=1 sk′=sksk+1且ar=1的default box,这样每个特征图都设置了两个长宽比为1但大小不同的正方形状的default box,需要注意的是对于Conv11_2层没有对应的 s m + 1 s_{m+1} sm+1,这时我们选择虚拟一个 s m + 1 = 300 × 105 / 100 = 315 s_{m+1}=300\times 105 / 100=315 sm+1=300×105/100=315来计算 s m ′ s_{m}^{'} sm′,因此,每个特征图中的每格一共有6种default box,但是在实现时,Conv4_3、Conv10_2、Conv11_2仅使用了4中default box,它们不使用长宽比为3和1/3的default box。每个default box的中心点分布在每格的中心,即 ( i + 0.5 ∣ f k ∣ , j + 0.5 ∣ f k ∣ ) , i , j ∈ [ 0 , ∣ f k ∣ ] (\frac{i+0.5}{|f_{k}|},\frac{j+0.5}{|f_{k}|}),i,j \in[0,|f_{k}|] (∣fk∣i+0.5,∣fk∣j+0.5),i,j∈[0,∣fk∣], f k f_{k} fk为特征图的大小。
通过组合具有不同比例和宽高比、feature map上不同位置的所有default box的预测,我们有了各种各样的预测结果,涵盖各种检测目标 的大小和形状。如下图,狗的 ground truth与 4×4 feature map 中的红色bounding box 吻合,所以其余的 boxes 都看作负样本。
Hard negative mining:尽管匹配原则2使得正样本的数量相对增加了,但是大多数的default box还是负样本,正负样本的比例仍然相差很大,在这里我们在正负样本中引入一个失调,我们在计算损失函数时并不使用所有的负样本,而是根据每个default box引入置信度损失大小进行排序,然后选择最高的一部分负样本,这使得正负样本的比例一般维持在1:3,这将导致收敛快且稳定。
数据增广:采用数据增广(Data Augmentation)可以提升SSD的性能,主要采用的技术有水平翻转(horizontal flip),随机裁剪加颜色扭曲(random crop & color distortion),随机采集块域(Randomly sample a patch)(获取小目标训练样本),如下图所示:
3. 实验结果
训练超参数设置:
- 初始学习率为 1 0 − 3 10^{-3} 10−3,momentum为0.9,权重衰减率为0.0005;
- 使用的SGD算法;
- batch size为32;
3.1 PASCAL VOC2017
数据集增加可以显著提升mAP:
3.2 模型分析
SSD中不同的设计和组件选择对结果的影响:
为了更细节的了解本文的两个 SSD model,我们使用了 ECCV 2012, Diagnosing error in object detectors 的检测分析工具。下图 Figure 3 显示了 SSD 可以高质量的检测不同种类的物体。
下图 Figure 4 展示了 SSD 模型对 bounding box 的 size 非常的敏感。也就是说,SSD 对小物体目标较为敏感,在检测小物体目标上表现较差。其实这也算情理之中,因为对于小目标而言,经过多层卷积之后,就没剩多少信息了。虽然提高输入图像的 size 可以提高对小目标的检测效果,但是对于小目标检测问题,还是有很多提升空间的。另外增加数据集对于小的object的检测也有帮助,原因在于随机裁剪后的图像相当于“放大”原图像,所以这样的裁剪操作不仅增加了图像数量,也放大了图像。
不同分辨率的多层融合是有好处的:
参考文章:
https://zhuanlan.zhihu.com/p/33544892
https://blog.csdn.net/u014380165/article/details/72824889
https://blog.csdn.net/u010167269/article/details/52563573
https://zhuanlan.zhihu.com/p/38013662