theirs ssd anchor

Sk是每个特征层的先验框大小与原图片大小之比,Smax和Smin分别是最大,最小的比例.
在SSD中一共有6个用于分类和回归的特征层(feature map),
分别是feat_layers=['block4', 'block7', 'block8', 'block9', 'block10', 'block11'],
m是就是特征层的个数,按理说分母应该是6-1=5,但是这里是5-1=4, 因为作者将第一层S1, 单独拿出来设置了.
k是第几个特征层的意思,注意k的范围是1~m, 也就是1~6.
知道这些就可以开始计算了.
Sk在实际计算中考虑到取整的问题,所有与上式有所区别:
----
其中这个anchor_sizes是最magic的一组数字,他到底是怎么来的呢?
首先你要知道这个公式:
Sk是每个特征层的先验框大小与原图片大小之比,Smax和Smin分别是最大,最小的比例.
在SSD中一共有6个用于分类和回归的特征层(feature map),
分别是feat_layers=['block4', 'block7', 'block8', 'block9', 'block10', 'block11'],
m是就是特征层的个数,按理说分母应该是6-1=5,但是这里是5-1=4.很神奇。
k是第几个特征层的意思,注意k的范围是1~m,  也就是1~6.
知道这些就可以开始计算了.
Sk在实际计算中考虑到取整的问题,所有与上式有所区别:
需要向下取整.
所以你将m=5, Smin, Smax=(0.2, 0.9) 就是anchor_size_bounds=[0.20, 0.90],带入之后,
得到S1~S6: 20, 37, 54, 71, 88, 105, 其实就是挨个+17.
此时你还需要将其除以100还原回来,也就是:
0.2, 0.37, 0.54, 0.71, 0.88, 1.05
然后,我们这个是比例, 我们需要得到其在原图上的尺寸,所以需要依次乘以300, 得到:
60, 111, 162, 213, 264, 315.
最后,你会问: 30呢? 这就要说到S1’了,S1’=0.5*S1, 就是0.1, 再乘以300, 就是30.
这样, 我们有七个数, 6个区间段, 也就是6组min_size和max_size. 就可以计算每个特征层的anchor_box 的尺寸了.详细见下片博文.
可以看出作者为了不让我们搞懂他的代码,也是煞费苦心啊…
最后, 既然我们高明白了anchor_size, 这个参数直接决定了当前特征层的box 的大小,  可以看出越靠近输入层, box越小, 越靠近输出层, box越大,  所以 SSD的底层用于检测小目标, 高层用于检测大目标.
 

------

 feature map则是刚才指的Classifier产生的结果,如图(b)和(c) b是大小8*8的feature map,c是大小4*4的feature map,其中每一个小个子都包含多个box,同时每个box对应loc(位置坐标)和conf(每个种类的得分),default box长宽比例默认有四个和六个,四个default box是长宽比为(1:1)、(2:1)、(1:2)、(1:1)这四个,六个则是添加了(1:3)、(3:1)这两个,这时候有小伙伴会问,为什么会有两个(1:1)呢。这时候就要讲下论文中Choosing scales and aspect ratios for default boxes这段内容了。作者认为不同的feature map应该有不同的比例,这是什么意思呢,代表的是default box中这个1在原图中的尺寸是多大的,计算公式如下所示:

公式Sk即代表在300*300输入中的比例,m为当前的feature map是第几层,k代表的是一共有多少层的feature map,Smin和Smax代表的是第一层和最后一层所占的比例,在ssd300中为0.2-0.9。那么S怎么用呢,作者给出的计算方法是,wk = Sk√ar,hk = Sk / √ar,其中ar代表的是之前提到的default box比例,即(1,2,3,1/2,1/3),对于default box中心点的值取值为((i+0.5) / |fk|,(j+0.5)/|fk|),其中i,j代表在feature map中的水平和垂直的第几格,fk代表的是feature map的size。那么重点来了,还记得之前有一个小疑问,为什么default box的size有两个1吗?作者在这有引入了一个Sk' = √Sk*Sk+1,多出来的那个1则是通过使用这个Sk'来计算的,有的小伙伴可能会有疑问,这有了k+1则需要多出来一部分的Sk啊,是的没错,作者的代码中就添加了两层,第一层取0.1,最后一层取1。到这位置,基本上模型和default box是解释清楚了。

---------
1. 默认框产生的个数
        我们都知道SSD默认框从6层卷积层输出的特征图中产生,分别为conv4_3、fc7、conv6_2、conv7_2、conv8_2、conv9_2。这6个特征层产生的特征图的大小分别为38*38、19*19、10*10、5*5、3*3、1*1。每个n*n大小的特征图中有n*n个中心点,每个中心点产生k个默认框,六层中每层的每个中心点产生的k分别为4、6、6、6、4、4。所以6层中的每层取一个特征图共产生38*38*4+19*19*6+10*10*6+5*5*6+3*3*4+1*1*4=8732个默认框。
2. 特征图产生min_sizes和max_sizes的计算方式
        对ssd产生的默认框的大小计算首先要计算参数min_sizes和max_sizes,这些参数具体在ssd_pascal.py中有计算方法。
