PyTorch实现论文细节笔记

SSD算法:

1 默认框的产生

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。这就是我们先验框的计算方式。

转载自: 关于SSD默认框产生的详细解读 https://blog.csdn.net/xunan003/article/details/79186162

2 正负样本获得

正样本获得
我们已经在图上画出了prior box,同时也有了ground truth,那么下一步就是将prior box匹配到ground truth上,这是在 src/caffe/utlis/bbox_util.cpp的 FindMatches以及子函数MatchBBox函数里完成的。值得注意的是先是从groudtruth box出发给每个groudtruth box找到了最匹配的prior box放入候选正样本集,然后再从prior box出发为prior box集中寻找与groundtruth box满足IOU>0.5的一个IOU最大的prior box(如果有的话)放入候选正样本集,这样显然就增大了候选正样本集的数量。

负样本获得
在生成一系列的 prior boxes 之后,会产生很多个符合 ground truth box 的 positive boxes(候选正样本集),但同时,不符合 ground truth boxes 也很多,而且这个 negative boxes(候选负样本集),远多于 positive boxes。这会造成 negative boxes、positive boxes 之间的不均衡。训练时难以收敛。

因此,本文采取,先将每一个物体位置上对应 predictions(prior boxes)loss 进行排序。 对于候选正样本集:选择最高的几个prior box与正样本集匹配(box索引同时存在于这两个集合里则匹配成功),匹配不成功则删除这个正样本(因为这个正样本不在难例里已经很接近ground truth box了,不需要再训练了);对于候选负样本集:选择最高的几个prior box与候选负样本集匹配,匹配成功则作为负样本。

这就是一个难例挖掘的过程,举个例子,假设在这8732个prior box里,经过FindMatches后得到候选正样本P个,候选负样本那就有8732-P个。将prior box的prediction loss按照从大到小顺序排列后选择最高的M个prior box。如果这P个候选正样本里有a个box不在这M个prior box里,将这M个box从候选正样本集中踢出去。如果这8732-P个候选负样本集中包含的8732-P有M-a个在这M个prior box,则将这M-a个候选负样本作为负样本。SSD算法中通过这种方式来保证 positives、negatives 的比例。实际代码中有三种负样本挖掘方式:

如果选择HARD_EXAMPLE方式(源于论文Training Region-based Object Detectors with Online Hard Example Mining),则默认M = 64,由于无法控制正样本数量,这种方式就有点类似于分类、回归按比重不同交替训练了。

如果选择MAX_NEGATIVE方式,则M = P*neg_pos_ratio,这里当neg_pos_ratio = 3的时候,就是论文中的正负样本比例1:3了。

3 损失函数

训练过程中的 prior boxes 和 ground truth boxes 的匹配,基本思路是:让每一个 prior box 回归并且到 ground truth box,这个过程的调控我们需要损失层的帮助,他会计算真实值和预测值之间的误差,从而指导学习的走向。

SSD 训练的目标函数(training objective)源自于 MultiBox 的目标函数,但是本文将其拓展,使其可以处理多个目标类别。具体过程是我们会让每一个 prior box 经过Jaccard系数计算和真实框的相似度,阈值只有大于 0.5的才可以列为候选名单;假设选择出来的是N个匹配度高于百分之五十的框吧,我们令 i 表示第 i个默认框,j表示第 j个真实框,p表示第p个类。那么x_{ij}^p 表示 第 i 个 prior box 与 类别 p 的 第 j 个 ground truth box 相匹配的Jaccard系数,若不匹配的话,则x_{ij}^p=0。总的目标损失函数(objective loss function)就由 localization loss(loc) 与 confidence loss(conf) 的加权求和:

N 是与 ground truth box 相匹配的 prior boxes 个数

localization loss(loc) 是 Fast R-CNN 中 Smooth L1 Loss,用在 predict box(l) 与 ground truth box(g) 参数(即中心坐标位置,width、height)中,回归 bounding boxes 的中心位置,以及 width、height

confidence loss(conf) 是 Softmax Loss,输入为每一类的置信度 c

权重项 α,可在protxt中设置 loc_weight,默认设置为 1
https://blog.csdn.net/xunan003/article/details/79086642

Faceboxes

1 特征图尺寸和感受野计算

https://zhuanlan.zhihu.com/p/56940729

发布了270 篇原创文章 · 获赞 344 · 访问量 65万+

猜你喜欢

转载自blog.csdn.net/shanglianlm/article/details/103498783
今日推荐