原文:http://blog.csdn.net/a8039974/article/details/77592395,

  http://blog.csdn.net/jesse_mx/article/details/74011886

另外一篇很详细的解析:https://www.cnblogs.com/xuanyuyt/p/7222867.html

SSD github : https://github.com/weiliu89/caffe/tree/ssd

SSD paper : https://arxiv.org/abs/1512.02325

SSD eccv2016 slide pdf : http://download.csdn.NET/download/zy1034092330/9940054

SSD pose estimation paper : http://download.csdn.net/download/zy1034092330/9940059

图1

缩进SSD,全称Single Shot MultiBox Detector,是Wei Liu在ECCV 2016上提出的一种目标检测算法,截至目前是主要的检测框架之一,相比Faster RCNN有明显的速度优势,相比YOLO又有明显的mAP优势(不过已经被CVPR 2017的YOLO9000超越)。SSD具有如下主要特点:

  1. 从YOLO中继承了将detection转化为regression的思路,同时一次即可完成网络训练
  2. 基于Faster RCNN中的anchor,提出了相似的prior box;
  3. 加入基于特征金字塔(Pyramidal Feature Hierarchy)的检测方式,相当于半个FPN思路

本文接下来都以SSD 300为例进行分析。

1 SSD网络结构

图2 SSD网络结构(和代码貌似有点差别)

缩进上图2是原论文中的SSD 300网络结构图。可以看到YOLO在卷积层后接全连接层,即检测时只利用了最高层feature maps(包括Faster RCNN也是如此);而SSD采用了特征金字塔结构进行检测,即检测时利用了conv4-3,conv-7(FC7),conv6-2,conv7-2,conv8_2,conv9_2这些大小不同的feature maps,在多个feature maps上同时进行softmax分类和位置回归,如图3。

图3 单层feature map预测和特征金字塔预测对比

下面以VGGNet-SSD为例,结合prototxt源文件说一下网络结构。

数据层

这里的数据层类型是AnnotatedData,主要注意下lmdb的source,batch_size以及label_map_file即可。

# 数据层
name: "VGG_VOC0712_SSD_300x300_train"
layer {
name: "data"
type: "AnnotatedData"
top: "data"
top: "label"
include {
phase: TRAIN
}
transform_param {
mirror: true
mean_value:
mean_value:
mean_value:
resize_param {
prob:
resize_mode: WARP
height:
width:
interp_mode: LINEAR
interp_mode: AREA
interp_mode: NEAREST
interp_mode: CUBIC
interp_mode: LANCZOS4
}
emit_constraint {
emit_type: CENTER
}
distort_param {
brightness_prob: 0.5
brightness_delta:
contrast_prob: 0.5
contrast_lower: 0.5
contrast_upper: 1.5
hue_prob: 0.5
hue_delta:
saturation_prob: 0.5
saturation_lower: 0.5
saturation_upper: 1.5
random_order_prob: 0.0
}
expand_param {
prob: 0.5
max_expand_ratio: 4.0
}
}
data_param {
source: "examples/VOC0712/VOC0712_trainval_lmdb"
batch_size:
backend: LMDB
}
annotated_data_param {
batch_sampler {
max_sample:
max_trials:
}
batch_sampler {
sampler {
min_scale: 0.3
max_scale: 1.0
min_aspect_ratio: 0.5
max_aspect_ratio: 2.0
}
sample_constraint {
min_jaccard_overlap: 0.1
}
max_sample:
max_trials:
}
batch_sampler {
sampler {
min_scale: 0.3
max_scale: 1.0
min_aspect_ratio: 0.5
max_aspect_ratio: 2.0
}
sample_constraint {
min_jaccard_overlap: 0.3
}
max_sample:
max_trials:
}
batch_sampler {
sampler {
min_scale: 0.3
max_scale: 1.0
min_aspect_ratio: 0.5
max_aspect_ratio: 2.0
}
sample_constraint {
min_jaccard_overlap: 0.5
}
max_sample:
max_trials:
}
batch_sampler {
sampler {
min_scale: 0.3
max_scale: 1.0
min_aspect_ratio: 0.5
max_aspect_ratio: 2.0
}
sample_constraint {
min_jaccard_overlap: 0.7
}
max_sample:
max_trials:
}
batch_sampler {
sampler {
min_scale: 0.3
max_scale: 1.0
min_aspect_ratio: 0.5
max_aspect_ratio: 2.0
}
sample_constraint {
min_jaccard_overlap: 0.9
}
max_sample:
max_trials:
}
batch_sampler {
sampler {
min_scale: 0.3
max_scale: 1.0
min_aspect_ratio: 0.5
max_aspect_ratio: 2.0
}
sample_constraint {
max_jaccard_overlap: 1.0
}
max_sample:
max_trials:
}
label_map_file: "data/VOC0712/labelmap_voc.prototxt"
}
}

