SPPNet论文笔记和caffe实现说明

来源:互联网 发布:网络出版服务许可单位 编辑:程序博客网 时间:2024/06/08 11:16

SPPNet:Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition
pdf:https://arxiv.org/pdf/1406.4729v2.pdf

论文主要贡献

之前的CNN网络的输入的size都是固定,为什么要这样呢,是因为最后fc层的输入需要是一个固定的尺寸,例如AlexNet的fc6的输入就是需要固定,但是如果各种图片都通过crop或者warp来resize成一样,就会有下面的情况发生:

crop导致看不全,warp导致变形

SPPNet

而该论文通过在conv和fc层之间引入Spatial Pyramid Pooling,可以接受不同的size输入,在SPP层后面产生固定size的输出送入fc层,而且SPP相较于之前的Pooling来说,通过设置不同level的spatial bins来达到Mutil-level,而且由于输入的size是多变的,所以它可以从各个不同的scales来pool feature,有更强的鲁棒性。


随手画了下大致的网络示意图 以AlexNet为例(有点难看 轻喷)

这里写图片描述

实验效果

  1. 直接把原来的Pooling换掉,其他不变输入还是fixed的,这样做为了控制变量,直接看SPP的效果,发现也有1个点的提升
  2. 输入不同size的图片进行训练,比起第一个实验,又有了0.5点的提升吧(注意此处训练是用了不同size,但是测试还是同一size)
  3. 做了组对比实验,每张图片令min(w,h)= 256,然后保持比例放缩到这个大小,送入网络。另一个是取刚刚放缩好的图片中间部位的224x224送入网络,实验结果发现full image比crop image高0.5个点,这也说明了输入全图更好。
  4. 测试时取很多view,比如中心、四个角、四个边的中间以及他们的翻转这18个view,然后全图计算一次conv5的feature map就行,通过映射看这18个view在feature map都是哪块,然后把这18块送入SPP后进入fc,发现也是有提升的

caffe实现

#caffe.proto中的关于SPP层参数的定义message SPPParameter {  enum PoolMethod {    MAX = 0;    AVE = 1;    STOCHASTIC = 2;  }  optional uint32 pyramid_height = 1;  optional PoolMethod pool = 2 [default = MAX]; // The pooling method  enum Engine {    DEFAULT = 0;    CAFFE = 1;    CUDNN = 2;  }  optional Engine engine = 6 [default = DEFAULT];}
# AlexNet trainval.prototxt....layer {  name: "relu5"  type: "ReLU"  bottom: "conv5"  top: "conv5"}#之前的Pooling层#layer {#  name: "pool5"#  type: "Pooling"#  bottom: "conv5"#  top: "pool5"#  pooling_param {#    pool: MAX#    kernel_size: 3#    stride: 2# }#}layer {  name: "spatial_pyramid_pooling"  type: "SPP"  bottom: "conv5"  top: "pool5"  spp_param {    pool: MAX    pyramid_height: 2 # SPP的level的数量  }}layer {  name: "fc6"  type: "InnerProduct"  bottom: "pool5"  top: "fc6"  ...}...
void SPPLayer<Dtype>::LayerSetUp(......){    for (int i = 0; i < pyramid_height_; i++) {        ......        // pooling layer setup        LayerParameter pooling_param = GetPoolingParam(        i, bottom_h_, bottom_w_, spp_param);    }}// 关于pyramid_height// pyramid_level是0到pyramid_height-1// 暂时是这样理解这段代码的,但是没有找到stride在哪???// pool 1x1 2x2 4x4 8x8 ... pow(2, pyramid_height-1)xpow(2, pyramid_height-1)LayerParameter SPPLayer<Dtype>::GetPoolingParam(const int pyramid_level,......){    ......      LayerParameter pooling_param;      int num_bins = pow(2, pyramid_level);      // find padding and kernel size so that the pooling is      // performed across the entire image      int kernel_h = ceil(bottom_h / static_cast<double>(num_bins));      // remainder_h is the min number of pixels that need to be padded before      // entire image height is pooled over with the chosen kernel dimension      int remainder_h = kernel_h * num_bins - bottom_h;      // pooling layer pads (2 * pad_h) pixels on the top and bottom of the      // image.      int pad_h = (remainder_h + 1) / 2;      // similar logic for width      int kernel_w = ceil(bottom_w / static_cast<double>(num_bins));      int remainder_w = kernel_w * num_bins - bottom_w;      int pad_w = (remainder_w + 1) / 2;      pooling_param.mutable_pooling_param()->set_pad_h(pad_h);      pooling_param.mutable_pooling_param()->set_pad_w(pad_w);      pooling_param.mutable_pooling_param()->set_kernel_h(kernel_h);      pooling_param.mutable_pooling_param()->set_kernel_w(kernel_w);      pooling_param.mutable_pooling_param()->set_stride_h(kernel_h);      pooling_param.mutable_pooling_param()->set_stride_w(kernel_w);      ...}

这里写图片描述这里写图片描述


注意事项

  1. 直接复现实验1是很容易,就是输入还是固定的,只是Pooling换掉,只有一点注意,pyramid_height的设置如果过大(图片的size很小)会报的错误:Check failed: pad_h_ < kernel_h_,因为size太小了,到conv5时feature map太小了,而caffe这里限制pad_h_ < kernel_h_

  2. 复现实验2就有些许问题,如果你的batch不是1的话,因为load_batch是多线程同步的,caffe会默认用这个batch里的第一个数据的chanel height width作为输出的格式,所以会报类似的错误:Check failed: height <= datum_height (80(第一个数据的height) vs. 64(后续数据的height)) ,暂时如果不修改部分源码的话,只有先把trainval.prototxt和test.prototxt的batchsize都设为1吧,实验2仍需要注意实验1的问题

  3. 实验3,4自行根据前两条注意事项进行操作
0 0
原创粉丝点击