caffe学习记录(四):如何配置caffe忽略掉某些不参与训练的标签

考虑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

调用方法如下:

layer { 
  name: "loss_cls" 
  type: "SoftmaxWithLoss" 
  bottom: "conv4-1" 
  bottom: "label_cls" 
  top: "loss_cls"
  loss_weight: 1
  loss_param{
    ignore_label: -1
  }
}

显然,只需要在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:

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

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

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

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

#include <vector>

#include "caffe/layers/euclidean_sp_ignore_loss_layer.hpp"
#include "caffe/util/math_functions.hpp"

namespace caffe {

template <typename Dtype>
void EuclideanSpIgnoreLossLayer<Dtype>::Reshape(
  const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
  LossLayer<Dtype>::Reshape(bottom, top);
  CHECK_EQ(bottom[0]->count(1), bottom[1]->count(1))
      << "Inputs must have the same dimension.";
  diff_.ReshapeLike(*bottom[0]);

  //###
  has_ignore_label_ =
    this->layer_param_.loss_param().has_ignore_label();
  if (has_ignore_label_) {
    ignore_label_ = this->layer_param_.loss_param().ignore_label();
  }
}

template <typename Dtype>
void EuclideanSpIgnoreLossLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,
    const vector<Blob<Dtype>*>& top) {
  int count = bottom[0]->count();        // batch-size * channels
  int num = bottom[0]->num();            // batch-size
  int channels = bottom[0]->channels();  // channels (4 or 10)

  //###
  caffe_sub(                             // 按元素相减
      count,
      bottom[0]->cpu_data(),
      bottom[1]->cpu_data(),
      diff_.mutable_cpu_data());

  num_nonzero_ = num;                    // 找到需要被忽略的项,将diff置0
  if (has_ignore_label_) {
    const Dtype* label_data = bottom[1]->cpu_data();
    Dtype* diff_data = diff_.mutable_cpu_data();
    for (int i = 0; i < num; ++i) {

      bool ignore = true;
      for (int j = 0; j < channels; ++j) {
        const int label_value = static_cast<int>(label_data[i * channels + j]);
        if (label_value != ignore_label_)
          ignore = false;
      }

      if (ignore) {
        for (int j = 0; j < channels; ++j)
          diff_data[i * channels + j] = 0;
        num_nonzero_ --;
      }
    }
  }

  Dtype dot = caffe_cpu_dot(count, diff_.cpu_data(), diff_.cpu_data());  // 按元素相乘再相加
  // Dtype loss = dot / bottom[0]->num() / Dtype(2);
  Dtype loss = dot / Dtype(2.0*num_nonzero_);                            // 除以2N(非0项)
  top[0]->mutable_cpu_data()[0] = loss;
}

template <typename Dtype>
void EuclideanSpIgnoreLossLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
    const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {

  for (int i = 0; i < 2; ++i) {
    if (propagate_down[i]) {
      const Dtype sign = (i == 0) ? 1 : -1;

      //###
      // const Dtype alpha = sign * top[0]->cpu_diff()[0] / bottom[i]->num(); 
      const Dtype alpha = sign * top[0]->cpu_diff()[0] / num_nonzero_;        
      
      caffe_cpu_axpby(
          bottom[i]->count(),              // count
          alpha,                              // alpha
          diff_.cpu_data(),                   // a
          Dtype(0),                           // beta
          bottom[i]->mutable_cpu_diff());  // b
    }
  }
}

#ifdef CPU_ONLY
STUB_GPU(EuclideanSpIgnoreLossLayer);
#endif

INSTANTIATE_CLASS(EuclideanSpIgnoreLossLayer);
REGISTER_LAYER_CLASS(EuclideanSpIgnoreLoss);

}  // namespace caffe

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

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

#include <vector>

#include "caffe/layers/euclidean_sp_ignore_loss_layer.hpp"
#include "caffe/util/math_functions.hpp"

namespace caffe {

template <typename Dtype>
void EuclideanSpIgnoreLossLayer<Dtype>::Forward_gpu(const vector<Blob<Dtype>*>& bottom,
    const vector<Blob<Dtype>*>& top) {
  int count = bottom[0]->count();        // batch-size * channels
  int num = bottom[0]->num();            // batch-size
  int channels = bottom[0]->channels();  // channels (4 or 10)

  //###
  caffe_gpu_sub(                         // 按元素相减
      count,
      bottom[0]->gpu_data(),
      bottom[1]->gpu_data(),
      diff_.mutable_gpu_data());

  num_nonzero_ = num;                    // 找到需要被忽略的项,将diff置0
  if (has_ignore_label_) {
    const Dtype* label_data = bottom[1]->gpu_data();
    Dtype* diff_data = diff_.mutable_gpu_data();
    for (int i = 0; i < num; ++i) {

      bool ignore = true;
      for (int j = 0; j < channels; ++j) {
        const int label_value = static_cast<int>(label_data[i * channels + j]);
        if (label_value != ignore_label_)
          ignore = false;
      }

      if (ignore) {
        for (int j = 0; j < channels; ++j)
          diff_data[i * channels + j] = 0;
        num_nonzero_ --;
      }
    }
  }
  
  Dtype dot;
  caffe_gpu_dot(count, diff_.gpu_data(), diff_.gpu_data(), &dot);  // 按元素相乘再相加
  // Dtype loss = dot / bottom[0]->num() / Dtype(2);
  Dtype loss = dot / Dtype(2.0*num_nonzero_);                      // 除以2N(非0项)
  top[0]->mutable_cpu_data()[0] = loss;
}

template <typename Dtype>
void EuclideanSpIgnoreLossLayer<Dtype>::Backward_gpu(const vector<Blob<Dtype>*>& top,
    const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {
  for (int i = 0; i < 2; ++i) {
    if (propagate_down[i]) {
      const Dtype sign = (i == 0) ? 1 : -1;

      //###
      // const Dtype alpha = sign * top[0]->cpu_diff()[0] / bottom[i]->num();
      const Dtype alpha = sign * top[0]->cpu_diff()[0] / num_nonzero_;

      caffe_gpu_axpby(
          bottom[i]->count(),              // count
          alpha,                              // alpha
          diff_.gpu_data(),                   // a
          Dtype(0),                           // beta
          bottom[i]->mutable_gpu_diff());  // b
    }
  }
}

INSTANTIATE_LAYER_GPU_FUNCS(EuclideanSpIgnoreLossLayer);

}  // namespace caffe

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

 

调用方法如下:

layer { 
  name: "loss_box" 
  type: "EuclideanSpIgnoreLoss" 
  bottom: "conv4-2"
  bottom: "label_box" 
  top: "loss_box"
  loss_weight: 0.5
  loss_param{
    ignore_label: -1
  }
}

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值