代码如下:
#参数生成先验。  #输入图像的最小尺寸
  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。具体如何计算以及两者代表什么,请关注我的博客SSD详解。这里产生很多改进。  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的产生。 
如上代码再结合prior_box_layer.cpp产生先验框,然后再结合bbox_util.cpp对先验框进行匹配。
        首先根据以上代码,六个特征层共产生6组min_sizes和max_sizes。另外min_dim=300,ratio取20到90即min_ratio=20,max_ratio=90。
        然后根据代码的计算公式,我们还需要step,注意是step不是steps,两者的作用不一样,在代码中有博主的注释。这里计算后step=(max_ratio-min_ratio)/(len(mbox_source_layers)-2)=(90-20)/(6-2)=17。要说这个step的作用,其实就是取一个间隔,全文看完你就应该明白了。其实这里用了一个复杂的公式说白了就是显得代码高大上一点。
        然后就开始计算min_sizes和max_sizes了,首先定义数组min_sizes[]和max_sizes[]用来存放计算结果,没有初始化说明默认为0,。然后计算conv4_3产生的min_sizes和max_sizes。根据代码中的公式计算:min_sizes=[min_dim*10/100]+min_sizes和max_sizes=[min_dim*20/100]+max_sizes得到结果为min_sizes=[300*10/100]+0=30,而max_sizes=[300*20/100]+0=60。这样conv4_3的计算公式被计算分别为30和60。这里为什么要先计算下面两行产生conv4_3的结果而不是使用上面两行公式产生博主也没有搞明白,欢迎指教。
        然后根据公式min_sizes.append(min_dim*ratio/100)和公式max_sizes.append(min_dim*(ratio+step)/100)来计算剩下5层的min_size和max_sizes。这里需要用到ratio和step,我们前面讲了step=17,根据代码for ratio in xrange(min_ratio, max_ratio+1, step)(这句的意思我们在代码中有注释,即在min_ratio和max_ratio之间即20-90之间以step=17为间隔产生一组数据赋值给ratio),最终ratio=[20,37,54,71,88]。所以对于剩余5层所产生的min_sizes和max_sizes分别为:
fc7:min_sizes=min_dim*ratio/100=300*20/100=60,max_sizes=min_dim*(ratio+step)/100=300*(20+17)/100=111;
conv6_2:min_sizes=min_dim*ratio/100=300*37/100=111,max_sizes=min_dim*(ratio+step)/100=300*(37+17)/100=162;
conv7_2:min_sizes=min_dim*ratio/100=300*54/100=162,max_sizes=min_dim*(ratio+step)/100=300*(54+17)/100=213;
conv8_2:min_sizes=min_dim*ratio/100=300*71/100=213,max_sizes=min_dim*(ratio+step)/100=300*(71+17)/100=264;
conv9_2:min_sizes=min_dim*ratio/100=300*88/100=213,max_sizes=min_dim*(ratio+step)/100=300*(88+17)/100=315;
        所以,最终计算的结果为:
3. 产生默认框的大小的计算方式
文中代码显示,给出的长宽比为
aspect_ratios = [[2], [2, 3], [2, 3], [2, 3], [2], [2]]
        这里并不是paper中所给出的ar={1,2,3,1/2,1/3},这个比例是计算出来的。
        首先我们要知道,我们在前面也讲了,每层的特征图的每个中心点分别会产生4、6、6、6、4、4个默认框,但我们要知道为什么是这几个默认框,这里就和aspect_ratios有关系了。
        在SSD中6层卷积层的每个特征图的每个中心点会产生2个不同大小的正方形默认框,另外每设置一个aspect_ratio则会增加两个长方形默认框,而文中代码对于6层的aspect_ratio个数分别为1、2、2、2、1、1,所以这也就是为什么会产生4、6、6、6、4、4个默认框了。例如conv4_3默认生成两个不同大小的正方形默认框,另外又有一个aspect_ratio=2产生了两个长方形默认框,所以总共有4个。再如fc7,默认生成两个正方形默认框,另外又有aspect_ratio=[2,3],所以又生成了4个不同的长方形默认框,共有6个不同大小的默认框。
        接着我们再讲这些产生的默认框的大小计算。这里参考paper中的计算公式,我们可以知道,对于产生的正方形的默认框,一大一小共两个,其边长计算公式为:小边长=min_size,而大边长=sqrt(min_size*max_size)。对于产生的长方形默认框,我们需要计算它的高(height)和宽(width),其中,height=1/sqrt(aspect_ratio)*min_size,width=sqrt(aspect_ratio)*min_size,对其高和宽翻转后得到另一个面积相同但宽高相互置换的长方形。如图所示:
根据以上分析,我们可以计算6层中每个特征图的每个中心点所产生的默认框的大小,分别如下:
conv4_3:小正方形边长=min_size=30,大正方形边长=sqrt(min_size*max_size)=sprt(30*60)=42.42;
                   长方形的宽=sqrt(aspect_ratio)*min_size=sqrt(2)*30,高=1/sqrt(aspect_ratio)*min_size=30/sqrt(2),宽高比刚好为2:1;
                    将以上宽高旋转90度产生另一个长方形,宽高比变为1:2。
