目标检测系列:SSD

论文链接:SSD: Single Shot MultiBox Detector
首先我们看一下这个题目可以知道,这是一个一阶段方法,肯定会有的好处,速度快,缺点就是精度没那么高,对小物体可能不敏感。然后MultiBox,这个是说要产生不同尺度的默认框,使默认框更加的多。SSD的整体结构借鉴了金字塔结构。原文作者认为他们的贡献如下:

  1. 简单的说:比yolov1还快,精度可以和faster rcnn想媲美。
  2. SSD的核心是预测默认框的分类,和默认框的修正偏差,使用更小的卷积过滤器
  3. 产生的默认框有多尺度
  4. 即使输入的图片分辨率低也可以产生很好的结果,并且速度很快
    在yolov1中说过每个目标检测都可以分为三个阶段:
  • 产生默认框
  • 对默认框进行图片分类
  • 对有物体的默认框进行微调

下面我们也是按着这个思路来看这篇文章:

1 生成默认框

在这里插入图片描述
SSD参考了FPN的金字塔特征图,和yolov1进行对比,yolov1只在最后一个层产生默认框,这样是不精确的,SSD作者认为特征图的w,h越小,那么具有的全局信息越大,可以用来预测大尺度的物体,w,h越大,说明对局部信息更加敏感,应该产生较小的默认框,预测小物体,于是作者在不同层的特征图上产生不同数量的默认框,针对不同大小的物体,
依次对应Conv5_3,Conv7,Conv8_2,Conv9_2,Conv10_2, Conv11_2,分别产生38x38x4,19x19x6,10x10x6,5x5x6,3x3x4,1x1x4,全部相加一共8732个默认框,这足以应对各类大小的物体。
具体怎么给每个卷积层产生默认框,看代码最直接了

#参数生成先验。  
#输入图像的最小尺寸  
min_dim = 300   #######维度  
# conv4_3 ==> 38 x 38  
# fc7 ==> 19 x 19  
# conv6_2 ==> 10 x 10  
# conv7_2 ==> 5 x 5  
# conv8_2 ==> 3 x 3  
# conv9_2 ==> 1 x 1  
mbox_source_layers = ['conv4_3', 'fc7', 'conv6_2', 'conv7_2', 'conv8_2', 'conv9_2'] #####prior_box来源层,可以更改。很多改进都是基于此处的调整。  
# in percent %  
min_ratio = 20 ####这里即是论文中所说的Smin=0.2,Smax=0.9的初始值,经过下面的运算即可得到min_sizes,max_sizes。
max_ratio = 90  
####math.floor()函数表示:求一个最接近它的整数,它的值小于或等于这个浮点数。  
step = int(math.floor((max_ratio - min_ratio) / (len(mbox_source_layers) - 2)))####取一个间距步长,即在下面for循环给ratio取值时起一个间距作用。可以用一个具体的数值代替,这里等于17。  
min_sizes = []  ###经过以下运算得到min_sizes和max_sizes。  
max_sizes = []  
for ratio in xrange(min_ratio, max_ratio + 1, step):  ####从min_ratio至max_ratio+1每隔step=17取一个值赋值给ratio。注意xrange函数的作用。  
########min_sizes.append()函数即把括号内部每次得到的值依次给了min_sizes。  
  min_sizes.append(min_dim * ratio / 100.)  
  max_sizes.append(min_dim * (ratio + step) / 100.)  
min_sizes = [min_dim * 10 / 100.] + min_sizes  
max_sizes = [min_dim * 20 / 100.] + max_sizes  
steps = [8, 16, 32, 64, 100, 300]  ###这一步要仔细理解,即计算卷积层产生的prior_box距离原图的步长,先验框中心点的坐标会乘以step,相当于从feature map位置映射回原图位置,比如conv4_3输出特征图大小为38*38,而输入的图片为300*300,所以38*8约等于300,所以映射步长为8。这是针对300*300的训练图片。  
aspect_ratios = [[2], [2, 3], [2, 3], [2, 3], [2], [2]]  #######这里指的是横纵比,六种尺度对应六个产生prior_box的卷积层。具体可查看生成的train.prototxt文件一一对应每层的aspect_ratio参数,此参数在caffe.proto中有定义,关于aspect_ratios如何把其内容传递给了aspect_ratio,在model_libs.py文件中有详细定义。  
##在此我们要说明一个事实,就是文中的长宽比是如何产生的,这里请读者一定要参看博主博文《SSD详解(一)》中的第2部分内容,关于prior_box的产生。  

