SDD算法的全称是Single Shot MultiBox Detector,在这个名字中开头使用了single shot就表示了这是一个ont-stage的方法,multiBOX说明了这是一个多框的预测。相比较yolo算法,SDD直接采用CNN来直接进行检测,而不像yolo在全连接层之后做检测。SDD算法有两个重大的改变,他采取了不同尺度的卷积的特征图来做检测,大尺度的特征图就可以用来检测小的东西,而许爱的尺度特征图用来检测大的物体;在另一方面SDD采用了不同尺度和长宽比的先验(Prior boxes, Default boxes,在Faster R-CNN中叫做锚,Anchors),yolo算法缺点是难以确定小目标,定位也不是很准,在这几点中SDD都对此做了改进。
Yolo和SDD都是采用CNN来进行检测,但是SSD采用了多尺度的特征图,它的结构如下的总结:
所谓多尺度就是采用大小不同的特征图,CNN网络一般前面的特征图是比较大的,后面会逐渐使用stride=x或者使用pool操作使得特征图带大小降低,就像上面的图片展示的一样,一个大的和一个小的特征图,他们都是拿来做检测的。这样的好处就在于大的特征图检测小的目标,反之小的检测大的,像下面这张图片给一样,8x8的特征图可以划分更多的单元,但是他每个单元的先验框是比较小的。
就像我们简介上讲的一样,SDD是直接使用卷积的特征图来进行检测,对于形状为m x n x p的特征图,只需要采用3X3Xp这样比较的卷积得到检测值。
在YOLO网络中我们每个单元预测是许多个边界框,但是他们都是我们预测单元的本身(即正方块),但是我们图像中的形状肯定不是一成不变的,YOLO在训练中才能自适应检测目标的形状。但是相比较SSD借鉴了FAST-R-CNN中的anchor的概念,每个单元设置尺度或者长宽比不同的先验框,预测的边界框是以这些我们已经检测到的先验框作为基准的,在一定程度上减少了训练的难度(也就是更好的识别我们图像中目标),在检测的一般情况下我们每个检测单元会有多个先验框,他们形状,方向是不同的。在下面的图像中我们每个目标有很多的先验框,我们通过查找他们最适合的先验框来让我们的网络来更好的区分并训练他们。
但是SSD的检测值也与Yolo不太一样。他对于每个单元的预选框,他都输出一套独立的检测值,对应一个边界框,它主要包含两个部分。第一个部分是每个类别的置信度(评分),就是看看他们更加的贴合那一类(分数比较的高),但是你要注意一个点,他的置信度是c+1的因为它包含的背景的置信度,他是没有目标的,他的分数也是对高的,所以我们一般会说置信度是c,这就表示他又c-1个检测真实类别。第二部分是边界框的location,他包含四个值**(x,y,w,h)**,分别便是边界框的中心坐标以及宽高,但是真实预测值其实只是边界框相当于先验框的转换值(paper里面说是offset,但是觉得transformation更合适,参见R-CNN),先验框位置用表示,其对应边界框用 $表示,那么边界框的预测值 [公式] 其实是 b 相对于 d 的转换值:
在我们的一般理解中,我们说我们上面的过程是边界框的编码(encode),预测的时候我们序言反向这个过程,就是说进行解码(decode),从预测值 中得到边界框的真实位置 b :
但是在SDD的Caffe源码实现中还有trick,就是设置cariance超参数来调整我们的检测值,通过
参数variance_encoded_in_target来控制两种模式,每当他的参数为TRUE时,表示variance被包含在预测值中,就是上面那种情况。反之(就是取值是FALSE的时候)我们就要去手动的设置超参数variance,用来对 的4个值进行放缩,这个时候边界框我们就需要这样解码:
根据上面说的,对于每一个 的特征图,我们就有了MN个单元,每个单元设置的先验框数目记为 ,那么每个单元共需要 个预测值,所有的单元共需要 个预测值,由于SSD采用卷积做检测,所以就需要 个卷积核完成这个特征图的检测过程。
SSD网络是用VGG16作为基础的,然后在VGG的基础上新增了卷积层来获得更多的特征图以用来检测,SDD网络如下所示。
上面第二个YOLO模型,可以看出的是SSD网络 使用了多尺度的特征图来做检测,模型的输入是 (还可以是 ,其与前者网络结构没有差别,只是最后新增一个卷积层。
采用VGG16模型做基础,首先我们要知道VGG16是在ILSVRC CLS-LOC数据集预训练,然后他又借鉴了DeepLab-LargeFOV,分别VGG16的全连接层fc6和fc7转换成 卷积层 conv6和 卷积层conv7,同时将池化层pool5由原来的stride=2的 变成stride=1的 ,为了配合这种变化,采用了一种Atrous Algorithm,其实就是conv6采用扩展卷积或带孔卷积(Dilation Conv),其在不增加参数与模型复杂度的条件下指数级扩大卷积的视野,其使用扩张率(dilation rate)参数,来表示扩张的大小,如下图所示,(a)是普通的 卷积,其视野就是 ,(b)是扩张率为2,此时视野变成 ,©扩张率为4时,视野扩大为 ,但是视野的特征更稀疏了。Conv6采用 大小但dilation rate=6的扩展卷积。
然后移除dropout层和fc8层,并新增一系列卷积层,在检测数据集上做finetuing。
其中的VGG16的Conv4_3层是作为我们用于检测的第一个特征图。conv4_3层的特征图大小是,但是该层是比较靠前的,他的norm会比较大,所以我们需要在他的后面加上一个l2 Normalizetion层(详细请看ParseNet),用来保证后面的检测层之间的差异不是很大,这个和Batch Normalization层不大一样,其仅仅是对每个像素点在channle维度做的归一化,而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
在后面新增的卷积层中提取Conv7,Conv8_2,Conv9_2,Conv10_2,Conv11_2作为检测所用的特征图,加上Conv4_3层,共提取了6个特征图,其大小分别是 ,但是不同的特征图我们设置先验框的数目也是不一样的(同一个特征图上每个单元设置的先验框是相同的,这里的数目指的是一个单元的先验框数目)。先验框的设置,包括大小和长宽比这两个方面。我们对于先验框的设置需要遵循一个规则:随着特征图大小降低,先验框尺度线性增加:
在这个中m就是我们的特征图的个数(-1表示我们去除了背景的特征图),但我们的m=5,这是因为第一层(也就是conv4_3)是我们最开始单独设置的一层卷积,表示先验框大小相对于图片的比例,而 和 表示比例的最小值与最大值,paper里面取0.2和0.9。对于我们的第一个特征图,他的先验框的尺度一般我们设置为 ,南无他的大小就是,对于后面的特征图,先验框尺度按照上面的公式进行线性增加,但是我们需要的是讲大小比例扩大100倍,这个时候的步长我们设置为 ,这样各个特征图的大小就是 ,将这些比例除以100,然后再乘以图片大小,可以得到各个特征图的尺度为 ,这种计算方式是参考SSD的Caffe源码。综上,可以得到各个特征图的先验框尺度 。对于长宽比,一般选取 ,对于特定的长宽比,按如下公式计算先验框的宽度与高度(后面的 均指的是先验框实际尺度,而不是尺度比例):
默认情况下,每个特征图会有一个 且尺度为 的先验框,除此之外,还会设置一个尺度为 且 的先验框,这样每个特征图都设置了两个长宽比为1但大小不同的正方形先验框。注意最后一个特征图需要参考一个虚拟 来计算 。因此,每个特征图一共有 个先验框 ,但是在实现时,Conv4_3,Conv10_2和Conv11_2层仅使用4个先验框,它们不使用长宽比为 的先验框。每个单元的先验框的中心点分布在各个单元的中心,即 ,其中 为特征图的大小。
得到了特征图之后,需要对特征图进行卷积得到检测结果,下图给出了一个 大小的特征图的检测过程。其中Priorbox是得到先验框,前面已经介绍了生成规则。检测值包含两个部分:类别置信度和边界框位置,各采用一次 卷积来进行完成。令 为该特征图所采用的先验框数目,那么类别置信度需要的卷积核数量为 ,而边界框位置需要的卷积核数量为 。由于每个先验框都会预测一个边界框,所以SSD300一共可以预测 个边界框,这是一个相当庞大的数字,所以说SSD本质上是密集采样。
这是一篇个人学习总结,如有错误请联系编者,谢谢