MTCNN训练环境搭建(二)

考虑MTCNN的训练场景,要求负样本只参与分类训练,而part样本只参与回归训练,正样本则要同时参与分类和回归训练,我们在数据集将不需要参与训练的部分标注为-1,如下图所示:

但是,如果直接使用这样的数据集进行训练的话,这些label=-1的样本也会参与计算梯度,这样显然是不对的。

那么,如何配置caffe使其忽略掉这些label=-1的数据呢?换句话说,我们需要caffe在回传梯度时将这些label=-1的样本梯度设置为0,这样,这些样本就不会参与训练了。

事实上,caffe中的LossLayer层提供了ignore_label参数,当该参数被设置时,程序会忽略ignore_label对应项的loss。caffe中所有loss层都是LossLayer层的子类,但并不是所有loss层都实现了ignore_label功能,SoftmaxWithLoss层可以直接使用这一功能,但EuclideanLoss层就没有这一功能。

我们的所有修改都在prototxt文件中的Loss层部分,对于训练过程中的其它步骤,和以前中提到的多标签分类/回归训练过程完 全 一 致。

 

一、SoftmaxWithLoss层的ignore_label

调用方法如下:


 
 
  1. layer {
  2. name: "loss_cls"
  3. type: "SoftmaxWithLoss"
  4. bottom: "conv4-1"
  5. bottom: "label_cls"
  6. top: "loss_cls"
  7. loss_weight: 1
  8. loss_param{
  9. ignore_label: - 1
  10. }
  11. }

显然,只需要在loss_param中添加一个ignore_label项就可以解决问题。

 

二、EuclideanLoss层的ignore_label

接下来,我们需要修改caffe源码,来为EuclideanLoss层添加ignore_label,添加后的调用方法应该和SoftmaxWithLoss层的调用方法完全一致。

总的来说,我们需要修改以下几个文件:

  • 在EuclideanLoss层的基础上添加新层:EuclideanSpIgnoreLossLayer,将include/caffe/layers/euclidean_loss_layer.hpp、src/caffe/layers/euclidean_loss_layer.cpp和src/caffe/layers/euclidean_loss_layer.cu分别拷贝一份,命名为include/caffe/layers/euclidean_sp_ignore_loss_layer.hpp、src/caffe/layers/euclidean_sp_ignore_loss_layer.cpp和src/caffe/layers/euclidean_sp_ignore_loss_layer.cu。
  • 修改include/caffe/layers/euclidean_sp_ignore_loss_layer.hpp文件
  • 修改src/caffe/layers/euclidean_sp_ignore_loss_layer.cpp文件
  • 修改src/caffe/layers/euclidean_sp_ignore_loss_layer.cu文件

接下来就是这些文件的修改版,其中,//###表示修改的部分,主要改动就是添加根据label将diff设置为0的代码

1. include/caffe/layers/euclidean_sp_ignore_loss_layer.hpp:

在类声明中添加如下部分:


 
 
  1. //###
  2. /// Whether to ignore instances with a certain label.
  3. bool has_ignore_label_;
  4. /// The label indicating that an instance should be ignored.
  5. int ignore_label_;
  6. int num_nonzero_;

2. src/caffe/layers/euclidean_sp_ignore_loss_layer.cpp:

