关于SSD训练时默认框输出层输出通道num_output的计算,小人脸检测建构SFD,他最低层的默认框提取层是conv3_3,而并不是conv4_4

关于SSD训练时默认框输出层输出通道num_output的计算

申明,本博文是为解决以下两个问题而撰写。

Check failed: num_priors_ * num_loc_classes_ * 4 == bottom[0]->channels() (264348 vs. 88116) Number of priors must match number of location predictions.

Check failed: num_priors_ * num_classes_ == bottom[1]->channels() (132174 vs. 99406) Number of priors must match number of confidence predictions.

我们针对小目标检测时受到此方面的困扰,在阅读源码后解决。特写下此博客,以供参考。具体源码,请仔细阅读:src/caffe/layers/multibox_loss_layer.cpp

输出通道主要涉及默认框产生层的置信输出mbox_conf和位置输出mbox_conf。对应prototxt文件中的应该是以下部分,我们以conv4_3为例讲解。


     
     
  1. layer {
  2. name: “conv4_3_norm_mbox_loc”
  3. type: “Convolution”
  4. bottom: “conv4_3_norm”
  5. top: “conv4_3_norm_mbox_loc”
  6. param {
  7. lr_mult: 1
  8. decay_mult: 1
  9. }
  10. param {
  11. lr_mult: 2
  12. decay_mult: 0
  13. }
  14. convolution_param {
  15. num_output: 16
  16. pad: 1
  17. kernel_size: 3
  18. stride: 1
  19. weight_filler {
  20. type: “xavier”
  21. }
  22. bias_filler {
  23. type: “constant”
  24. value: 0
  25. }
  26. }
  27. }
以上就是conv4_3层的在train.prototxt中的内容。我们主要讲解num_output=16是怎么计算来的,在原始SSD中conv4_3产生的每个特征图的中心点产生4个默认框,具体默认框的产生数量及方式请参看博主博文: 点击打开链接

这四个默认仍框分别对应x1,y1,x2,y2四个点,所以呢在产生位置损失时就会产生四个loc损失,所以一个中心点的所产生的4个默认框就有4*4=16个位置信息需要输出,这就是16的来源。具体在multibox_loss_layer.cpp中的定义是:


     
     
  1. template < typename Dtype>
  2. void MultiBoxLossLayer<Dtype>::Reshape( const vector<Blob<Dtype>*>& bottom,
  3. const vector<Blob<Dtype>*>& top) {
  4. LossLayer<Dtype>::Reshape(bottom, top);
  5. num_ = bottom[ 0]->num();
  6. num_priors_ = bottom[ 2]->height() / 4;
  7. num_gt_ = bottom[ 3]->height();
  8. CHECK_EQ(bottom[ 0]->num(), bottom[ 1]->num());
  9. CHECK_EQ(num_priors_ * loc_classes_ * 4, bottom[ 0]->channels()) //num_priors_就是指每个中心点产生的默认框个数,位置个数计算。
  10. << "Number of priors must match number of location predictions.";
  11. CHECK_EQ(num_priors_ * num_classes_, bottom[ 1]->channels()) //置信个数计算。
  12. << "Number of priors must match number of confidence predictions.";
  13. }
其他5个默认框提取特征层同样的计算方法。

对于置信输出通道,其在train.prototxt中的内容为:


     
     
  1. layer {
  2. name: "conv4_3_norm_mbox_conf"
  3. type: "Convolution"
  4. bottom: "conv4_3_norm"
  5. top: "conv4_3_norm_mbox_conf"
  6. param {
  7. lr_mult: 1
  8. decay_mult: 1
  9. }
  10. param {
  11. lr_mult: 2
  12. decay_mult: 0
  13. }
  14. convolution_param {
  15. num_output: 64
  16. pad: 1
  17. kernel_size: 3
  18. stride: 1
  19. weight_filler {
  20. type: "xavier"
  21. }
  22. bias_filler {
  23. type: "constant"
  24. value: 0
  25. }
  26. }
  27. }