因为有六组不同尺度的默认框,然后要给每个卷积层一个基本大小,首先要计算每个卷积层中对应的min_size,max_size,然后给每个卷积层产生[4,6,6,6,4,4]个默认框
在这里插入图片描述
代码中给出了每个卷积层中对应默认框的比率[[2], [2, 3], [2, 3], [2, 3], [2], [2]],这个怎么就算呢,以[2]为例,boxes=[]首先要计算出两个正方形默认框加入到boxes中,这两个正方形框如下图所示分别问[min_size,min_size], [ m i n s i z e ∗ m a x s i z e ] [\sqrt{min_size*max_size}] [minsizemaxsize ],然后加入 [ 1 / 2 ∗ m i n s i z e , 2 ∗ m i n s i z e ] [1/\sqrt2*min_size, \sqrt2*min_size] [1/2 minsize,2 minsize] [ 2 ∗ m i n s i z e , 1 / 2 ∗ m i n s i z e ] [\sqrt2*min_size,1/ \sqrt2*min_size] [2 minsize,1/2 minsize],同理可以计算当比率为3的尺寸。

在这里插入图片描述
一次计算所有的默认框尺寸

  • conv4_3:小正方形边长=min_size=30,大正方形边长 = m i n s i z e ∗ m a x s i z e = 30 ∗ 60 = 42.42 =\sqrt{min_size*max_size}=\sqrt{30*60}=42.42 =minsizemaxsize =3060 =42.42
    长方形的宽 = a s p e c t r a t i o ∗ m i n s i z e = 2 ∗ 30 =\sqrt{aspect_ratio}*min_size=\sqrt2*30 =aspectratio minsize=2 30,高 = 1 / a s p e c t r a t i o ∗ m i n s i z e = 30 / 2 =1/\sqrt{aspect_ratio}*min_size=30/\sqrt2 =1/aspectratio minsize=30/2 ,宽高比刚好为2:1;将以上宽高旋转90度产生另一个长方形,宽高比变为1:2。
  • fc7:小正方形边长=min_size=60,大正方形边长 = m i n s i z e ∗ m a x s i z e = 60 ∗ 111 = 81.6 =\sqrt{min_size*max_size}=\sqrt{60*111}=81.6 =minsizemaxsize =60111 =81.6; 第1组长方形的宽= a s p e c t r a t i o ∗ m i n s i z e = 2 ∗ 60 \sqrt{aspect_ratio}*min_size=\sqrt2*60 aspectratio minsize=2 60,高 = 1 / a s p e c t r a t i o ∗ m i n s i z e = 60 / 2 =1/\sqrt{aspect_ratio}*min_size=60/\sqrt2 =1/aspectratio minsize=60/2 ,宽高比刚好为2:1;将以上宽高旋转90度产生另一个长方形,宽高比变为1:2。 第2组长方形的宽 = a s p e c t r a t i o ∗ m i n s i z e = 3 ∗ 60 =\sqrt{aspect_ratio}*min_size=\sqrt3*60 =aspectratio minsize=3 60,高 = 1 / a s p e c t r a t i o ∗ m i n s i z e = 60 / 3 =1/\sqrt{aspect_ratio}*min_size=60/\sqrt3 =1/aspectratio minsize=60/3 ,宽高比刚好为3:1;将以上宽高旋转90度产生另一个长方形,宽高比变为1:3。
  • conv6_2:小正方形边长=min_size=111,大正方形边长 = m i n s i z e ∗ m a x s i z e = 111 ∗ 162 =\sqrt{min_size*max_size}=\sqrt{111*162} =minsizemaxsize =111162 ;第1组长方形的宽 = a s p e c t r a t i o ∗ m i n s i z e = 2 ∗ 111 =\sqrt{aspect_ratio}*min_size=\sqrt2*111 =aspectratio minsize=2 111,高 = 1 / a s p e c t r a t i o ∗ m i n s i z e = 111 / 2 =1/\sqrt{aspect_ratio}*min_size=111/\sqrt2 =1/aspectratio minsize=111/2 ,宽高比刚好为2:1;将以上宽高旋转90度产生另一个长方形,宽高比变为1:2。 第2组长方形的宽 = a s p e c t r a t i o ∗ m i n s i z e = 3 ∗ 111 =\sqrt{aspect_ratio}*min_size=\sqrt3*111 =aspectratio minsize=3 111,高 = 1 / a s p e c t r a t i o ∗ m i n s i z e = 111 / 3 =1/\sqrt{aspect_ratio}*min_size=111/\sqrt3 =1/aspectratio minsize=111/3 ,宽高比刚好为3:1;
    将以上宽高旋转90度产生另一个长方形,宽高比变为1:3。
    conv7_2、conv8_2、conv9_2我们这里就不再计算了,相信大家看完以上应该明白了如何计算,具体实现的步骤请大家参考脚本prior_box_layer.cpp。这就是我们先验框的计算方式。