特征提取网络

特征提取网络由两部分构成,VGGNet和新增的8个卷积层,要点有三处。

第一,这个VGGNet不同于原版,是修改过的,全名叫VGG_ILSVRC_16_layers_fc_reduced是作者另一篇论文ParseNet: Looking Wider to See Better的成果,然后被用到了SSD中。

第二,为什么要新增这么8个卷积层?因为在300x300输入下,conv4_3的分辨率为38x38,而到fc7层,也还有19x19,那就必须新增一些卷积层,以产生更多低分辨率的层,方便进行多层特征融合。最终作者选择了6个卷积层作为bottom,分辨率如下图所示,然后依次接上若干检测层。

卷积层 分辨率
conv4_3_norm 38x38
f7 19x19
conv6_2 10x10
conv7_2 5x5
conv8_2 3x3
conv9_2 1x1

第三,只有conv4_3加入了norm,这是为什么呢?原因仍然在论文中ParseNet中,大概是说,因为conv4_3的scale(尺度?)和其它层相比不同,才要加入norm使其均衡一些。

# 特征提取网络
layer {
name: "conv1_1"
type: "Convolution"
bottom: "data"
top: "conv1_1"
param {
lr_mult:
decay_mult:
}
param {
lr_mult:
decay_mult:
}
convolution_param {
num_output:
pad:
kernel_size:
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
value:
}
}
}
#
# 省略很多层
#
layer {
name: "fc7"
type: "Convolution"
bottom: "fc6"
top: "fc7"
param {
lr_mult:
decay_mult:
}
param {
lr_mult:
decay_mult:
}
convolution_param {
num_output:
kernel_size:
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
value:
}
}
}
layer {
name: "relu7"
type: "ReLU"
bottom: "fc7"
top: "fc7"
} # 到此是VGG网络层,从conv1到fc7 layer {
name: "conv6_1"
type: "Convolution"
bottom: "fc7"
top: "conv6_1"
param {
lr_mult:
decay_mult:
}
param {
lr_mult:
decay_mult:
}
convolution_param {
num_output:
pad:
kernel_size:
stride:
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
value:
}
}
}
#
# 省略很多层
#
layer {
name: "conv9_2_relu"
type: "ReLU"
bottom: "conv9_2"
top: "conv9_2" # 到此是新增的8层卷积层,从conv6_1到conv9_2

多层融合检测网络

这一部分,作者选择了6个层作为bottom,在其上连接了一系列的检测相关层,这里以f7层为例,看看它作为bottom,连接了那些层。首先是loc相关层,包括fc7_mbox_loc,fc7_mbox_loc_perm和fc7_mbox_loc_flat。

# mbox_loc层,预测box的坐标,其中24=(default box数量)x4(四个坐标)
layer {
name: "fc7_mbox_loc"
type: "Convolution"
bottom: "fc7"
top: "fc7_mbox_loc"
param {
lr_mult:
decay_mult:
}
param {
lr_mult:
decay_mult:
}
convolution_param {
num_output:
pad:
kernel_size:
stride:
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
value:
}
}
}
# mbox_loc_perm层,将上一层产生的mbox_loc重新排序
layer {
name: "fc7_mbox_loc_perm"
type: "Permute"
bottom: "fc7_mbox_loc"
top: "fc7_mbox_loc_perm"
permute_param {
order:
order:
order:
order:
}
}
# mbox_loc_flat层,将perm层展平(例如将7x7展成1x49),方便拼接
layer {
name: "fc7_mbox_loc_flat"
type: "Flatten"
bottom: "fc7_mbox_loc_perm"
top: "fc7_mbox_loc_flat"
flatten_param {
axis:
}
}