置信conf的输出计算方法不同与位置loc,一个中心点会产生一个默认框,但是这个默认框到底是正样本还是负样本,这就涉及到正负的两个置信,如果是负的,那它就是背景。所以这需要看你的类别,在原始SSD检测VOC时,类别数为21,所以这里的num_output应该=(你的类别数+1)*中心点产生的默认框的个数,这里为21*4=64。它在multibox_loss_layer.cpp中的定义也在上面我们复制出来的代码中。

其他5个提取层也是如此计算。

注意的一点是以上是针对conv4_3的计算,而fc7、conv6_2、conv7_2,原始的SSD框架中这三层的每个中心点产生了6个默认框,例如fc7,他在train.prototxt中的内容是:


     
     
  1. layer {
  2. name: "fc7_mbox_loc"
  3. type: "Convolution"
  4. bottom: "fc7"
  5. top: "fc7_mbox_loc"
  6. param {
  7. lr_mult: 1
  8. decay_mult: 1
  9. }
  10. param {
  11. lr_mult: 2
  12. decay_mult: 0
  13. }
  14. convolution_param {
  15. num_output: 24
  16. pad: 1
  17. kernel_size: 3
  18. stride: 1
  19. weight_filler {
  20. type: "xavier"
  21. }
  22. bias_filler {
  23. type: "constant"
  24. value: 0
  25. }
  26. }
  27. }

这里我们只列举了位置loc,计算为4*6=24。置信输出就应该是21*6=126了,大家应该知道怎么计算了吧。

另外我们着重强调一点,关于flip参数的设置,这在我的博文关于ssd_pascal.py解读中有讲解:点击打开链接

他是设置翻转的,我们在关于默认框的产生一文中讲到,ssd默认产生一大一小两个正方形默认框,另外,每设置一个aspect_ratio则增加两个长方形默认框,然而在prior_box_layer.cpp文件中只有产生一个长方形默认框的计算公式如下:


     
     
  1. // rest of priors
  2. for ( int r = 0; r < aspect_ratios_.size(); ++r) {
  3. float ar = aspect_ratios_[r];
  4. if ( fabs(ar - 1.) < 1e-6) {
  5. continue;
  6. }
  7. box_width = min_size_ * sqrt(ar);
  8. box_height = min_size_ / sqrt(ar);
  9. // xmin
  10. top_data[idx++] = (center_x - box_width / 2.) / img_width;
  11. // ymin
  12. top_data[idx++] = (center_y - box_height / 2.) / img_height;
  13. // xmax
  14. top_data[idx++] = (center_x + box_width / 2.) / img_width;
  15. // ymax
  16. top_data[idx++] = (center_y + box_height / 2.) / img_height;
  17. }
比如我们在conv4_3计算出的min_size=30,max_size=60,而该层的aspect_ratio=2,那么产生四个默认框,两个正方形,两个长方形。这里只计算了一个长方形框的大小,最后计算的纵横比为1:2,那么2:1的纵横比长方形哪里去了呢?就是靠我们的flip来计算了,当我们设置flip=True时一个aspect_ratio才会产生两个默认框,如果不设置或者为Flase那么就只产生一个长方形默认框。比如conv4_3产生的默认框在train.prototxt中的内容如下:


     
     
  1. layer {
  2.   name: "conv4_3_norm_mbox_priorbox" //注意在卷积层conv4_3后有一个norm层做归一化。
  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: 32
  9.     aspect_ratio: 2
  10.     flip: true //注意,如果没有flip参数,则aspect_ratio=2只能产生一个纵横比为1:2的默认框。
  11.     clip: false
  12.     variance: 0.1
  13.     variance: 0.1
  14.     variance: 0.2
  15.     variance: 0.2
  16.     step: 8
  17.     offset: 0.5
  18.   }
  19. }
