整理别人的分享。。。
参考原文链接
参考链接:https://blog.csdn.net/bigFatCat_Tom/article/details/90729173
https://blog.csdn.net/wweiainn/article/details/82831701
前言
目标检测主流算法主要分为两个类型:
(1)two-stage方法,如R-CNN系列,主要思路是先通过启发式方法(selective search)或者CNN网络(RPN)产生一系列稀疏的候选框,然后对这些候选框进行分类与回归,two-stage方法的优势是准确度高。
(2)one-stage方法,如Yolo和SSD,主要思路是均匀地在图片的不同位置进行密集抽样,抽样时可以采用不同尺度和长宽比,然后利用CNN提取特征后直接进行分类与回归,整个过程只需要一步,优势是速度快,但是均匀的密集采样的一个重要缺点是训练困难,这主要是因为正样本与负样本(背景)极其不均衡 (Focal loss),导致模型准确度稍低。不同算法性能如图1所示,可以看到两类方法在准确度和速度上的差异。
图1 不同检测算法的性能对比
SSD算法,全名Single Shot MultiBox Detector,单发多框检测。图1可以看到,SSD算法在准确度和速度(除了SSD512)上都比Yolo要好很多。
图2给出了不同算法的基本框架,相比Faster R-CNN,其先通过CNN得到候选框,然后再进行分类与回归,而Yolo与SSD可以一步到位完成检测。SSD相比Yolo不同点:
SSD采用CNN来直接进行检测,而不是像Yolo那样在全连接层之后做检测;
SSD提取了不同尺度的特征图来做检测,大尺度特征图(较靠前的特征图)可以用来检测小物体,而小尺度特征图(较靠后的特征图)用来检测大物体;
SSD采用了不同尺度和长宽比的先验框(Prior boxes, Default boxes,在Faster R-CNN中叫做锚,Anchors)。
Yolo算法缺点是难以检测小目标,而且定位不准,但是这几点重要改进使得SSD在一定程度上克服这些缺点。
图2 不同算法的基本框架图
设计理念
SSD和Yolo一样是采用一个CNN网络来进行检测,但却采用多尺度的特征图,其基本架构如图3所示。下面将SSD核心设计理念总结为以下三点:
图3 SSD基本框架图
(1)采用多尺度特征图用于检测
所谓多尺度,是采用大小不同的特征图,CNN网络一般靠近输入的层特征图比较大,后面逐渐采用stride=2的卷积或者pool来降低特征图大小,如图3所示,一个比较大的特征图和一个比较小的特征图,都用来做检测。这样做的好处是大的特征图可以用来检测相对较小的目标,而小的特征图则负责检测大目标。如图4所示,8x8的特征图可以划分更多单元,但每个单元的先验框尺度较小。
图4 不同尺度的特征图
(2)采用卷积进行检测
与Yolo最后采用全连接层不同,SSD直接采用卷积对不同的特征图来进行提取检测结果。对于形状为
m
∗
n
∗
p
m*n*p
m∗n∗p的特征图,只需要采用
3
∗
3
∗
p
3*3*p
3∗3∗p这样比较小的卷积核得到检测值。
(3)设置先验框
在Yolo中,每个单元预测多个边界框,但都是相对这个单元本身(正方块),但真实目标的形状是多变的,Yolo需要在训练过程中自适应目标的形状。而SSD借鉴了Faster R-CNN中anchor的理念,每个单元设置尺度或者长宽比不同的先验框,预测的边界框(bounding boxes)是以这些先验框为基准的,在一定程度上减少训练难度。一般每个单元会设置多个先验框,其尺度和长宽比存在差异,如图5所示,可以看到每个单元使用了4个不同的先验框,图片中猫和狗分别采用最适合它们形状的先验框来进行训练。
图5 SSD的先验框
SSD的检测值也与Yolo不太一样。对每个单元的每个先验框,都输出一套独立的检测值,对一个边界框,输出主要分为两个部分。
第一部分是各个类别的置信度(评分),SSD将背景也当做一个类别,如检测目标共有 c c c个类别,SSD需要预测 c + 1 c+1 c+1个置信度值,其中第一个置信度指的是不含目标(属于背景)的评分。
预测过程中,置信度最高的那个类别就是边界框所属的类别,特别地,当第一个置信度值最高时,表示边界框中并不包含目标(背景类别)。
第二部分就是边界框的location,包含4个值 ( c x , c y , w , h ) (cx,cy,w,h) (cx,cy,w,h),表示边界框的中心坐标、宽高。
注意真实预测值是边界框相对于先验框的转换值。先验框位置用 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)表示,其对应边界框用 b = ( b c x , b c y , b w , b h ) b=(b^{cx}, b^{cy}, b^w, b^h) b=(bcx,bcy,bw,bh)表示,那么边界框的预测值 l l l其实是 b b b相对于 d d d的转换值:
l c x = ( b c x − d c x ) / d w , l c y = ( b c y − d c y ) / d h l^{cx} = (b^{cx} - d^{cx})/d^w, \space l^{cy} = (b^{cy} - d^{cy})/d^h lcx=(bcx−dcx)/dw, lcy=(bcy−dcy)/dh
l w = log ( b w / d w ) , l h = log ( b h / d h ) l^{w} = \log(b^{w}/d^w), \space l^{h} = \log(b^{h}/d^h) lw=log(bw/dw), lh=log(bh/dh)
习惯上,称上面这个过程为边界框的编码(encode),预测时,需要反向这个过程,即进行解码(decode),从预测值 l l l中得到边界框的真实位置 b b b :
b c x = d w l c x + d c x , b c y = d y l c y + d c y b^{cx}=d^w l^{cx} + d^{cx}, \space b^{cy}=d^y l^{cy} + d^{cy} bcx=dwlcx+dcx, bcy=dylcy+dcy
b w = d w exp ( l w ) , b h = d h exp ( l h ) b^{w}=d^w \exp(l^{w}), \space b^{h}=d^h \exp(l^{h}) bw=dwexp(lw), bh=dhexp(lh)
然而,在SSD的Caffe源码实现中有设置variance超参数来调整检测值,通过bool参数variance_encoded_in_target来控制两种模式,当其为True时,表示variance(变化幅度,差额)被包含在预测值中,就是上面那种情况。但是如果是False(大部分采用这种方式,训练更容易?),就需要手动设置超参数variance,用来对 l l l 的4个值进行放缩,此时边界框需要这样解码:
b c x = d w ( v a r i a n c e [ 0 ] ∗ l c x ) + d c x , b c y = d y ( v a r i a n c e [ 1 ] ∗ l c y ) + d c y b^{cx}=d^w (variance[0]*l^{cx}) + d^{cx}, \space b^{cy}=d^y (variance[1]*l^{cy}) + d^{cy} bcx=dw(variance[0]∗lcx)+dcx, bcy=dy(variance[1]∗lcy)+dcy
b w = d w exp ( v a r i a n c e [ 2 ] ∗ l w ) , b h = d h exp ( v a r i a n c e [ 3 ] ∗ l h ) b^{w}=d^w \exp(variance[2]*l^{w}), \space b^{h}=d^h \exp(variance[3]*l^{h}) bw=dwexp(variance[2]∗lw), bh=dhexp(variance[3]∗lh)
综上所述,对于一个大小 m × n m\times n m×n 的特征图,共有 m n mn mn个单元,每个单元设置的先验框数目记为 k k k,那么每个单元共需要 ( c + 4 ) k (c+4)k (c+4)k 个预测值( c c c表示类别(包含背景),4表示4个位置参数),所有的单元共需要 ( c + 4 ) k m n (c+4)kmn (c+4)kmn 个预测值。由于SSD采用卷积做检测,所以就需要 ( c + 4 ) k (c+4)k (c+4)k 个卷积核完成这个特征图的检测过程。
网络结构
SSD采用VGG16作为基础网络,然后在VGG16的基础上新增了卷积层来获得更多的特征图以用于检测。SSD的网络结构如图6所示(论文图片)。上面是SSD,下面是Yolo,可以看到SSD利用了多尺度的特征图做检测。模型的输入图片大小是 300 × 300 300\times300 300×300 (还可以是 512 × 512 512\times512 512×512 ,其与前者网络结构没有差别,只是最后新增一个卷积层)。
图6 SSD的网络结构
首先VGG16是在ILSVRC CLS-LOC数据集预训练。然后借鉴了DeepLab-LargeFOV,分别将VGG16的全连接层fc6和fc7转换成 3 × 3 3\times3 3×3 卷积层 conv6和 1 × 1 1\times1 1×1 卷积层conv7,同时将池化层pool5由原来的stride=2的 2 × 2 2\times 2 2×2 变成stride=1的 3 × 3 3\times 3 3×3 =(猜想是不想reduce特征图大小)。为了配合这种变化,采用了一种Atrous Algorithm(下面会说)。
Conv6采用 3 × 3 3\times3 3×3 大小但dilation rate=6的扩展卷积。
关于Atrous Algorithm,其实就是conv6采用扩展卷积或带孔卷积,在不增加参数与模型复杂度的条件下指数级扩大卷积的视野,其使用扩张率(dilation rate)参数,来表示扩张的大小。如下图6所示,(a)是普通的 3 × 3 3\times3 3×3卷积,其视野就是 3 × 3 3\times3 3×3 ,(b)是扩张率为2,此时视野变成 7 × 7 7\times7 7×7(对于一个7x7的图像区域,只有9个红色的点和3x3的kernel发生卷积操作,其余的点不加入计算) ,©扩张率为4时,视野扩大为 15 × 15 15\times15 15×15 ,但是视野的特征更稀疏了。
图7 扩展卷积
然后移除dropout层和fc8层,并新增一系列卷积层,在检测数据集上做finetuing。
其中VGG16中的Conv4_3层将作为用于检测的第一个特征图。conv4_3层特征图大小是 38 × 38 38\times38 38×38 ,但该层比较靠前,其norm较大,所以在其后增加一个L2 Normalization层,保证和后面的检测层差异不是很大,这和Batch Normalization层不太一样,它仅仅是对每个像素点在channle维度做归一化,而Batch Normalization层是在[batch_size, width, height]三个维度上做归一化。归一化后一般设置一个可训练的放缩变量gamma。
从后面新增的卷积层中提取Conv7,Conv8_2,Conv9_2,Conv10_2,Conv11_2作为检测所用的特征图,加上Conv4_3层,共提取6个特征图,大小分别是 ( 38 , 38 ) , ( 19 , 19 ) , ( 10 , 10 ) , ( 5 , 5 ) , ( 3 , 3 ) , ( 1 , 1 ) (38, 38), (19, 19), (10, 10), (5, 5), (3, 3), (1, 1) (38,38),(19,19),(10,10),(5,5),(3,3),(1,1) 。注意,不同特征图设置的先验框数目不同(见图,有的为4有的为6)。先验框的设置,包括尺度和长宽比两个方面。先验框的尺度遵守一个线性递增规则:随着特征图大小降低,先验框尺度线性增加:
s k = s m i n + s m a x − s m i n m − 1 ( k − 1 ) , k ∈ [ 1 , m ] s_k = s_{min} + \frac{s_{max} - s_{min}}{m-1}(k-1), k\in[1,m] sk=smin+m−1smax−smin(k−1),k∈[1,m]
其中 m m m 指的特征图个数,但却是 5 5 5 ,因为第一层(Conv4_3层)是单独设置的, s k s_k sk表示先验框大小相对于图片的比例,而 s m i n s_{min} smin和 s m a x s_{max} smax表示比例的最小值与最大值,paper里面取0.2和0.9。对于第一个特征图(单独设置),其先验框的尺度比例一般设置为 s m i n / 2 = 0.1 s_{min}/2=0.1 smin/2=0.1 ,那么尺度为 300 × 0.1 = 30 300\times 0.1=30 300×0.1=30 。对于后面的特征图,先验框尺度按照上面公式线性增加,但是先将尺度比例先扩大100倍,此时增长步长为 ⌊ ⌊ s m a x × 100 ⌋ − ⌊ s m i n × 100 ⌋ m − 1 ⌋ = 17 \lfloor \frac{\lfloor s_{max}\times 100\rfloor - \lfloor s_{min}\times 100\rfloor}{m-1}\rfloor=17 ⌊m−1⌊smax×100⌋−⌊smin×100⌋⌋=17 ,这样各个特征图的 s k s_k sk为 20 , 37 , 54 , 71 , 88 20, 37, 54, 71, 88 20,37,54,71,88,将这些比例除以100,然后再乘以图片大小(此处为300*300),可以得到各个特征图的尺度为 60 , 111 , 162 , 213 , 264 60,111, 162,213,264 60,111,162,213,264。综上,可以得到各个特征图的先验框尺度 30 , 60 , 111 , 162 , 213 , 264 30,60,111, 162,213,264 30,60,111,162,213,264。对于长宽比,一般选取 a r ∈ { 1 , 2 , 3 , 1 2 , 1 3 } a_r\in \{1,2,3,\frac{1}{2},\frac{1}{3}\} ar∈{1,2,3,21,31},对于特定的长宽比,按如下公式计算先验框的宽度与高度(后面的 s k s_k sk均指的是先验框实际尺度,而不是尺度比例):
w k a = s k a r , h k a = s k / a r w^a_{k}=s_k\sqrt{a_r},\space h^a_{k}=s_k/\sqrt{a_r} wka=skar, hka=sk/ar
默认情况下,每个特征图会有一个 a r = 1 a_r=1 ar=1 且尺度为 s k s_k sk的先验框,除此之外,还会设置一个尺度为 s k ′ = s k s k + 1 s'_{k}=\sqrt{s_k s_{k+1}} sk′=sksk+1 且 a r = 1 a_r=1 ar=1的先验框,这样每个特征图都设置了两个长宽比为1但大小不同的正方形先验框。注意最后一个特征图需要参考一个虚拟 s m + 1 = 300 × 105 / 100 = 315 s_{m+1}=300\times105/100=315 sm+1=300×105/100=315来计算 s m ′ s'_{m} sm′ (88+17(步长)=105)。因此,每个特征图一共有 6 6 6个先验框 { 1 , 2 , 3 , 1 2 , 1 3 , 1 ′ } \{1,2,3,\frac{1}{2},\frac{1}{3},1'\} {1,2,3,21,31,1′},但是实现时,Conv4_3,Conv10_2和Conv11_2层仅使用4个先验框,它们不使用长宽比为 3 , 1 3 3,\frac{1}{3} 3,31的先验框。每个单元的先验框的中心点分布在各个单元的中心,即 ( 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∣为特征图的大小。
得到了特征图之后,需要对特征图进行卷积得到检测结果,图8给出了一个
5
×
5
5\times5
5×5 大小的特征图的检测过程。其中Priorbox是得到的先验框,前面已经介绍了生成规则。
检测值包含两个部分:类别置信度和边界框位置,各采用一次
3
×
3
3\times3
3×3卷积来进行完成。令
n
k
n_k
nk为该特征图所采用的先验框数目,那么类别置信度需要的卷积核数量为
n
k
×
c
n_k\times c
nk×c,而边界框位置需要的卷积核数量为
n
k
×
4
n_k\times 4
nk×4。由于每个先验框都会预测一个边界框,所以SSD300一共可以预测
38
×
38
×
4
+
19
×
19
×
6
+
10
×
10
×
6
+
5
×
5
×
6
+
3
×
3
×
4
+
1
×
1
×
4
=
8732
38\times38\times4+19\times19\times6+10\times10\times6+5\times5\times6+3\times3\times4+1\times1\times4=8732
38×38×4+19×19×6+10×10×6+5×5×6+3×3×4+1×1×4=8732个边界框,这是一个相当庞大的数字,所以说SSD本质上是密集采样。
图8 基于卷积得到检测结果
训练过程
(1)先验框匹配
在训练过程中,首先要确定训练图片中的ground truth(真实目标)与哪个先验框来进行匹配,与之匹配的先验框所对应的边界框将负责预测它。在Yolo中,ground truth的中心落在哪个单元格,该单元格中与其IOU最大的边界框就负责预测它。但是在SSD中却完全不一样,SSD的先验框与ground truth的匹配原则主要有两点:
- 第一个原则,对每个ground truth,找到与其IOU最大的先验框,该先验框与其匹配,保证每个ground truth一定与某个先验框匹配。称与ground truth匹配的先验框为正样本,反之,若一个先验框没有与任何ground truth匹配,那么该先验框只能与背景匹配,即负样本。图片中ground truth是非常少的, 而先验框却很多,如果仅按第一个原则匹配,很多先验框会是负样本,正负样本极不平衡,所以需要第二个原则。
- 第二个原则是:对于剩余的未匹配先验框,若与某个ground truth的 I O U IOU IOU大于某个阈值(一般是0.5),那么该先验框也与这个ground truth进行匹配。这意味着一个ground truth可能与多个先验框匹配。第二个原则在第一个原则之后进行,考虑一下这种情况,如果某个ground truth所对应最大 I O U IOU IOU小于阈值,并且所匹配的先验框却与另外一个ground truth的 I O U IOU IOU大于阈值,那么该先验框应该匹配谁,答案是前者,首先要确保某个ground truth一定有一个先验框与之匹配。但是,这种情况基本上是不存在的,由于先验框很多,某个ground truth的最大 I O U IOU IOU肯定大于阈值,所以可能只实施第二个原则就可以了。
图9为一个匹配示意图,其中绿色的GT是ground truth,红色为先验框,FP表示负样本,TP表示正样本。
图9 先验框匹配示意图
尽管一个ground truth可以与多个先验框匹配,但是ground truth相对先验框还是太少,所以负样本相对正样本还是会多很多。为保证正负样本尽量平衡,SSD采用了hard negative mining,就是对负样本进行抽样(应该是背景),抽样时按照置信度误差(预测背景的置信度越小,误差越大)进行降序排列,选取误差的较大的top-k(前k个)作为训练的负样本,以保证正负样本比例接近1:3。
(2)损失函数
损失函数定义为位置误差(locatization loss, loc)与置信度误差(confidence loss, conf)的加权和:
L ( x , c , l , g ) = 1 N ( L c o n f ( x , c ) + α L l o c ( x , l , g ) ) L(x, c, l, g) = \frac{1}{N} (L_{conf}(x,c) + \alpha L_{loc}(x,l,g)) L(x,c,l,g)=N1(Lconf(x,c)+αLloc(x,l,g))
其中
N
N
N是先验框的正样本数量。这里
x
i
j
p
∈
{
1
,
0
}
x^p_{ij}\in \{ 1,0 \}
xijp∈{1,0}为一个指示参数,当
x
i
j
p
=
1
x^p_{ij}= 1
xijp=1时表示第
i
i
i 个先验框与第
j
j
j个ground truth匹配,并且ground truth的类别为
p
p
p。
c
c
c为类别置信度预测值。
l
l
l 为先验框的所对应边界框的位置预测值,而
g
g
g是ground truth的位置参数。对于位置误差,其采用Smooth L1 loss,定义如下:
由于 x i j p x^p_{ij} xijp的存在,所以位置误差仅针对正样本进行计算。值得注意的是,要先对ground truth的 g g g进行编码得到 g ^ \hat{g} g^ ,因为预测值 l l l也是编码值,若设置variance_encoded_in_target=True,编码时要加上variance:
g ^ j c x = ( g j c x − d i c x ) / d i w / v a r i a n c e [ 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}^{cx}_j = (g^{cx}_j - d^{cx}_i)/d^w_i/variance[0], \hat{g}^{cy}_j = (g^{cy}_j - d^{cy}_i)/d^h_i/variance[1] g^jcx=(gjcx−dicx)/diw/variance[0],g^jcy=(gjcy−dicy)/dih/variance[1]
g ^ j w = log ( g j w / d i w ) / v a r i a n c e [ 2 ] , g ^ j h = log ( g j h / d i h ) / v a r i a n c e [ 3 ] \hat{g}^{w}_j = \log(g^{w}_j/d^w_i)/variance[2], \space \hat{g}^{h}_j = \log(g^{h}_j/d^h_i)/variance[3] g^jw=log(gjw/diw)/variance[2], g^jh=log(gjh/dih)/variance[3]
对于置信度误差,其采用softmax loss(有的也用交叉熵):
权重系数
α
\alpha
α通过交叉验证设置为1。
预测过程
对于每个预测框,首先根据类别置信度确定其类别(置信度最大者)与置信度值,并过滤掉属于背景的预测框。然后根据置信度阈值(如0.5)过滤掉阈值较低的预测框。对于留下的预测框进行解码,根据先验框得到其真实的位置参数(解码后一般还需要做clip,防止预测框位置超出图片)。解码之后,一般根据置信度降序排列,保留top-k(如400)个预测框。最后进行NMS算法,过滤掉重叠度较大的预测框。最后剩余的就是检测结果。
性能评估
首先整体看一下SSD在VOC2007,VOC2012及COCO数据集上的性能,如表1所示。相比之下,SSD512的性能会更好一些。加*的表示使用了image expansion data augmentation(通过zoom out来创造小的训练样本)技巧来提升SSD在小目标上的检测效果,所以性能会有所提升。
SSD与其它检测算法的对比结果(在VOC2007数据集),基本可以看到,SSD与Faster R-CNN有同样的准确度,与Yolo具有同样较快地检测速度。
文章还对SSD的各个trick做了更为细致的分析,表3为不同的trick组合对SSD的性能影响,从表中可以得出如下结论:
- 数据扩增技术很重要,对于mAP的提升很大;
- 使用不同长宽比的先验框可以得到更好的结果;
同样的,采用多尺度的特征图用于检测也是至关重要的:
小结
SSD在Yolo的基础上主要改进了三点:多尺度特征图,利用卷积进行检测,设置先验框。这使得SSD在准确度上比Yolo更好,而且对于小目标检测效果也相对好一点。