然后是conf相关层,包括fc7_mbox_conf,fc7_mbox_conf_perm和fc7_mbox_conf_flat。

# mbox_conf层,预测box的类别置信度,=(default box数量)x21(总类别数)
layer {
name: "fc7_mbox_conf"
type: "Convolution"
bottom: "fc7"
top: "fc7_mbox_conf"
param {
lr_mult:
decay_mult:
}
param {
lr_mult:
decay_mult:
}
convolution_param {
num_output:
pad:
kernel_size:
stride:
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
value:
}
}
}
# box_conf_perm层,将上一层产生的mbox_conf重新排序
layer {
name: "fc7_mbox_conf_perm"
type: "Permute"
bottom: "fc7_mbox_conf"
top: "fc7_mbox_conf_perm"
permute_param {
order:
order:
order:
order:
}
}
# mbox_conf_flat层,将perm层展平,方便拼接
layer {
name: "fc7_mbox_conf_flat"
type: "Flatten"
bottom: "fc7_mbox_conf_perm"
top: "fc7_mbox_conf_flat"
flatten_param {
axis:
}
}

最后是priorbox层,就只有fc7_mbox_priorbox

# mbox_priorbox层,根据标注信息,经过转换,在cell中产生box真实值
layer {
name: "fc7_mbox_priorbox"
type: "PriorBox"
bottom: "fc7"
bottom: "data"
top: "fc7_mbox_priorbox"
prior_box_param {
min_size: 60.0
max_size: 111.0 # priorbox的上下界为60~111
# first prior: aspect_ratio = 1, box_width = box_height = min_size_; 
# second prior: aspect_ratio = 1, box_width = box_height = sqrt(min_size * max_size);
# others: box_width = min_size_ * sqrt(aspect_ratio); box_height = min_size_ / sqrt(aspect_ratio); 然后对称交换宽高
# 具体详见prior_box_layer.cpp文件
    aspect_ratio:
aspect_ratio: # 共同定义default box数量为6
flip: true
clip: false
variance: 0.1
variance: 0.1
variance: 0.2
variance: 0.2
step: # 不同层的值不同
offset: 0.5
}
}

各个值的计算,详见ssd_pascal.py

mbox_source_layers = ['conv4_3', 'fc7', 'conv6_2', 'conv7_2', 'conv8_2', 'conv9_2']
# in percent %
min_ratio =
max_ratio =
step = int(math.floor((max_ratio - min_ratio) / (len(mbox_source_layers) - )))
min_sizes = []
max_sizes = []
for ratio in xrange(min_ratio, max_ratio + , step):
min_sizes.append(min_dim * ratio / .)
max_sizes.append(min_dim * (ratio + step) / .)
min_sizes = [min_dim * / .] + min_sizes
max_sizes = [min_dim * / .] + max_sizes
steps = [, , , , , ]
aspect_ratios = [[], [, ], [, ], [, ], [], []]

下面做一个表格统计一下多层融合检测层的相关重要参数:

bottom层 conv4_3 fc7 conv6_2 conv7_2 conv8_2 conv9_2
分辨率 38x38 19x19 10x10 5x5 3x3 1x1
default box数量 4 6 6 6 4 4
aspect_ratio 2 2,3 2,3 2,3 2 2
steps 8 16 32 64 100 300
loc层num_output 16 24 24 24 16 16
conf层num_output 84 126 126 126 84 84
priorbox层min max size 30~60 60~111 111~162 162~213 213~264 264~315
offset 0.5 0.5 0.5 0.5 0.5 0.5
variance 0.1,0.1,0.2,0.2 0.1,0.1,0.2,0.2 0.1,0.1,0.2,0.2 0.1,0.1,0.2,0.2 0.1,0.1,0.2,0.2 0.1,0.1,0.2,0.2

具体到每一个feature map上获得prior box时,会从这6种中进行选择。如下表和图所示最后会得到(38*38*4 + 19*19*6 + 10*10*6 + 5*5*6 + 3*3*4 + 1*1*4)= 8732个prior box

这里需要添加3个concat层,分别拼接所有的loc层,conf层以及priorbox层。

