在优化深度网络时,首先要根据损失函数计算 loss,但它对计算梯度是没有用的, 因为 loss 对最后一层的误差敏感项 $\delta$ 可以根据input表示出来, 之后所有参数的梯度更新只需要根据前一层的 $\delta$ 计算. 所以它的存在是用来指导训练的, 即显示地告诉你模型的泛化能力是如何变化的,最否已经达到了最优。

在 Caffe 中, 所有的结构运算已经抽象成了 layer , 怎么计算 loss 呢? 如果一个 layer 是 loss 层, 则其 top 即为 loss.

指定 loss 属性

实际上 Layer 的 Forward 接口是返回值的 (loss), 但需要一个标记位指定这一层是否有 loss 权重, 从而决定是否需要计算 loss.

如下 loss_layer.cpp, 如果一个 layer 属于 LossLayer 则指定 loss_weight

template <typename Dtype>
void LossLayer<Dtype>::LayerSetUp(
    const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
  // LossLayers have a non-zero (1) loss by default.
  if (this->layer_param_.loss_weight_size() == 0) {
    this->layer_param_.add_loss_weight(Dtype(1));
  }
}

在 Layer 层计算 loss

在 Layer 中根据是否有 loss_weight 设置每个 top 的 loss 的标识位 及 loss weight 到 top 的 diff. loss weight 只是影响 loss 的表现形式, 导致在 Backforward 的时候也要用到. =.=

代码出自 Layer.hpp:

inline void SetLossWeights(const vector<Blob<Dtype>*>& top) {
    const int num_loss_weights = layer_param_.loss_weight_size();
    if (num_loss_weights) {
      CHECK_EQ(top.size(), num_loss_weights) << "loss_weight must be "
          "unspecified or specified once per top blob.";
      for (int top_id = 0; top_id < top.size(); ++top_id) {
        const Dtype loss_weight = layer_param_.loss_weight(top_id);
        if (loss_weight == Dtype(0)) { continue; }
        this->set_loss(top_id, loss_weight);
        const int count = top[top_id]->count();
        Dtype* loss_multiplier = top[top_id]->mutable_cpu_diff();
        caffe_set(count, loss_weight, loss_multiplier);
      }
    }
  }

然后在 Forward 的时候, 根据 layer 各个 top 的loss 标记位计算 loss 即可.

template <typename Dtype>
inline Dtype Layer<Dtype>::Forward(const vector<Blob<Dtype>*>& bottom,
    const vector<Blob<Dtype>*>& top) {
  // Lock during forward to ensure sequential forward
  Lock();
  Dtype loss = 0;
  Reshape(bottom, top);
  switch (Caffe::mode()) {
  case Caffe::CPU:
    Forward_cpu(bottom, top);
    for (int top_id = 0; top_id < top.size(); ++top_id) {
      if (!this->loss(top_id)) { continue; }
      const int count = top[top_id]->count();
      const Dtype* data = top[top_id]->cpu_data();
      const Dtype* loss_weights = top[top_id]->cpu_diff();
      loss += caffe_cpu_dot(count, data, loss_weights);
    }
    break;
  ...
  Unlock();
  return loss;
}

Net 中的 loss

Net 最后输出的 loss, 是所有可以计算 loss 的 layer的 loss 之和.

template <typename Dtype>
Dtype Net<Dtype>::ForwardFromTo(int start, int end) {
  CHECK_GE(start, 0);
  CHECK_LT(end, layers_.size());
  Dtype loss = 0;
  for (int i = start; i <= end; ++i) {
    // LOG(ERROR) << "Forwarding " << layer_names_[i];
    Dtype layer_loss = layers_[i]->Forward(bottom_vecs_[i], top_vecs_[i]);
    loss += layer_loss;
    if (debug_info_) { ForwardDebugInfo(i); }
  }
  return loss;
}

问题: 是否可以建立多损失函数的模型?