caffe loss以及正则项反向传播过程

概述

以前一直以为反向传播的过程是这样的,由Softmax计算得出分类loss,然后在加上L2正则项得出总的total_loss,然后再利用total_loss进行反向梯度的计算,而正则项会在每层的Forward函数中计算并返回,表面上感觉很对,但是实际分析就会发现这样想法不合理的地方,

  • 对正则项的计算,不同的solver是不一样的,而且正则项的方法也是有L2L1, 每层layer的Forward函数在不知道正则方法和solver类型的情况下是不可能计算出正则结果的。

  • 对于用total_loss进行反向梯度传播,我们知道total_loss在每一层只和该层的权值有关系,而total_loss中包含着所有权值的正则化结果,这时候,如果我想把反向误差传播转化为矩阵运算,显然不行。

caffe中处理方法

首先需要声明的是,每一个layer的Forward函数的确有返回loss,但是这个loss函数不是表示正则项的结果,而是应付可能出现的如下情况:就是有些网络并不是在最后才进行Softmax的分类计算,有时候会在网络的中间插入Softmax分类,例如googLeNet
所以在caffe中,loss和正则项的计算过程是下面的叙述这样的

计算Softmax分类loss

Softmax分类loss是在Network的最后一层SoftmaxWithLossLayer计算的,这里需要交代的是,当参数iter_size大于1的时候,比如为4,那么一次迭代会有4次ForwardBackward的过程,这时候loss会进行4次累加,最后做个平均;更重要的是每次方向传播的过程中,对于参数的梯度也会进行累加,包括权值和偏置,最后也会对梯度做个平均,这在下面有介绍。主要代码如下:
solver.cpp line221

for(int i = 0; i < param_.iter_size(); ++i){
  loss += net_->ForwardBackward();
}
loss /= param_.iter_size();
...
ApplyUpdate();

往参数梯度中加入正则项

如一开始所说,正则项不能在最后加入,而是在每一层计算梯度完成之后,再对梯度进行微调。在加入正则项之前,如果iter_size大于0,要对梯度进行平均,然后才进行正则化,正则化之后才根据lr_rate计算最终要更新的值,这些操作,毫无疑问都是在sgd有关函数sgd_solver.cpp里完成的。主要代码如下:
sgd_solver.cpp: 109

for (int param_id = 0; param_id < this->net_->learnable_params().size(); ++param_id) {
  Normalize(param_id) // 对梯度去均值
  Regularize(param_id) // 在梯度中加入正则项
  ComputeUpdateValue(param_id, rate);
}
this->net_->Update();

正则项的计算也是非常简单,利用L2优化项对对应权值的导数公式,直接利用矩阵乘法计算,

dL2dw=αw

caffe中代码如下:

Dtype weight_decay = this->param_.weight_decay();
string regularization_type = this->param_.regularization_type();
Dtype local_decay = weight_decay * net_params_weight_decay[param_id];
if (local_decay) {
  if(regularization_type == "L2") {
    caffe_axpy(net_params[param_id]->count(),
      local_decay,
      net_params[param_id]->cpu_data(),
      net_params[param_id]->mutable_cpu_diff());
  }
}

这里顺便提一下,ComputeUpdateValue函数是基于Momentum算法计算更新量的。这个在以后会进行总结。

你可能感兴趣的:(Deep,Learning)