# mbox_loc层,拼接6个loc_flat层
layer {
name: "mbox_loc"
type: "Concat"
bottom: "conv4_3_norm_mbox_loc_flat"
bottom: "fc7_mbox_loc_flat"
bottom: "conv6_2_mbox_loc_flat"
bottom: "conv7_2_mbox_loc_flat"
bottom: "conv8_2_mbox_loc_flat"
bottom: "conv9_2_mbox_loc_flat"
top: "mbox_loc"
concat_param {
axis:
}
}
# mbox_conf层,拼接6个conf_flat层
layer {
name: "mbox_conf"
type: "Concat"
bottom: "conv4_3_norm_mbox_conf_flat"
bottom: "fc7_mbox_conf_flat"
bottom: "conv6_2_mbox_conf_flat"
bottom: "conv7_2_mbox_conf_flat"
bottom: "conv8_2_mbox_conf_flat"
bottom: "conv9_2_mbox_conf_flat"
top: "mbox_conf"
concat_param {
axis:
}
}
# mbox_priorbox层,拼接6个mbox_priorbox层
layer {
name: "mbox_priorbox"
type: "Concat"
bottom: "conv4_3_norm_mbox_priorbox"
bottom: "fc7_mbox_priorbox"
bottom: "conv6_2_mbox_priorbox"
bottom: "conv7_2_mbox_priorbox"
bottom: "conv8_2_mbox_priorbox"
bottom: "conv9_2_mbox_priorbox"
top: "mbox_priorbox"
concat_param {
axis:
}
}

损失层

损失层类型是MultiBoxLoss,这也是作者自己写的,在smooth_L1损失层基础上修改。损失层的bottom是三个concat层以及data层中的label,参数一般不需要改,注意其中的num_classes即可。

# 损失层
layer {
name: "mbox_loss"
type: "MultiBoxLoss"
bottom: "mbox_loc"
bottom: "mbox_conf"
bottom: "mbox_priorbox"
bottom: "label"
top: "mbox_loss"
include {
phase: TRAIN
}
propagate_down: true
propagate_down: true
propagate_down: false
propagate_down: false
loss_param {
normalization: VALID
}
multibox_loss_param {
loc_loss_type: SMOOTH_L1
conf_loss_type: SOFTMAX
loc_weight: 1.0
num_classes:
share_location: true
match_type: PER_PREDICTION
overlap_threshold: 0.5
use_prior_for_matching: true
background_label_id:
use_difficult_gt: true
neg_pos_ratio: 3.0
neg_overlap: 0.5
code_type: CENTER_SIZE
ignore_cross_boundary_bbox: false
mining_type: MAX_NEGATIVE
}
}

正负样本选择

使用将prior box 和 grount truth box 按照IOU(JaccardOverlap)进行匹配,匹配成功则这个prior box就是positive example(正样本),如果匹配不上,就是negative example(负样本),显然这样产生的负样本的数量要远远多于正样本。这里将前向loss进行排序,选择最高的num_sel个prior box序号集合 DD。那么如果Match成功后的正样本序号集合PP。那么最后正样本集为 P−D∩PP−D∩P,负样本集为 D−D∩PD−D∩P,这里利用num_sel控制最后正、负样本的比例在 1:3 左右。

如果选择HARD_EXAMPLE方式(源于论文Training Region-based Object Detectors with Online Hard Example Mining),则默认M=,由于无法控制正样本数量,这种方式就有点类似于分类、回归按比重不同交替训练了。
如果选择MAX_NEGATIVE方式,则M=P∗neg_pos_ratio,这里当neg_pos_ratio=3的时候,就是论文中的正负样本比例1:3了。

2 Prior Box

缩进在SSD中引入了Prior Box,实际上与anchor非常类似,就是一些目标的预选框,后续通过softmax分类+bounding box regression获得真实目标的位置。SSD按照如下规则生成prior box:

  • 以feature map上每个点的中点为中心(offset=0.5),生成一些列同心的prior box(然后中心点的坐标会乘以step,相当于从feature map位置映射回原图位置)
  • 正方形prior box最小边长为,最大边长为:
  • 每在prototxt设置一个aspect ratio,会生成2个长方形,长宽为: 和 

图4 prior box

  • 而每个feature map对应prior box的min_size和max_size由以下公式决定,公式中m是使用feature map的数量(SSD 300中m=6):

第一层feature map对应的min_size=S1,max_size=S2;第二层min_size=S2,max_size=S3;其他类推。在原文中,Smin=0.2,Smax=0.9,但是在SSD 300中prior box设置并不能和paper中上述公式对应:

  min_size max_size
