CAFFE CONTRASTIVE_LOSS 对比损失函数

caffe的损失函数,目前已经囊括了所有可以用的了吧,损失函数由激活函数决定,同时有时会加入regularization,在BP过程中,使得误差传递得以良好运行。

一、

contrastive_loss,对应contrastive_loss_layer,我看了看代码,这个应该是输入是一对用来做验证的数据,比如两张人脸图,可能是同一个人的(正样本),也可能是不同个人(负样本)。在caffe的examples中,siamese这个例子中,用的损失函数是该类型的。

该损失函数具体数学表达形式:

二、

euclidean_loss,对应euclidean_loss_layer,该损失函数就是loss=(y-f(wx))^2

hinge_loss,对应hinge_loss_layer,该损失函数就是loss=(0,)

infogain_loss,对应infogain_loss_layer,损失函数表达式:

multinomial_logistic_loss,对应multinomial_logistic_loss_layer,损失函数表达式:

sigmoid_cross_entropy,对应sigmoid_cross_entropy_loss_layer,损失函数表达式:

softmax_loss,对应softmax_loss_layer,损失函数表达式:


三、

对比损失函数(Contrastive loss)

输入:

形状: (N×C×1×1)  特征  a[,+]

形状: (N×C×1×1)  特征  b[,+]

形状: (N×1×1×1)  相似性  y[0,1]

输出:

形状: (1×1×1×1)

对比损失函数为:  E=12Nn=1N(y)d+(1y)max(margind,0)

其中  d=||anbn||22 .

适合场景:

可以用来训练Siamese网络

#include <algorithm>
#include <vector>

#include "caffe/layer.hpp"
#include "caffe/loss_layers.hpp"
#include "caffe/util/io.hpp"
#include "caffe/util/math_functions.hpp"

// Computes the contrastive loss ,loss = [sum(y)*d + max(margin - d,0)]/(2*N)
// where d = |a_i-b_i|. Detailed reference to the official document:
// http://caffe.berkeleyvision.org/doxygen/classcaffe_1_1ContrastiveLossLayer.html

