date: 2018/08/06


论文:《SSD: Single Shot MultiBox Detector(2016)


网络结构

与分析MobileNets v1模型不同,分析框架我们先从整体入手。

SSD_architecture.png


vgg16_300.png


感受野和缺省框

bounding box.png


感受野

卷积是对特定小区域的特征提取,比如一张 的原图经过一定卷积操作之后得到 的特征图,特征图上的每个“像素点”其实对应原图的一个“感受野”,在这里一个“感受野”的大小为 也即 (实际上不可能有.5,是37还是38要看卷积过程是否padding)。


换句话说,特征图左上角的一个感受野其实是原图左上角一个 大小区域的一个特征抽取(或者说是这个区域的一个抽象化,融合了这个区域的信息)。


缺省框

论文里称为default box,而源码里称为prior box.


在SSD中,为每个向Detection贡献特征的特征图上的每个感受野分配若干不同大小和长宽比(aspect ratio)的绑定框(Bounding Box),比如在作者开源的源码中,这些绑定框是这样生成的——

参考SSD - PriorBoxLayer源码 | github

(其中, 都是用户指定的超参数,而且  是可选的)


  1. 首先有一个最小的绑定框,其尺寸为 ,绑定框中心相对感受野中心偏移
  2. 根据用户指定的长宽比列表 AR 生成若干绑定框
    对于某一特定长宽比 ,生成尺寸分别为 的绑定框,其框中心同1;
  3. 最后还生成一个尺寸为 的绑定框,其框中心同1;


事实上网络输出的预测框位置参数就是这每一个缺省框的中心点偏移量和长、宽偏移量(每个框共计4个参数)!

对于一个方框其实有两种不同的表示方法,都是四元组,即(中心点x坐标,中心点y坐标,长度,宽度)或(左上角x坐标,左下角x坐标,右下角x坐标,右下角y坐标)


Dmin和Dmax的设置建议

论文中也给出了超参数 的设置建议——


写成数学公式为:

其中,

,是第k个特征图的 参数;

,是设计好的最小归一化尺寸,即前述

,是设计好的最大归一化尺寸,即前述

m,是特征图的总数;

注意:上述“特征图”指的是对Detection有贡献的特征图,序号从浅层到深层递增


回过头再看看网络结构,注意从不同尺度融合特征信息的那一部分,用的都是一个  的卷积核,输出通道要么是 num_class + 4 的四倍要么是六倍,这里 num_class 就是最终分类的数量,数值 4 其实指的是预测框的位置参数的数量,而四倍或六倍指的是特征图上每个感受野对应的绑定框数量。

按照论文,浅层的分辨率比较高,所以使用了 AR={1,2,3} 的分辨率组合,对于  分别会生成两个绑定框,而 除了本身之外还会额外产生一个归一化尺寸为 的绑定框;而浅层分辨率较低,2或3的长宽比其实没太大区别或必要,所以只取了 AR={1,2}。


源码prototxt参数示例:

layer {
  name: "conv11_mbox_priorbox"
  type: "PriorBox"
  bottom: "conv11"  # bottom[0]:特征向量
  bottom: "data"    # bottom[1]:原始输入(主要提供宽、高参数)
  top: "conv11_mbox_priorbox"

  prior_box_param {
    min_size: 30.0      # 最小的基本Box大小(可根据论文计算)
    aspect_ratio: 2.0   # 所要用的长宽比(除了1.0)
    flip: true          # 是否以0.5的概率翻转
    clip: false         # 是否截断Box(若截断表示不允许Box超出图片范围)
    variance: 0.1       # xmin的偏差因子
    variance: 0.1       # ymin的偏差因子
    variance: 0.2       # xmax的偏差因子
    variance: 0.2       # ymax的偏差因子
    offset: 0.5         # Box中心相对于感受野中心的x、y偏移量
  }
}


训练技巧

框配对(Matching)

前边讲到了感受野和缺省框,我们现在看看这些缺省框是如何跟实际标注的真实框(Ground Truth, GT)配对起来的。


介绍一个重叠率计算公式,jaccard重叠率:


对于每个GT,


损失函数(MultiBoxLoss)