2.对默认框中的图片进行分类

网络的整体框架使用的是vgg-16,关于网络的具体实现,这里就不详细研究了。
1. SSD和yolov1有个很明显的不同,yolov1中,每个grid可以产生多个boxes但是,只能有一个物体落入当前grid当中,而SSD不是这样,SSD是以box为单位的,每个box只能有一个物体,但是一个grid有多个box,也就是说可能有多个物体落入grid中,这是ssd和yolov1 的一个明显不同。
2. 要添加一个背景类num_class + 1个类别
SSD中的多个卷积层都产生默认框,只要将这些卷积层分开一个一个处理就好了,这与yolov1没有太多不同,这里不再详细讲了

3. 对生成的默认框进行回归

这个地方可以直接看损失函数,损失函数由两部分构成:
1. L c o n f ( x , c ) L_{conf}(x,c) Lconf(x,c)这部分是confidence损失,也是由两部分构成的, x i , j k = 1 , 0 x^k_{i,j} = {1,0} xi,jk=1,0,取1表示此时第i个搜索框和第j个类别框IOU大于阈值,此时真实框中对象类别为p。前半部分表示有物体的默认框产生的误差,后半部分表示没有物体背景类产生的误差,这个东西仔细一看,豁然开朗,原来就是个简单的交叉熵。
2. L l o c ( x , l , g ) L_{loc}(x,l,g) Lloc(x,l,g)是坐标产生的误差,这部分的处理和yolov1,faster rcnn的处理方式相同,都是计算偏移产生的误差,并不是直接计算坐标的误差,smooth函数如下,这个函数的的好处,是当误差小于一的时候梯度变小,这是不容易在波谷的位置来回波动,不收敛,当x大于一的时候梯度为1,下降的快,这个函数非常好,可以加速收敛。
3. α \alpha α只是一个用来平衡两种损失的一个系数
4. 本文中采用了难例挖掘:CNN目标检测类的文章中,hard negative mining一般是,有正负样本,然后分类器分出来一些分错的负样本(容易将负样本看成正样本的那些样本),即假阳性(false positive),也就是说在对负样本分类时候,loss比较大(label与prediction相差较大)的那些样本,这些就是hard negative/困难样本。
hard negative mining就是多找一些hard negative加入负样本集,重新进行训练,这样会比easy negative(例如roi里没有物体,全是背景,这时候分类器很容易正确分类成背景,这个就叫easy negative;如果roi里有二分之一个物体,标签仍是负样本,这时候分类器就容易把他看成正样本,这时候就是hard negative)组成的负样本集效果更好。主要体现在虚警率更低一些(也就是false positive少)。
如何判断困难负样本呢?先用初始样本集(即第一帧随机选择的正负样本)去训练网络,再用训练好的网络去预测负样本集中剩余的负样本,选择其中得分最高,即最容易被判断为正样本的负样本为困难样本,加入负样本集中,重新训练网络,循环往复。我的理解就是假设当大于0.5的时候为正例,小于0.5的时候为负样本,那么就要逐渐利用接近0.5的标签进行训练,这样训练得到的模型区分能力更强

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值