解读SSD:Single Shot MultiBox Detector

一、网络框架

     SSD的框架较为简单,basenet采用VGG16,并将FC8层去掉,FC6、FC7层变为3*3conv层。后边加上四层的extra layers,网络结构图如下:


将SSD打印出来得到:

SSD(
  (vgg): ModuleList(
    con1_1: Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    relu1_1: ReLU(inplace)
    conv1_2: Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    relu1_2: ReLU(inplace)
    pool1: MaxPool2d(kernel_size=(2, 2), stride=(2, 2), dilation=(1, 1), ceil_mode=False)
    conv2_1: Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    relu2_1: ReLU(inplace)
    conv2_2: Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    relu2_2: ReLU(inplace)
    pool2: MaxPool2d(kernel_size=(2, 2), stride=(2, 2), dilation=(1, 1), ceil_mode=False)
    conv3_1 Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    relu3_1: ReLU(inplace)
    conv3_2: Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    relu3_2: ReLU(inplace)
    con3_3: Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    relu3_3: ReLU(inplace)
    pool3: MaxPool2d(kernel_size=(2, 2), stride=(2, 2), dilation=(1, 1), ceil_mode=True)
    conv4_1: Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    relu4_1: ReLU(inplace)
    conv4_2: Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    relu4_2: ReLU(inplace)
    conv4_3: Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    relu4_3: ReLU(inplace)
    pool4: MaxPool2d(kernel_size=(2, 2), stride=(2, 2), dilation=(1, 1), ceil_mode=False)
    conv5_1: Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    relu5_1: ReLU(inplace)
    conv5_2: Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    relu5_2: ReLU(inplace)
    conv5_3: Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    relu5_3: ReLU(inplace)
    pool5: MaxPool2d(kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), dilation=(1, 1), ceil_mode=False)
    f6->conv6: Conv2d(512, 1024, kernel_size=(3, 3), stride=(1, 1), padding=(6, 6), dilation=(6, 6))
    relu6: ReLU(inplace)
    f7->conv7: Conv2d(1024, 1024, kernel_size=(1, 1), stride=(1, 1))
    relu7: ReLU(inplace)
  )
  (L2Norm): L2Norm( 用在conv4_3
  )
  (extras): ModuleList(
    conv8_1: Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1))
    conv8_2: Conv2d(256, 512, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
    conv9_1: Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1))
    conv9_2: Conv2d(128, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
    conv10_1: Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1))
    conv10_2: Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1))
    conv11_1: Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1))
    conv11_2: Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1))
  )
  (loc): ModuleList(
    conv4_3: Conv2d(512, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    conv7: Conv2d(1024, 24, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    conv8_2: Conv2d(512, 24, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    conv9_2: Conv2d(256, 24, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    conv10_2: Conv2d(256, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    conv11_2: Conv2d(256, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  )
  (conf): ModuleList(
   conv4_3: Conv2d(512, 12, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
   conv7: Conv2d(1024, 18, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
   conv8_2: Conv2d(512, 18, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
   conv9_2: Conv2d(256, 18, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
   conv10_2: Conv2d(256, 12, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
   conv11_2: Conv2d(256, 12, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  )
  (softmax): Softmax()

)

二、Default box

   SSD中的Default box类似RPN生成的anhour。Default box是在不同的feature map上的每一个像素点(cell)生成不同长宽比的预选框。因此,SSD不用先生成proposal再进行分类、回归,是一个单阶段的过程,所以速度非常快,论文中表明对于VOC2007数据集的检测速度达到59FPS,比两阶段的目标检测网络快好几倍,检测精度为74.3mAP,与Faster相当。并且SSD通过融合feature map和不同aspect ratio的box方式实现了图像的多尺度检测。

Default box的产生过程和数量如下所示,得到了一共8732个Default box后,网络直接对这些box做3*3的conv,预测每个box属于某一类的confidence,以及每一个box的位置(左上角和右下角的坐标)。


不同的featrure map的大小是不同的,因此我们产生的box大小也回随着feature map的变化而变化,具体的计算方式为:


m为6,代表选取6张特征图做预测和回归,论文指出smin=0.2,smax=0.9,每一层的sk通过上式计算,得到sk后,我们就能计算box的宽高和中心点坐标了:

                                      

                                           

其中a代表不同的aspect ratio,具体取值为,对于a=1,即aspect ratio=1时,我们计算得到的box是一个边长为sk的正方形,论文提出对于aspect ratio为1的时候,增加一个box,此box的sk计算公式如下:


显而易见,我们对于提取的feature map层的每一个像素点都可以产生6个box,但是实际过程中我们对于某些feature map只用产生4个box,去掉a={3,1/3}两个长宽比的box。feature map层产生box的数量如下图所示:


   表中的variance表示我们产生的box坐标信息(中心点+宽高)对loss的权衡,如variance={0.1,0.2}即表明我们计算loss的时候中心点坐标(c_x,c_y)会乘以0.1,宽高(w,h)会乘以0.2,即宽高的大小对loss影响更严重。offeset对应上面计算中心点坐标的偏移量。

  对于一张m*n大小的feature map,我们在上面产生k个box,通过3*3的卷积对box进行类别预测和位置预测,假设我们一共有c类,那么最终对于这张特征图,我们需要预测m*n*k*(c+4)个特征量。

三、训练过程

     以pytorh版本的SSD为例,代码中训练的过程可简单归纳为:


 dataset读取调用pytorch的DataLoader函数即可,load进数据后,我们需要转换annotation的格式,在VOCDetection中调用transform annotation函数,然后进行数据增广,SSD前传,计算损失并反传调参,直到loss小于设定阈值或达到最大迭代次数则终止训练。对数据增广和构建SSD网络以及Loss计算,这里做一些解释。

3.1数据增强

    普通的CNN网络或目标检测网络,我们在数据预处理阶段,总是会通过旋转、色彩改变等方式做数据增强,SSD网络除了用这些增强方式以外,还提出了三种扩充样本的方法:

  1->使用原始图像

  2->随机采样patch,patch需要满足与GT的jaccard overlap是0.1、0.3、0.5、0.7、0.9

 3->随机采样一个patch(我理解为crop),相对原图的采样比例必须在[0.1,1],长宽比在[0.5,2]之间,并且必须包含GT的中心坐标,若不包含则视为无效的patch.

  在执行数据增强的时候,SSD定义的以上三种方式是随机的选一种执行。数据增强会将多种增强方式compose起来,代码见下图:


   你是不是以为进行了数据增强,训练的样本就增多了呢?大错特错,一个batch的训练数据进行数据增强后还是只有batch那么多训练数据!但是这个batch里面的数据形式已经和增强前的batch数据形式发生了改变!因为我们对于每一个输入,是随机决定是否进行增强的,如果选择是,那么增强后的样本就会替代掉本来的样本。当存在多种增强方式时,第一种增强的输出作为第二种增强的输入,最后一种增强的输出作为新的样本替代掉原始输入,这样总数就是不变的,并且样本也有了新的表达形式!

3.2 构建SSD网络

    pytorch版本的代码里面是调用build_ssd来实现网络搭建的。ssd主要由vgg16(modified)+extra layers组成;除此之外,还定义了mbox,对应产生box的feature map;prior box(就是default box),这里会调用prior box.py函数,prior box.py里面主要计算了feature map上每个cell不同aspect ratio下提取的prior box的位置信息(c_x,c_y,w,h);SSD类里面的source存放的是提取了box的那些feature map(conv4_3,conv7,extra layers);最后定义了两个损失loc、conf。

3.3 Loss

 SSD的损失函数包含box的位置损失Loc loss和置信度损失Conf loss,数学表达式如下:


Conf loss只有正样本才存在,具体是一个关于softmax的交叉熵损失,表达式为:


Loc loss是类似Faster RCNN里面的smooth L1损失,计算的方式如



xij    表示第i个default box与第j个GT匹配上,类别是k,因此它的取值是{1,0}。

    还要提一点的是计算g的(cx,cy,w,h)时进行了归一化,即除了宽高信息,这里我的理解是要保持在相同的IOU重叠率下得到的Loss应该是相同的。见下图:


 3.3.1 Match priors

       SSD网络会产生8732个prior boxes,对于这8732个box,我们需要划分其为正样本或负样本,划分样本可概括为以下两步:

    1->保存与GT最匹配的prior box,确保每一个GT至少有一个box是与其匹配的;

    2->GT与每一个prior box匹配,jaccard overlap>threshold视为正样本,其余为负样本。

 3.3.2 Hard negative mining

     经过Match priors后我们会得到部分正样本和非常多的负样本,如果我们不进行处理的话,会造成正负样本间的严重失衡,因此我们需要对负样本进行筛选,筛选过程概括为:

   1->统计正样本数量,乘3得到负样本的数量(正负样本比1:3),假设记为num;

   2->将负样本中loss最大的前num个负样本作为参与训练的负样本。

至此,SSD的基本结构和训练过程就了解得差不多了,代码中还有很多细节值得大家去琢磨,比如计算sk等地方,最后我对SSD做一个简单的总结。

四 小结

   SSD特点:

   1.单阶段,速度快,精度与Faster RCNN相当 ;

   2.通过融合多张feature map,多个aspect ratio的box实现图像的多尺度检测;

   3.直接用3*3的卷积核进行预测。

   4.小目标检测效果较差。


参考资料:

1.深度学习笔记(七)SSD 论文阅读笔记

2.Normalization on conv4_3 in SSD

3.论文阅读:SSD: Single Shot MultiBox Detector

4.目标检测算法之SSD

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值