conv4_3
30
60
fc7
60
111
conv6_2
111
162
conv7_2
162
213
conv8_2
213
264
conv9_2 
264
315

不过依然可以看出,SSD使用低层feature map检测小目标,使用高层feature map检测大目标,这也应该是SSD的突出贡献了。其中SSD 300在conv4_3生成prior box的conv4_3_norm_priorbox层prototxt定义如下:

  1. layer {
  2. name: "conv4_3_norm_mbox_priorbox"
  3. type: "PriorBox"
  4. bottom: "conv4_3_norm"
  5. bottom: "data"
  6. top: "conv4_3_norm_mbox_priorbox"
  7. prior_box_param {
  8. min_size: 30.0
  9. max_size: 60.0
  10. aspect_ratio: 2
  11. flip: true
  12. clip: false
  13. variance: 0.1
  14. variance: 0.1
  15. variance: 0.2
  16. variance: 0.2
  17. step: 8
  18. offset: 0.5
  19. }
  20. }

知道了priorbox如何产生,接下来分析prior box如何使用。这里以conv4_3为例进行分析。

图5

从图5可以看到,在conv4_3 feature map网络pipeline分为了3条线路:

  • 经过一次batch norm+一次卷积后,生成了[1, num_class*num_priorbox, layer_height, layer_width]大小的feature用于softmax分类目标和非目标(其中num_class是目标类别,SSD 300中num_class = 21)
  • 经过一次batch norm+一次卷积后,生成了[1, 4*num_priorbox, layer_height, layer_width]大小的feature用于bounding box regression(即每个点一组[dxmin,dymin,dxmax,dymax],参考Faster RCNN 2.5节)
  • 生成了[1, 2, 4*num_priorbox]大小的prior box blob,其中2个channel分别存储prior box的4个点坐标和对应的4个variance

缩进后续通过softmax分类+bounding box regression即可从priox box中预测到目标,熟悉Faster RCNN的读者应该对上述过程应该并不陌生。其实pribox box的与Faster RCNN中的anchor非常类似,都是目标的预设框,没有本质的差异。区别是每个位置的prior box一般是4~6个,少于Faster RCNN默认的9个anchor;同时prior box是设置在不同尺度的feature maps上的,而且大小不同。

缩进还有一个细节就是上面prototxt中的4个variance,这实际上是一种bounding regression中的权重。在图4线路(2)中,网络输出[dxmin,dymin,dxmax,dymax],即对应下面代码中bbox;然后利用如下方法进行针对prior box的位置回归:

  1. decode_bbox->set_xmin(
  2. prior_bbox.xmin() + prior_variance[0] * bbox.xmin() * prior_width);
  3. decode_bbox->set_ymin(
  4. prior_bbox.ymin() + prior_variance[1] * bbox.ymin() * prior_height);
  5. decode_bbox->set_xmax(
  6. prior_bbox.xmax() + prior_variance[2] * bbox.xmax() * prior_width);
  7. decode_bbox->set_ymax(
  8. prior_bbox.ymax() + prior_variance[3] * bbox.ymax() * prior_height);

上述代码可以在SSD box_utils.cpp的void DecodeBBox()函数见到。

3 Permute,Flatten And Concat Layers

图6

缩进上一节以conv4_3 feature map分析了如何检测到目标的真实位置,但是SSD 300是使用包括conv4_3在内的共计6个feature maps一同检测出最终目标的。在网络运行的时候显然不能像图6一样:一个feature map单独计算一次softmax socre+box regression(虽然原理如此,但是不能如此实现)。那么多个feature maps如何协同工作?这时候就要用到Permute,Flatten和Concat这3种层了。其中conv4_3_norm_conf_perm的prototxt定义如下:

  1. layer {
  2. name: "conv4_3_norm_mbox_conf_perm"
  3. type: "Permute"
  4. bottom: "conv4_3_norm_mbox_conf"
  5. top: "conv4_3_norm_mbox_conf_perm"
  6. permute_param {
  7. order: 0
  8. order: 2
  9. order: 3
  10. order: 1
  11. }
  12. }

Permute是SSD中自带的层,上面conv4_3_norm_mbox_conf_perm的的定义。Permute相当于交换caffe blob中的数据维度。在正常情况下caffe blob的顺序为:

bottom blob = [batch_num, channel, height, width]

经过conv4_3_norm_mbox_conf_perm后的caffe blob为:

top blob = [batch_num, height, width, channel]

而Flattlen和Concat层都是caffe自带层,请参照caffe official documentation理解。

图7 SSD中部分层caffe blob shape变化

缩进那么接下来以conv4_3和fc7为例分析SSD是如何将不同size的feature map组合在一起进行prediction。图7展示了conv4_3和fc7合并在一起的过程中caffe blob shape变化(其他层类似,考虑到图片大小没有画出来,请脑补)。

  • 对于conv4_3 feature map,conv4_3_norm_priorbox(priorbox层)设置了每个点共有4个prior box。由于SSD 300共有21个分类,所以conv4_3_norm_mbox_conf的channel值为num_priorbox * num_class = 4 * 21 = 84;而每个prior box都要回归出4个位置变换量,所以conv4_3_norm_mbox_loc的caffe blob channel值为4 * 4 = 16。
  • fc7每个点有6个prior box,其他feature map同理。
  • 经过一系列图7展示的caffe blob shape变化后,最后拼接成mbox_conf和mbox_loc。而mbox_conf后接reshape,再进行softmax(为何在softmax前进行reshape,Faster RCNN有提及)。
  • 最后这些值输出detection_out_layer,获得检测结果

4 SSD网络结构优劣分析

缩进SSD算法的优点应该很明显:运行速度可以和YOLO媲美,检测精度可以和Faster RCNN媲美。除此之外,还有一些鸡毛蒜皮的优点,不解释了。这里谈谈缺点:

  1. 需要人工设置prior box的min_size,max_size和aspect_ratio值。网络中prior box的基础大小和形状不能直接通过学习获得,而是需要手工设置。而网络中每一层feature使用的prior box大小和形状恰好都不一样,导致调试过程非常依赖经验。
  2. 虽然采用了pyramdial feature hierarchy的思路,但是对小目标的recall依然一般,并没有达到碾压Faster RCNN的级别。作者认为,这是由于SSD使用conv4_3低级feature去检测小目标,而低级特征卷积层数少,存在特征提取不充分的问题。

5 SSD训练过程

缩进对于SSD,虽然paper中指出采用了所谓的“multibox loss”,但是依然可以清晰看到SSD loss分为了confidence loss和location loss两部分,其中N是match到GT(Ground Truth)的prior box数量;而α参数用于调整confidence loss和location loss之间的比例,默认α=1。SSD中的confidence loss是典型的softmax loss:

其中代表第i个prior box匹配到了第j个class为p类别的GT box;而location loss是典型的smooth L1 loss:

Matching strategy:

缩进在训练时,groundtruth boxes 与 default boxes(就是prior boxes) 按照如下方式进行配对:

  • 首先,寻找与每一个ground truth box有最大的jaccard overlap的default box,这样就能保证每一个groundtruth box与唯一的一个default box对应起来(所谓的jaccard overlap就是IoU,如图8)。
  • SSD之后又将剩余还没有配对的default box与任意一个groundtruth box尝试配对,只要两者之间的jaccard overlap大于阈值,就认为match(SSD 300 阈值为0.5)。
  • 显然配对到GT的default box就是positive,没有配对到GT的default box就是negative。

图8 jaccard overlap

Hard negative mining:
缩进值得注意的是,一般情况下negative default boxes数量>>positive default boxes数量,直接训练会导致网络过于重视负样本,从而loss不稳定。所以需要采取:
  • 所以SSD在训练时会依据confidience score排序default box,挑选其中confidience高的box进行训练,控制positive:negative=1:3

Data augmentation:

缩进数据增广,即每一张训练图像,随机的进行如下几种选择:

  • 使用原始的图像
  • 采样一个 patch,与物体之间最小的 jaccard overlap 为:0.1,0.3,0.5,0.7 或 0.9
  • 随机的采样一个 patch
采样的 patch 是原始图像大小比例是[0.1,1],aspect ratio在1/2与2之间。当 groundtruth box 的 中心(center)在采样的patch中时,保留重叠部分。在这些采样步骤之后,每一个采样的patch被resize到固定的大小,并且以0.5的概率随机的 水平翻转(horizontally flipped)。