损失函数由位置损失 和分类损失 加权求和获得;

完整公式如下:

其中,

N,是匹配正样本的总量(如果N=0,则令L=0);

x、c,分别是分类的指示量和置信度;

l、g,分别是预测框和GT框;

,是位置损失的权重。


位置损失是预测框l和真实框g之间的Smooth L1损失,

其中,

,是指示量,当第i个匹配框和分类p的第j个真实框配对时值为1,否则为0;

cx、cy、w、h,分别是框的中心点x坐标、中心点y坐标,宽度、高度;

d,是绑定框(网络本身预设的绑定框);

l,是预测框(网络输出的加上预测偏移量的框);

g,是GT框(数据集标注的真实框)。


分类损失是分类置信度之间的softmax损失,


源码prototxt参数示例:

layer {
  name: "mbox_loss"
  type: "MultiBoxLoss"
  bottom: "mbox_loc"        # 用于预测位置的特征向量
  bottom: "mbox_conf"       # 用于预测分类的特征向量
  bottom: "mbox_priorbox"   # 若干PriorBox的输出连接
  bottom: "label"           # 训练用的标签
  top: "mbox_loss"
  include {
    phase: TRAIN
  }
  propagate_down: true      # bottom[0] : mbox_loc,需要训练(反向传播)
  propagate_down: true      # bottom[1] : mbox_conf,需要训练
  propagate_down: false     # bottom[2] : mbox_priorbox,不需要训练
  propagate_down: false     # bottom[3] : label,不需要训练
  # 损失计算参数组
  loss_param {
    normalization: VALID    # 损失的归一化方式
                            #    FULL:除以batch_size
                            #    VALID:除以有效的数量(排除ignore_label参数指定的标签)
                            #    NONE
  }
  # multibox损失参数组
  multibox_loss_param {
    loc_loss_type: SMOOTH_L1    # 位置预测的损失函数
    conf_loss_type: SOFTMAX     # 分类预测的损失函数
    loc_weight: 1.0             # loc_loss的权重,论文中的alpha
    num_classes: 12             # 输出类别
    share_location: true        # 是否让所有的预测框共享参数
    match_type: PER_PREDICTION  # 
    overlap_threshold: 0.5      # 重叠阈值(训练时超过该阈值的Box作为正样本)
    use_prior_for_matching: true    # 是否使用先验框(即前边是否有PriorBox)
    background_label_id: 0      # 背景(background)标签的id值(与labelmap.prototxt文件匹配)
    use_difficult_gt: true      # 是否使用difficult的Ground Truth(?)
    neg_pos_ratio: 3.0          # 负样本:正样本 的比例
    neg_overlap: 0.5            # 负样本阈值(低于该阈值的Box作为负样本)
    code_type: CENTER_SIZE      # bounding box的编码方式
    ignore_cross_boundary_bbox: false   # 
    mining_type: MAX_NEGATIVE   # 挖掘类型
                                #   NONE:什么都不用,会发生正负样本步均衡
                                #   MAX_NEGATIVE:为负样本排序,选择分类得分最高的一批作为训练的负样本
                                #   HARD_EXAMPLE:选择基于“在线硬示例挖掘的基于训练区域的对象探测器”的硬实例(?)
  }
}


数据增强(Data Augmentation)

目标检测任务和图片分类任务不同,


基本的数据增强,每张图片在输入会等概率地选取以下一种变换:


  1. 原始图片
  2. 随机裁剪
  3. 带jaccard重叠率约束的随机裁剪(论文给出的是0.1, 0.3, 0.5, 0.7, 0.9五种重叠率设置)


随机裁剪是有条件的,限制最低归一化尺寸(论文中为0.1),以及长宽比(论文中为[1/2,2]);

实际操作中是先生成一系列满足条件的框,在从中挑选若干框来裁剪图像输入训练。


仔细思考一下会发现,上述的数据增强方式只能对某个目标进行放大(zoom in)操作,这会导致小目标数据集缺失。

于是论文中在执行上述数据增强前又增加了一步缩小(zoom out)的增强措施——


  1. 首先生成一张画布,画布的长宽是原图片的1-4倍(随机数);
  2. 将原图放在画布的随机位置上;
  3. ……接下来再送给前述的基本数据增强步骤