当然,这里是我们项目中的设置,我们这里只设置了min_size,因为我们只需要一个较小的正方形边框就可以了,并不需要较大的正方形边框,所以我们没有设置max_size参数,故每个默认框产生特征层只生成一个边长为min_size的正方形默认框,剔除边长为sqrt(min_size*max_size)的默认框。这里我们设置了一个aspect_ratio=2,所以每个中心点产生3个默认框。这里flip我吃了很大的亏。一直在报错。


最后以上面的讲解延伸到我们最近研究的小人脸检测建构SFD,他最低层的默认框提取层是conv3_3,而并不是conv4_4,所以其在conv3_3后面也做了norm操作,另外他在conv3_3后面还添加了一个slice层,注意只在conv3_3后面添加,它在train.prototxt中的内容为:


     
     
  1. layer {
  2.   name: "conv3_3_norm_mbox_conf"
  3.   type: "Convolution"
  4.   bottom: "conv3_3_norm"
  5.   top: "conv3_3_norm_mbox_conf"
  6.   param {
  7.     lr_mult: 1
  8.     decay_mult: 1
  9.   }
  10.   param {
  11.     lr_mult: 2
  12.     decay_mult: 0
  13.   }
  14.   convolution_param {
  15.     num_output: 8 //这里=类别数*该层每个中心点产生的默认框个数+2,这个2是由于以下添加的slice层的作用导致的。其他的如4_3、5_3、fc7、6_2、7_2这几层没有加slice层的则不需要+2。
  16.     pad: 1
  17.     kernel_size: 3
  18.     stride: 1
  19.     weight_filler {
  20.       type: "xavier"
  21.     }
  22.     bias_filler {
  23.       type: "constant"
  24.       value: 0
  25.     }
  26.   }
  27. }
  28. layer {
  29. name: "conv3_3_norm_mbox_conf_slice"
  30. type: "Slice"
  31. bottom: "conv3_3_norm_mbox_conf"
  32. top: "conv3_3_norm_mbox_conf1"
  33. top: "conv3_3_norm_mbox_conf2"
  34. top: "conv3_3_norm_mbox_conf3"
  35. top: "conv3_3_norm_mbox_conf4"
  36. slice_param {
  37. axis: 1
  38. slice_point: 1
  39. slice_point: 2
  40. slice_point: 3
  41. }
  42. }
  43. layer {
  44. name: "conv3_3_norm_mbox_conf_maxout"
  45. type: "Eltwise"
  46. bottom: "conv3_3_norm_mbox_conf1"
  47. bottom: "conv3_3_norm_mbox_conf2"
  48. bottom: "conv3_3_norm_mbox_conf3"
  49. top: "conv3_3_norm_mbox_conf_maxout"
  50. eltwise_param {
  51. operation: MAX
  52. }
  53. }
  54. layer {
  55. name: "conv3_3_norm_mbox_conf_out"
  56. type: "Concat"
  57. bottom: "conv3_3_norm_mbox_conf_maxout"
  58. bottom: "conv3_3_norm_mbox_conf4"
  59. top: "conv3_3_norm_mbox_conf_out"
  60. concat_param {
  61. axis: 1
  62. }
  63. }
该层的具体作用,博主还没有做仔细的解读,但是其中slice层导致了conv3_3的置信conf输出发生了变化,要在我们前面讲的基础上加上2,即我们在以上代码中所注释的部分。

最后给大家附上有置信conf和位置loc输出通道设置错误引起的error:

1.由置信引起的:

Check failed: num_priors_ * num_classes_ == bottom[1]->channels() (132174 vs. 99406) Number of priors must match number of confidence predictions.

其中括号里的数字不必去纠结,这和你的数据有关。

2.由位置引起的:

Check failed: num_priors_ * num_loc_classes_ * 4 == bottom[0]->channels() (264348 vs. 88116) Number of priors must match number of location predictions.

博主水平有限,如有错误请多多指正,转载请注明地址。谢谢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值