fc7:小正方形边长=min_size=60,大正方形边长=sqrt(min_size*max_size)=sprt(60*111)=81.6;
                   第1组长方形的宽=sqrt(aspect_ratio)*min_size=sqrt(2)*60,高=1/sqrt(aspect_ratio)*min_size=60/sqrt(2),宽高比刚好为2:1;
                              将以上宽高旋转90度产生另一个长方形,宽高比变为1:2。
                   第2组长方形的宽=sqrt(aspect_ratio)*min_size=sqrt(3)*60,高=1/sqrt(aspect_ratio)*min_size=60/sqrt(3),宽高比刚好为3:1;
                              将以上宽高旋转90度产生另一个长方形,宽高比变为1:3。
conv6_2:小正方形边长=min_size=111,大正方形边长=sqrt(min_size*max_size)=sprt(111*162);
                   第1组长方形的宽=sqrt(aspect_ratio)*min_size=sqrt(2)*111,高=1/sqrt(aspect_ratio)*min_size=111/sqrt(2),宽高比刚好为2:1;
                              将以上宽高旋转90度产生另一个长方形,宽高比变为1:2。
                   第2组长方形的宽=sqrt(aspect_ratio)*min_size=sqrt(3)*111,高=1/sqrt(aspect_ratio)*min_size=111/sqrt(3),宽高比刚好为3:1;
                              将以上宽高旋转90度产生另一个长方形,宽高比变为1:3。
        conv7_2、conv8_2、conv9_2我们这里就不再计算了,相信大家看完以上应该明白了如何计算,具体实现的步骤请大家参考脚本prior_box_layer.cpp。这就是我们先验框的计算方式。
       另外先验框与ground truth框的匹配通过函数CHECK_GT()函数实现,具体在bbox_util.cpp脚本中实现
 
 
-------------------------------------------------

然后移除dropout层和fc8层,并新增一系列卷积层,在检测数据集上做finetuing。

其中VGG16中的Conv4_3层将作为用于检测的第一个特征图。conv4_3层特征图大小是 [公式] ,但是该层比较靠前,其norm较大,所以在其后面增加了一个L2 Normalization层(参见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个特征图,其大小分别是 [公式] ,但是不同特征图设置的先验框数目不同(同一个特征图上每个单元设置的先验框是相同的,这里的数目指的是一个单元的先验框数目)。先验框的设置,包括尺度(或者说大小)和长宽比两个方面。对于先验框的尺度,其遵守一个线性递增规则:随着特征图大小降低,先验框尺度线性增加:

[公式]

其中 [公式] 指的特征图个数,但却是 [公式] ,因为第一层(Conv4_3层)是单独设置的, [公式] 表示先验框大小相对于图片的比例,而 [公式] 和 [公式] 表示比例的最小值与最大值,paper里面取0.2和0.9。对于第一个特征图,其先验框的尺度比例一般设置为 [公式] ,那么尺度为 [公式] 。对于后面的特征图,先验框尺度按照上面公式线性增加,但是先将尺度比例先扩大100倍,此时增长步长为 [公式] ,这样各个特征图的 [公式] 为 [公式] ,将这些比例除以100,然后再乘以图片大小,可以得到各个特征图的尺度为 [公式] ,这种计算方式是参考SSD的Caffe源码。综上,可以得到各个特征图的先验框尺度 [公式] 。对于长宽比,一般选取 [公式] ,对于特定的长宽比,按如下公式计算先验框的宽度与高度(后面的 [公式] 均指的是先验框实际尺度,而不是尺度比例):

[公式]

默认情况下,每个特征图会有一个 [公式] 且尺度为 [公式] 的先验框,除此之外,还会设置一个尺度为 [公式] 且 [公式] 的先验框,这样每个特征图都设置了两个长宽比为1但大小不同的正方形先验框。注意最后一个特征图需要参考一个虚拟 [公式] 来计算 [公式] 。因此,每个特征图一共有 [公式] 个先验框 [公式] ,但是在实现时,Conv4_3,Conv10_2和Conv11_2层仅使用4个先验框,它们不使用长宽比为 [公式] 的先验框。每个单元的先验框的中心点分布在各个单元的中心,即 [公式] ,其中 [公式] 为特征图的大小。

得到了特征图之后,需要对特征图进行卷积得到检测结果,图7给出了一个 [公式] 大小的特征图的检测过程。其中Priorbox是得到先验框,前面已经介绍了生成规则。检测值包含两个部分:类别置信度和边界框位置,各采用一次 [公式] 卷积来进行完成。令 [公式] 为该特征图所采用的先验框数目,那么类别置信度需要的卷积核数量为 [公式] ,而边界框位置需要的卷积核数量为 [公式] 。由于每个先验框都会预测一个边界框,所以SSD300一共可以预测 [公式] 个边界框,这是一个相当庞大的数字,所以说SSD本质上是密集采样。

图7 基于卷积得到检测结果

训练过程

原文地址:https://www.cnblogs.com/skydaddy/p/12022652.html