其实Matching strategy,Hard negative mining,Data augmentation,都是为了加快网络收敛而设计的。尤其是Data augmentation,翻来覆去的randomly crop,保证每一个prior box都获得充分训练而已。不过当数据达到一定量的时候,不建议再进行Data augmentation,毕竟“真”的数据比“假”数据还是要好很多。

以ResNet作为前缀网络的SSD

http://blog.csdn.net/zhangjunbob/article/details/53119959

前面的部分直接使用ResNet网络即可,并且可以事先使用对应的数据集对ResNet网络进行预训练,后续直接将其接入SSD框架中,下面讲的是将ResNet50接入SSD中,对应的接入部分列出如下:

//至此resnet主体结构完成,随后接上ssd的结构

//用pool5作为bottom分别产生mbox_loc/mbox_conf/mbox_priorbox

layer {
name: "pool5_mbox_loc"
type: "Convolution"
bottom: "pool5" //选取pool5作为bottom,产生mbox_loc
top: "pool5_mbox_loc"
param {
lr_mult:
decay_mult:
}
param {
lr_mult:
decay_mult:
}
convolution_param {
num_output:
pad:
kernel_size:
stride:
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
value:
}
}
}
layer {
name: "pool5_mbox_loc_perm" //将上一层产生的mbox_loc重新排序
type: "Permute"
bottom: "pool5_mbox_loc"
top: "pool5_mbox_loc_perm"
permute_param {
order:
order:
order:
order:
}
}
layer {
name: "pool5_mbox_loc_flat" //将上一层展平(例如7*7的展平成1*49,方便之后的拼接)
type: "Flatten"
bottom: "pool5_mbox_loc_perm"
top: "pool5_mbox_loc_flat"
flatten_param {
axis:
}
}
layer {
name: "pool5_mbox_conf"
type: "Convolution"
bottom: "pool5" //选取pool5作为bottom,产生mbox_conf(之后的排序展平同理)
top: "pool5_mbox_conf"
param {
lr_mult:
decay_mult:
}
param {
lr_mult:
decay_mult:
}
convolution_param {
num_output:
pad:
kernel_size:
stride:
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
value:
}
}
}
layer {
name: "pool5_mbox_conf_perm"
type: "Permute"
bottom: "pool5_mbox_conf"
top: "pool5_mbox_conf_perm"
permute_param {
order:
order:
order:
order:
}
}
layer {
name: "pool5_mbox_conf_flat"
type: "Flatten"
bottom: "pool5_mbox_conf_perm"
top: "pool5_mbox_conf_flat"
flatten_param {
axis:
}
}
layer {
name: "pool5_mbox_priorbox"
type: "PriorBox"
bottom: "pool5" //选取pool5作为bottom,产生mbox_priorbox(之后排序展平)
bottom: "data"
top: "pool5_mbox_priorbox"
prior_box_param {
min_size: 276.0
max_size: 330.0
aspect_ratio:
aspect_ratio:
flip: true
clip: true
variance: 0.1
variance: 0.1
variance: 0.2
variance: 0.2
}
} //同理用res5c作为bottom分别产生mbox_loc/mbox_conf/mbox_priorbox layer {
name: "res5c_mbox_loc"
type: "Convolution"
bottom: "res5c"
top: "res5c_mbox_loc"
param {
lr_mult:
decay_mult:
}
param {
lr_mult:
decay_mult:
}
convolution_param {
num_output:
pad:
kernel_size:
stride:
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
value:
}
}
}
layer {
name: "res5c_mbox_loc_perm"
type: "Permute"
bottom: "res5c_mbox_loc"
top: "res5c_mbox_loc_perm"
permute_param {
order:
order:
order:
order:
}
}
layer {
name: "res5c_mbox_loc_flat"
type: "Flatten"
bottom: "res5c_mbox_loc_perm"
top: "res5c_mbox_loc_flat"
flatten_param {
axis:
}
}
layer {
name: "res5c_mbox_conf"
type: "Convolution"
bottom: "res5c"
top: "res5c_mbox_conf"
param {
lr_mult:
decay_mult:
}
param {
lr_mult:
decay_mult:
}
convolution_param {
num_output:
pad:
kernel_size:
stride:
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
value:
}
}
}
layer {
name: "res5c_mbox_conf_perm"
type: "Permute"
bottom: "res5c_mbox_conf"
top: "res5c_mbox_conf_perm"
permute_param {
order:
order:
order:
order:
}
}
layer {
name: "res5c_mbox_conf_flat"
type: "Flatten"
bottom: "res5c_mbox_conf_perm"
top: "res5c_mbox_conf_flat"
flatten_param {
axis:
}
}
layer {
name: "res5c_mbox_priorbox"
type: "PriorBox"
bottom: "res5c"
bottom: "data"
top: "res5c_mbox_priorbox"
prior_box_param {
min_size: 276.0
max_size: 330.0
aspect_ratio:
aspect_ratio:
flip: true
clip: true
variance: 0.1
variance: 0.1
variance: 0.2
variance: 0.2
}
} //Concat层将刚才的res5c和pool5产生的mbox_loc/mbox_conf/mbox_priorbox拼接起来形成一个层 layer {
name: "mbox_loc"
type: "Concat"
bottom: "res5c_mbox_loc_flat"
bottom: "pool5_mbox_loc_flat"
top: "mbox_loc"
concat_param {
axis:
}
}
layer {
name: "mbox_conf"
type: "Concat"
bottom: "res5c_mbox_conf_flat"
bottom: "pool5_mbox_conf_flat"
top: "mbox_conf"
concat_param {
axis:
}
}
layer {
name: "mbox_priorbox"
type: "Concat"
bottom: "res5c_mbox_priorbox"
bottom: "pool5_mbox_priorbox"
top: "mbox_priorbox"
concat_param {
axis:
}
} <span style="color:#ff0000;">//mbox_loc,mbox_conf,mbox_priorbox一起做的loss-function</span> layer {
name: "mbox_loss"
type: "MultiBoxLoss"
bottom: "mbox_loc"
bottom: "mbox_conf"
bottom: "mbox_priorbox"
bottom: "label"
top: "mbox_loss"
include {
phase: TRAIN
}
propagate_down: true
propagate_down: true
propagate_down: false
propagate_down: false
loss_param {
normalization: VALID
}
multibox_loss_param {
loc_loss_type: SMOOTH_L1
conf_loss_type: SOFTMAX
loc_weight: 1.0
num_classes:
share_location: true
match_type: PER_PREDICTION
overlap_threshold: 0.5
use_prior_for_matching: true
background_label_id:
use_difficult_gt: true
do_neg_mining: true
neg_pos_ratio: 3.0
neg_overlap: 0.5
code_type: CENTER_SIZE
}
}

