caffe的正则项理解

caffe的正则项理解

在神经网络的cost function中,出了主目标函以外,往往伴有正则项。如下述公式,

J ( θ ) = 1 N i = 0 N ( h i ( θ ) y i ) 2 + λ L 2 ( θ ) 1

J ( θ ) = 1 N i = 0 N ( h i ( θ ) y i ) 2 + λ L 1 ( θ ) 1

其中 λ 叫做惩罚项系数, L 2 正则是 L 2 ( θ ) = 1 2 i θ i 2 , 并且 L 1 正则是 L 1 ( θ ) = i | θ i | .

正则项的目的可以参考博客,
https://blog.csdn.net/w5688414/article/details/78046960
https://www.cnblogs.com/Peyton-Li/p/7607858.html


caffe实现正则项

在sgd_solver.cpp的void SGDSolver::ApplyUpdate()函数中:

void SGDSolver<Dtype>::ApplyUpdate() {
  CHECK(Caffe::root_solver());
  Dtype rate = GetLearningRate();
  if (this->param_.display() && this->iter_ % this->param_.display() == 0) {
    LOG(INFO) << "Iteration " << this->iter_ << ", lr = " << rate;
  }
  ClipGradients();
  for (int param_id = 0; param_id < this->net_->learnable_params().size();//是指存在多少个参数层,若VGG-                  16则有16个可学习的参数层
       ++param_id) {
    Normalize(param_id);  //对梯度归一化,归一化的目的是对多次iteration的梯度求平均。
    Regularize(param_id);  //对梯度正则化,
    ComputeUpdateValue(param_id, rate);  //计算主cost function的梯度方向
  }
  this->net_->Update();
}

这里提一下代码中Normalize(param_id)归一化的含义与目的:在caffe的solver.prototxt中存在一个参数叫iter_size。一般情况下iter_size=1,若iter_size=2相当于进行两次new->forward()和net->backward()才进行一次梯度更新,而此时梯度保存的是两次iteration的总和,因此需要归一化梯度。
iter_size这个参数的目的:若显卡显存较小,无法装载大的batch size,可以利用iter_size来实现模拟大batch size的作用。
for example:batch size = 128,iter_size=1近似等价于batch size = 64,iter_size=2。

回到Regularize函数,这个函数是针对每一层可学习的参数层,来求解出 正则化梯度方向

进入到Regularize函数, :代码只贴出了GPU computing部分。

void SGDSolver<Dtype>::Regularize(int param_id) {
  const vector<Blob<Dtype>*>& net_params = this->net_->learnable_params();
  const vector<float>& net_params_weight_decay =
      this->net_->params_weight_decay();
  Dtype weight_decay = this->param_.weight_decay();//solver.prototxt中的惩罚项
  string regularization_type = this->param_.regularization_type();//在solver.prototxt中设置正则化类型,L1或L2
  Dtype local_decay = weight_decay * net_params_weight_decay[param_id];weight_decay//最终正则项的惩罚是solver.prtotxt中设置的惩罚项与layer中的decay_mult的乘积,例如,一般把bias的decay_mult设置为0,代表bias不加入到正则项的优化中。
  switch (Caffe::mode()) {
  case Caffe::CPU: {
    if (local_decay) {
      if (regularization_type == "L2") { 
        // add weight decay
        caffe_axpy(net_params[param_id]->count(),
            local_decay,
            net_params[param_id]->cpu_data(),
            net_params[param_id]->mutable_cpu_diff());
      } else if (regularization_type == "L1") {
        caffe_cpu_sign(net_params[param_id]->count(),
            net_params[param_id]->cpu_data(),
            temp_[param_id]->mutable_cpu_data());
        caffe_axpy(net_params[param_id]->count(),
            local_decay,
            temp_[param_id]->cpu_data(),
            net_params[param_id]->mutable_cpu_diff());
      } else {
        LOG(FATAL) << "Unknown regularization type: " << regularization_type;
      }
    }
    break;
  }

上述代码需要注意的是:
1.最终正则项的惩罚 是solver.prtotxt中设置的惩罚项weight_decay与layer中的decay_mult的乘积。若不想让某一些参数加入正则项中,则将layer中的对应param中的参数decay_mult设置为0。例如,一般把bias的decay_mult设置为0,代表bias不加入到正则项的优化中。

2.当 regularization_type==‘L2’时,调用
void caffe_axpy(const int N, const float alpha, const float* X,float* Y)
功能:Y=alpha*X+Y

L2正则的梯度计算:

L 2 ( θ ) θ i = θ i

因此若采用L2正则化就是直接将 参数 θ i 赋值给响应index的梯度。如,
net_params[param_id]->mutable_cpu_diff() : = net_params[param_id]->cpu_data()

3.当 regularization_type==‘L1’时,先调用caffe_cpu_sign函数,这个函数的功能是返回vector中的每个元素的符号,例如:
vector = {2,-16,5,-3};
caffe_cpu_sign(4,vector,result);
result的值就是{1,-1,1,-1}
之后将符号赋值给梯度,caffe_axpy

L1的梯度计算:

L 1 ( θ ) θ i = { 1 θ i >= 0 1 θ i < 0

当依次完成了 归一化Normalize, 正则化Regularize, 主干网络梯度计算ComputeUpdateValue,之后更新网络参数。

猜你喜欢

转载自blog.csdn.net/zbzb1000/article/details/80987347