namespace caffe {

template <typename Dtype>
void ContrastiveLossLayer<Dtype>::LayerSetUp(
  const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
  LossLayer<Dtype>::LayerSetUp(bottom, top);
  CHECK_EQ(bottom[0]->channels(), bottom[1]->channels());
  CHECK_EQ(bottom[0]->height(), 1);
  CHECK_EQ(bottom[0]->width(), 1);
  CHECK_EQ(bottom[1]->height(), 1);
  CHECK_EQ(bottom[1]->width(), 1);
  CHECK_EQ(bottom[2]->channels(), 1);
  CHECK_EQ(bottom[2]->height(), 1);
  CHECK_EQ(bottom[2]->width(), 1);
  diff_.Reshape(bottom[0]->num(), bottom[0]->channels(), 1, 1);
  diff_sq_.Reshape(bottom[0]->num(), bottom[0]->channels(), 1, 1);
  dist_sq_.Reshape(bottom[0]->num(), 1, 1, 1);
  // vector of ones used to sum along channels
  summer_vec_.Reshape(bottom[0]->channels(), 1, 1, 1);
  for (int i = 0; i < bottom[0]->channels(); ++i)
    summer_vec_.mutable_cpu_data()[i] = Dtype(1);
}

template <typename Dtype>
void ContrastiveLossLayer<Dtype>::Forward_cpu(
    const vector<Blob<Dtype>*>& bottom,
    const vector<Blob<Dtype>*>& top) {
  int count = bottom[0]->count();
  // diff_ = a_i-b_i
  // d 
  caffe_sub(
      count,
      bottom[0]->cpu_data(),  // a
      bottom[1]->cpu_data(),  // b
      diff_.mutable_cpu_data());  // a_i-b_i
  const int channels = bottom[0]->channels();
  // margin for dissimilar pair, default = 1.0
  Dtype margin = this->layer_param_.contrastive_loss_param().margin();
  // legacy_version = false (the default) uses (margin - d)^2 
  // legacy_version = true uses (margin - d^2). This is kept to support 
  // reproduce existing models and results
  bool legacy_version =
      this->layer_param_.contrastive_loss_param().legacy_version();
  Dtype loss(0.0);
  for (int i = 0; i < bottom[0]->num(); ++i) {
    // d 点乘
    dist_sq_.mutable_cpu_data()[i] = caffe_cpu_dot(channels,
        diff_.cpu_data() + (i*channels), diff_.cpu_data() + (i*channels));
    if (static_cast<int>(bottom[2]->cpu_data()[i])) {  // similar pairs
      loss += dist_sq_.cpu_data()[i];
    } else {  // dissimilar pairs
	  // if legacy_version = true ,uses (margin - d^2), else uses (margin - d)^2 
      if (legacy_version) {
        loss += std::max(margin - dist_sq_.cpu_data()[i], Dtype(0.0));
      } else {
        Dtype dist = std::max(margin - sqrt(dist_sq_.cpu_data()[i]), 0.0);
        loss += dist*dist;
      }
    }
  }
  loss = loss / static_cast<Dtype>(bottom[0]->num()) / Dtype(2);
  top[0]->mutable_cpu_data()[0] = loss;
}

template <typename Dtype>
void ContrastiveLossLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
    const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {
  Dtype margin = this->layer_param_.contrastive_loss_param().margin();
  bool legacy_version =
      this->layer_param_.contrastive_loss_param().legacy_version();
  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] /
          static_cast<Dtype>(bottom[i]->num());
      int num = bottom[i]->num();
      int channels = bottom[i]->channels();
      for (int j = 0; j < num; ++j) {
        Dtype* bout = bottom[i]->mutable_cpu_diff();
        if (static_cast<int>(bottom[2]->cpu_data()[j])) {  // similar pairs
          caffe_cpu_axpby(
              channels,
              alpha,
              diff_.cpu_data() + (j*channels),
              Dtype(0.0),
              bout + (j*channels));
        } else {  // dissimilar pairs
          Dtype mdist(0.0);
          Dtype beta(0.0);
          if (legacy_version) {
            mdist = margin - dist_sq_.cpu_data()[j];
            beta = -alpha;
          } else {
            Dtype dist = sqrt(dist_sq_.cpu_data()[j]);
            mdist = margin - dist;
            beta = -alpha * mdist / (dist + Dtype(1e-4));
          }
          if (mdist > Dtype(0.0)) {
            caffe_cpu_axpby(
                channels,
                beta,
                diff_.cpu_data() + (j*channels),
                Dtype(0.0),
                bout + (j*channels));
          } else {
            caffe_set(channels, Dtype(0), bout + (j*channels));
          }
        }
      }
    }
  }
}

#ifdef CPU_ONLY
STUB_GPU(ContrastiveLossLayer);
#endif

INSTANTIATE_CLASS(ContrastiveLossLayer);
REGISTER_LAYER_CLASS(ContrastiveLoss);

}  // namespace caffe

从程序中可以看到, 代码不是按照 上边的那个代价函数写的,  这可能是 caffe 一种优化方法, 这种代价函数可能效果更好。 

从代码中可以看出 

loss = d + max(margin - d)^2  或  + (margin - d^2) 


成员函数,见的最多的有:
const Dtype* cpu_data() const; //cpu使用的数据 
void set_cpu_data(Dtype* data);//用数据块的值来blob里面的data。
const Dtype* gpu_data() const;//返回不可更改的指针,下同 
const Dtype* cpu_diff() const; 
const Dtype* gpu_diff() const; 
Dtype* mutable_cpu_data();//返回可更改的指针,下同
Dtype* mutable_gpu_data(); 
Dtype* mutable_cpu_diff();
   Dtype* mutable_gpu_diff();
总之,带mutable_开头的意味着可以对返回的指针内容进行更改,而不带mutable_开头的返回const 指针,不能对其指针的内容进行修改,


很多程序中用到 mutable_cpu_data();//返回可更改的指针和 cpu_data() const; //cpu使用的数据 
其实这两个返回的都是 cpu_data。 一个是返回可以更改的指针,一个返回的是不可更改的指针
这里体现作者 编程技巧的地方 。 
有的数据比如读取的数据, 这些在运行过程中不能让他发生变化, 所以定义了const 类型,但是定义他的指针就必须定义为const 类型的指针, 但是很多时候, 读取完数据后,下面要进行很多操作。 这里就需要改变他的值大小。 所以再去定义一个 mutable类型的指针,这个指向原来的数据, 这体现了作者编程能力 


你可能感兴趣的:(CAFFE CONTRASTIVE_LOSS 对比损失函数)