【Caffe细致入微】Solver_Step
2016-12-30 11:47
295 查看
void Solver::Step(int iters)
【Introduction】简单的说,这个函数就是核心的优化方法,不断通过前向和反向传播来更新参数的过程。
【Loss处理——均值滤波(广泛应用)】
template <typename Dtype> void Solver<Dtype>::UpdateSmoothedLoss(Dtype loss, int start_iter, int average_loss)
每一次训练迭代都会进行前向传播,就会得到loss值。这个函数的作用就是把几次的loss进行平均化处理。
若average_loss为1:loss_容器里面只存当前获得的真实loss值,而smooth_loss_当然也是这个值
若average_loss为n:loss_容器里面就会存储前n个loss的值,而smooth_loss_相当于做了一个loss平均
【你可能会问】这么做好处是什么?
当然无论average_loss为多少,最终都是为了展示训练之后的loss,但是我们当然要从loss中得到一些信息和经验,比如,这一阶段的训练的效果等。平均值会比个体值更加客观的刻画整个集合。做这样的平均其实应用有很多,主要是去除掉一些噪音,让得到的值更加可信。caffe中默认average_loss为1。
【讲故事1】头部姿态估计
因为评估算法会产生一些误差,但是大部分估计的较准确,就可以利用中值滤波进行去除噪声。上图可以说明问题。Yaw和Pitch方向的Ground Truth都是0°,但是因为算法和环境的因素产生一些误差。
【讲故事2】 眼部疲劳检测
需要评估一段时间是睁眼还是闭眼这个事件,因为有眨眼这个动作,但是这个动作非常快,基本上不需要进行判断,属于噪声,假如每一帧对眼睛都会有个阈值来进行睁闭眼的判断,这个时候就需要用到上述方法了,如果是睁开眼睛的事件,一段时间会有相对很少的几个值是闭眼睛的阈值,做平均就会把这个值过滤掉,反之依然。这样就达到了滤波的作用。
【重要变量List】
// 进行平滑loss处理的loss数量 int average_loss; // 存储average_loss个loss的容器,初始化的时候需要真实的loss一个一个添加 vector<Dtype> losses_; // 平滑后的loss Dtype smoothed_loss_; // 一次迭代训练中进行几次前向和反向传播 param_.iter_size() // TODO:还没搞懂。。。 vector<Callback*> callbacks_; // True iff a request to stop early was received. bool requested_early_exit_;
batch_size设多少合适?
一般的,batch_size增加,利用更多的数据一起进行梯度方向的判断,这样会更加接近整体样本,得到的梯度方向更加客观和相对准确,但是很多情况下,内存不足以一次加载batch_size个样本,所以需要设置param_.iter_size()。作用相当于进行一次训练迭代,需要进行param_.iter_size()次取batch_size个训练数据进行前向和反向传播。可以达到在有限的内存情况下增加batch_size效果。
当然,batch_size与训练集的总数,base_lr和任务本身的难度和样本的选取都有关系,是一个经验参数。
【测试阶段】
// 遍历所有的测试网络,当前只说明一个测试网络的情况 void Solver<Dtype>::TestAll(); // 对某个测试网络进行测试,获得相应任务的Loss void Solver<Dtype>::Test(const int test_net_id)
首先需要的是几个Solver里面的变量。
test_iter:在一次测试中进行test_iter次迭代,每次迭代选取batch_size个测试集中的数据,最好是test_iter*batch_size = 测试集数据总数
test_interval:训练权值更新test_interval次后会进行一次测试,这个只是相当于测试频率,可大可小
CHECK_NOTNULL(test_nets_[test_net_id].get())->ShareTrainedLayersWith(net_.get());
测试时,训练网络的参数赋值给测试网络,例如:卷积层、ReLU的参数进行共享。
【你可能会问】为什么要训练网络参数赋值给测试网络那? 直接利用训练网络添加一些控制变量不行么?
【参数传播】
这部分主要涉及前向和反向传播操作,后续文章我会继续说明。【权值更新——SGD说明】
template <typename Dtype> void SGDSolver<Dtype>::ApplyUpdate() template <typename Dtype> void SGDSolver<Dtype>::ClipGradients() template <typename Dtype> void SGDSolver<Dtype>::Normalize(int param_id) template <typename Dtype> void SGDSolver<Dtype>::Regularize(int param_id) template <typename Dtype> void SGDSolver<Dtype>::ComputeUpdateValue(int param_id, Dtype rate)
程序会依次调用上述函数。咱们一个一个说明。
ClipGradients On the difficulty of training Recurrent Neural Networks
当在一次迭代中权重的更新过于迅猛的话,很容易导致loss divergence,出现gradient explosion的问题。clip_gradient 的直观作用就是让权重的更新限制在一个合适的范围。
具体的细节是:
1、在solver中先设置一个clip_gradient
2、在前向传播与反向传播之后,我们会得到每个权重的梯度diff,这时不像通常那样直接使用这些梯度进行权重更新,而是先求所有权重梯度的平方和 sumsq_diff,如果sumsq_diff > clip_gradient,则求缩放因子scale_factor = clip_gradient / sumsq_diff。这个scale_factor在(0,1)之间。如果权重梯度的平方和sumsq_diff越大,那缩放因子将越小。
3、最后将所有的权重梯度乘以这个缩放因子,这时得到的梯度才是最后的梯度信息。
这样就保证了在一次迭代更新中,所有权重的梯度的平方和在一个设定范围以内,这个范围就是clip_gradient。
Normalize 归一化
若param_.iter_size()!=1时,在一次训练迭代进行多次前向和反向传播,每一次都会产生梯度,要把他们一起看做一次前向和方向传播,所以需要把梯度归一化,就是都乘个系数(Dtype(1.) / this->param_.iter_size())。
Regularize 正则化 A Simple Weight Decay Can Improve Generalization
防止过拟合,利用参数和weight_decay作为惩罚项,caffe中分为L2和L1正则规则。
ComputeUpdateValue 利用梯度更新参数
参数都准备完成,利用momentum来更新参数。
相关文章推荐
- Caffe solver.net.forward(),solver.test_nets[0].forward() 和 solver.step(1)
- caffe源码解析 — solver.cpp
- pycaffe install step
- caffe solver.ptototxt详解
- Caffe学习系列(7):solver及其配置
- solver优化方法_caffe
- CAFFE源码学习之优化方法solver
- 流程管理细致入微:利or弊?
- windows7 下编译 Microsoft的 caffe step by step
- Caffe solver.prototxt 文件参数详解
- caffe solver参数解析
- caffe学习 - solver的优化方法
- Caffe:solver及其配置
- caffe中参数含义solver文件<二>
- step by step install Caffe
- Caffe学习系列(7):solver及其配置
- 【caffe源码研究】第三章:源码篇(4) :Solver
- Caffe使用step by step:caffe框架下的基本操作和分析
- caffe solver configuration
- Caffe实践C++源码解读(2):走入Solver