可以对照下面的文件进行修改。


 
 
  1. #include <vector>
  2. #include "caffe/layers/euclidean_sp_ignore_loss_layer.hpp"
  3. #include "caffe/util/math_functions.hpp"
  4. namespace caffe {
  5. template < typename Dtype>
  6. void EuclideanSpIgnoreLossLayer<Dtype>::Reshape(
  7. const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
  8. LossLayer<Dtype>::Reshape(bottom, top);
  9. CHECK_EQ(bottom[ 0]->count( 1), bottom[ 1]->count( 1))
  10. << "Inputs must have the same dimension.";
  11. diff_.ReshapeLike(*bottom[ 0]);
  12. //###
  13. has_ignore_label_ =
  14. this->layer_param_.loss_param().has_ignore_label();
  15. if (has_ignore_label_) {
  16. ignore_label_ = this->layer_param_.loss_param().ignore_label();
  17. }
  18. }
  19. template < typename Dtype>
  20. void EuclideanSpIgnoreLossLayer<Dtype>::Forward_cpu( const vector<Blob<Dtype>*>& bottom,
  21. const vector<Blob<Dtype>*>& top) {
  22. int count = bottom[ 0]->count(); // batch-size * channels
  23. int num = bottom[ 0]->num(); // batch-size
  24. int channels = bottom[ 0]->channels(); // channels (4 or 10)
  25. //###
  26. caffe_sub( // 按元素相减
  27. count,
  28. bottom[ 0]->cpu_data(),
  29. bottom[ 1]->cpu_data(),
  30. diff_.mutable_cpu_data());
  31. num_nonzero_ = num; // 找到需要被忽略的项,将diff置0
  32. if (has_ignore_label_) {
  33. const Dtype* label_data = bottom[ 1]->cpu_data();
  34. Dtype* diff_data = diff_.mutable_cpu_data();
  35. for ( int i = 0; i < num; ++i) {
  36. bool ignore = true;
  37. for ( int j = 0; j < channels; ++j) {
  38. const int label_value = static_cast< int>(label_data[i * channels + j]);
  39. if (label_value != ignore_label_)
  40. ignore = false;
  41. }
  42. if (ignore) {
  43. for ( int j = 0; j < channels; ++j)
  44. diff_data[i * channels + j] = 0;
  45. num_nonzero_ --;
  46. }
  47. }
  48. }
  49. Dtype dot = caffe_cpu_dot(count, diff_.cpu_data(), diff_.cpu_data()); // 按元素相乘再相加
  50. // Dtype loss = dot / bottom[0]->num() / Dtype(2);
  51. Dtype loss = dot / Dtype( 2.0*num_nonzero_); // 除以2N(非0项)
  52. top[ 0]->mutable_cpu_data()[ 0] = loss;
  53. }
  54. template < typename Dtype>
  55. void EuclideanSpIgnoreLossLayer<Dtype>::Backward_cpu( const vector<Blob<Dtype>*>& top,
  56. const vector< bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {
  57. for ( int i = 0; i < 2; ++i) {
  58. if (propagate_down[i]) {
  59. const Dtype sign = (i == 0) ? 1 : -1;
  60. //###
  61. // const Dtype alpha = sign * top[0]->cpu_diff()[0] / bottom[i]->num();
  62. const Dtype alpha = sign * top[ 0]->cpu_diff()[ 0] / num_nonzero_;
  63. caffe_cpu_axpby(
  64. bottom[i]->count(), // count
  65. alpha, // alpha
  66. diff_.cpu_data(), // a
  67. Dtype( 0), // beta
  68. bottom[i]->mutable_cpu_diff()); // b
  69. }
  70. }
  71. }
  72. #ifdef CPU_ONLY
  73. STUB_GPU(EuclideanSpIgnoreLossLayer);
  74. #endif
  75. INSTANTIATE_CLASS(EuclideanSpIgnoreLossLayer);
  76. REGISTER_LAYER_CLASS(EuclideanSpIgnoreLoss);
  77. } // namespace caffe

2. src/caffe/layers/euclidean_sp_ignore_loss_layer.cu:

可以对照下面的文件进行修改。


 
 
  1. #include <vector>
  2. #include "caffe/layers/euclidean_sp_ignore_loss_layer.hpp"
  3. #include "caffe/util/math_functions.hpp"
  4. namespace caffe {
  5. template < typename Dtype>
  6. void EuclideanSpIgnoreLossLayer<Dtype>::Forward_gpu( const vector<Blob<Dtype>*>& bottom,
  7. const vector<Blob<Dtype>*>& top) {
  8. int count = bottom[ 0]->count(); // batch-size * channels
  9. int num = bottom[ 0]->num(); // batch-size
  10. int channels = bottom[ 0]->channels(); // channels (4 or 10)
  11. //###
  12. caffe_gpu_sub( // 按元素相减
  13. count,
  14. bottom[ 0]->gpu_data(),
  15. bottom[ 1]->gpu_data(),
  16. diff_.mutable_gpu_data());
  17. num_nonzero_ = num; // 找到需要被忽略的项,将diff置0
  18. if (has_ignore_label_) {
  19. const Dtype* label_data = bottom[ 1]->gpu_data();
  20. Dtype* diff_data = diff_.mutable_gpu_data();
  21. for ( int i = 0; i < num; ++i) {
  22. bool ignore = true;
  23. for ( int j = 0; j < channels; ++j) {
  24. const int label_value = static_cast< int>(label_data[i * channels + j]);
  25. if (label_value != ignore_label_)
  26. ignore = false;
  27. }
  28. if (ignore) {
  29. for ( int j = 0; j < channels; ++j)
  30. diff_data[i * channels + j] = 0;
  31. num_nonzero_ --;
  32. }
  33. }
  34. }
  35. Dtype dot;
  36. caffe_gpu_dot(count, diff_.gpu_data(), diff_.gpu_data(), &dot); // 按元素相乘再相加
  37. // Dtype loss = dot / bottom[0]->num() / Dtype(2);
  38. Dtype loss = dot / Dtype( 2.0*num_nonzero_); // 除以2N(非0项)
  39. top[ 0]->mutable_cpu_data()[ 0] = loss;
  40. }
  41. template < typename Dtype>
  42. void EuclideanSpIgnoreLossLayer<Dtype>::Backward_gpu( const vector<Blob<Dtype>*>& top,
  43. const vector< bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {
  44. for ( int i = 0; i < 2; ++i) {
  45. if (propagate_down[i]) {
  46. const Dtype sign = (i == 0) ? 1 : -1;
  47. //###
  48. // const Dtype alpha = sign * top[0]->cpu_diff()[0] / bottom[i]->num();
  49. const Dtype alpha = sign * top[ 0]->cpu_diff()[ 0] / num_nonzero_;
  50. caffe_gpu_axpby(
  51. bottom[i]->count(), // count
  52. alpha, // alpha
  53. diff_.gpu_data(), // a
  54. Dtype( 0), // beta
  55. bottom[i]->mutable_gpu_diff()); // b
  56. }
  57. }
  58. }
  59. INSTANTIATE_LAYER_GPU_FUNCS(EuclideanSpIgnoreLossLayer);
  60. } // namespace caffe

到这里为止,程序就修改完成了,编译caffe,不出问题的话,此时EuclideanSpIgnoreLoss层就可以支持ignore_label的添加了。

 

调用方法如下:


 
 
  1. layer {
  2. name: "loss_box"
  3. type: "EuclideanSpIgnoreLoss"
  4. bottom: "conv4-2"
  5. bottom: "label_box"
  6. top: "loss_box"
  7. loss_weight: 0.5
  8. loss_param{
  9. ignore_label: - 1
  10. }
  11. }

意为:如果该层的所有输入标签均为-1,则忽略这个样本的loss。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值