最新文章

  1. 信号量互斥,王明学learn
  2. 智能车学习(五)&mdash;&mdash; dac学习
  3. Codeforces Round #177 (Div. 1) 题解【ABCD】
  4. [IOS UIScrollView+PageControl]信息展示横幅
  5. 多路由器环境下路由器的入口IP地址及DHCP设置探讨
  6. DuiLib——第二篇UIBase
  7. Xcode 7 支持http请求info.plist设置
  8. 实例:用jQuery实现垂直和水平下拉 菜单
  9. Windows7下chm文件打不开
  10. Cookie和Session的原理和异同
  11. 【Python 02】计算机与程序设计
  12. srping的历史与哲学
  13. pycharm 安装和首次使用
  14. HDU 1385 Minimum Transport Cost (输出字典序最小路径)【最短路】
  15. curl 模拟请求
  16. vue angular 分别实现分页
  17. bind的封装
  18. jQuery事件处理(三)
  19. Python入门之Python的单例模式和元类
  20. onchange()事件的应用

热门文章

  1. 怎样用bat批量重命名文件夹和文件
  2. 如果$.ajax函数迟迟得不到响应,那么最有可能出错的地方是请求参数写错了
  3. HDU 2191悼念512汶川大地震遇难同胞——珍惜如今,感恩生活(多重背包)
  4. CSDN个人空间、问答频道停站维护公告
  5. 【原】使用StarUML画用例图
  6. Visual studio C++ 之空控制台工程添加文件并解决头文件包含问题
  7. rabbitMq视频教程
  8. the filename directory name or volume label syntax is incorrect
  9. Android编程之Fragment使用动画造成Unknown animation name: objectAnimator异常
  10. redis 使用内存超过maxmemory