caffe的正则项理解
在神经网络的cost function中,出了主目标函以外,往往伴有正则项。如下述公式,
其中 叫做惩罚项系数, 正则是 , 并且 正则是 .
正则项的目的可以参考博客,
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正则的梯度计算:
因此若采用L2正则化就是直接将 参数
赋值给响应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的梯度计算:
当依次完成了 归一化Normalize, 正则化Regularize, 主干网络梯度计算ComputeUpdateValue,之后更新网络参数。