据论文介绍,该额外操作为mAP提升了两到三个百分点。


对于画布,从源码data_transform.cpp/ExpandImage()函数(第489行) | github上看,是用空格符初始化了一个字符串缓冲区来作为画布,这似乎意味着是用RGB(32,32,32)的固定颜色填充了画布(空格符的assic码为32);

这种做法有些残暴,或许用原图的平均值、或者一个随机值来作固定颜色填充效果会更好一些;

甚至,用实际图片作为背景图可能会取得更好的效果,具体结论还有待实验。


源码prototxt参数示例:


layer {
  name: "data"
  type: "AnnotatedData"
  top: "data"
  top: "label"
  include {
    phase: TRAIN
  }
  # 图像转换参数组
  transform_param {
    scale: 0.007843     # 归一化,1/127.5
    mirror: true        # 0.5的概率镜像翻转
    mean_value: 127.5   # 去均值(R通道)
    mean_value: 127.5   # 去均值(G通道)
    mean_value: 127.5   # 去均值(B通道)
    # 缩放参数组
    resize_param {
      prob: 1.0                 # 缩放操作的概率
      resize_mode: WARP         # 缩放模式
                                #   WARP:放大或缩小以适应(width, height)
                                #   FIT_SMALL_SIZE:
                                #   FIT_LARGE_SIZE_AND_PAD:
      height: 300               # 缩放后的高
      width: 300                # 缩放后的宽
      # 插值模式同opencv
      interp_mode: LINEAR       # 线性
      interp_mode: AREA         # 像素区域重采样
      interp_mode: NEAREST      # 最近邻
      interp_mode: CUBIC        # 三次样条
      interp_mode: LANCZOS4     # Lanczos
    }
    emit_constraint {
      emit_type: CENTER
    }
    # 色彩扭曲参数组
    distort_param {
      brightness_prob: 0.5
      brightness_delta: 32.0
      contrast_prob: 0.5
      contrast_lower: 0.5
      contrast_upper: 1.5
      hue_prob: 0.5
      hue_delta: 18.0
      saturation_prob: 0.5
      saturation_lower: 0.5
      saturation_upper: 1.5
      random_order_prob: 0.0
    }
    # 图像扩展参数组(zoom-out)
    expand_param {
      prob: 0.5                 # 概率
      max_expand_ratio: 4.0     # 扩大倍数(4x4)
    }
  }
  # 数据源参数组
  data_param {
    source: "trainval_lmdb/"    # 源文件
    batch_size: 24       
    backend: LMDB               # 数据源类型
  }
  # AnnotatedData参数组
  annotated_data_param {
    # 采样器1:原图
    batch_sampler {
      max_sample: 1
      max_trials: 1
    }
    # 采样器2:随机抠图(overlap=0.1)
    batch_sampler {
      sampler {
        min_scale: 0.15         # 最小尺寸(原图的0.15倍)
        max_scale: 1.0          # 最大尺寸
        min_aspect_ratio: 0.5   # 最小长宽比
        max_aspect_ratio: 2.0   # 最大长宽比
      }
      sample_constraint {
        min_jaccard_overlap: 0.1    # 最小JACCARD_OVERLAP(重叠率)
      }
      max_sample: 1     # 最大采样数量
      max_trials: 50    # 最大尝试数量(产生50个随机框,然后再筛选出1个输出)
    }
    
    # [略]采样器3:随机抠图(overlap=0.3)
    # [略]采样器4:随机抠图(overlap=0.5)
    # [略]采样器5:随机抠图(overlap=0.7)
    # [略]采样器6:随机抠图(overlap=0.9)
    # [略]采样器7:随机抠图(overlap=1.0)
    
    label_map_file: "labelmap.prototxt"     # 标签映射文件
  }
}

本文介绍分析了SSD框架的核心思路和训练技巧,加上上一篇文章对MobileNet模型的解析,基本的前置理论准备完毕。在下一篇博文中,将分析chuanqi305是如何把MobileNets和SSD结合起来的——《MobileNet-SSD网络解析 | Hey~YaHei!》。