3.1.1 Solver介绍
Caffe的重中之重(核心)——Solver。负责对模型优化,让损失函数(loss function)达到全局最小。solver的主要作用就是交替调用前向(forward)算法和后向(backward)算法来更新参数,实际上就是一种迭代的优化算法。
到目前的版本,caffe提供了六种优化算法来求解最优参数,在solver配置文件中,通过设置type类型来选择。
• Stochastic Gradient Descent (type: "SGD")
• AdaDelta (type: "AdaDelta")
• Adaptive Gradient (type: "AdaGrad")
• Adam (type: "Adam")
• Nesterov’s Accelerated Gradient (type: "Nesterov")
• RMSprop (type: "RMSProp")
在每一次的迭代过程中,solver做了这几步工作:
1、调用forward算法来计算最终的输出值,以及对应的loss
2、调用backward算法来计算每层的梯度
3、根据选用的slover方法,利用梯度进行参数更新
4、记录并保存每次迭代的学习率、快照,以及对应的状态。
3.1.2 Solver参数配置
$ cd caffe/src/caffe/proto
$ vim caffe.proto
message SolverParameter { // train_net_param, train_net, net_param, net .... } 241 optional SolverType solver_type = 30 [default = SGD]; |
#结合实例讲解
net: "examples/mnist/lenet_train_test.prototxt" //设置深度网络模型。每一个模型就是一个net,需要在一个专门的配置文件中对net进行配置,每个net 由许多的layer所组成。 //注意的是:文件的路径要从caffe的根目录开始,其它的所有配置都是这样,也可用train_net和test_net来对训练模型和测试模型分别设定: test_iter: 100 //mnist数据中测试样本总数为10000,一次性执行全部数据效率很低,因此我们将测试数据分成几个批次来执行,每个批次的数量就是batch_size。假设我们设置batch_size为100,则需要迭代100次才能将10000个数据全部执行完。因此test_iter设置为100。执行完一次全部数据,称之为一个epoch。 test_interval: 500 //测试间隔,在训练集中每迭代500次,在测试集进行一次测试。 base_lr: 0.01 //设置基础学习率,在迭代中,可以对基础学习率进行调整。调整策略见Ir_policy momentum: 0.9 //上一次梯度更新的权重 type: SGD //优化算法选择,这一行可以省掉,因为默认值是SGD,随机梯度下降法。 weight_decay: 0.0005 //权重衰减项,防止过拟合的一个参数。 lr_policy: "inv" //调整策略,以下是可以设置的值和相应的学习率计算 • fixed: 保持base_lr不变. • step: 如果设置为step,则还需要设置一个stepsize, 返回 base_lr * gamma ^ (floor(iter / stepsize)),其中iter表示当前的迭代次数 • exp: 返回base_lr * gamma ^ iter, iter为当前迭代次数 • inv: 如果设置为inv,还需要设置一个power, 返回base_lr * (1 + gamma * iter) ^ (- power) •multistep: 如果设置为multistep,则还需要设置一个stepvalue。这个参数和step很相似,step是均匀等间隔变化,而multistep则是根据stepvalue值变化 • poly: 学习率进行多项式误差, 返回 base_lr (1 - iter/max_iter) ^ (power) • sigmoid: 学习率进行sigmod衰减,返回 base_lr ( 1/(1 + exp(-gamma * (iter - stepsize)))) gamma: 0.0001 power: 0.75 display: 100 //每训练100次,在屏幕上显示一次。如果设置为0,则不显示。 max_iter: 20000 //最大迭代次数,这个数设置太小,会导致没有收敛,精确度很低。设置太大,会导致震荡,浪费时间。 snapshot: 5000 snapshot_prefix: "examples/mnist/lenet" // 将训练出来的model和solver状态进行保存,snapshot用于设置训练多少次后进行保存,默认为0,不保存。snapshot_prefix设置保存路径。 还可以设置snapshot_diff,是否保存梯度值,默认为false,不保存。 也可以设置snapshot_format,保存的类型。有两种选择:HDF5 和BINARYPROTO ,默认为BINARYPROTO
solver_mode: CPU |
3.1.3 Solver 优化方式
• Stochastic Gradient Descent (type: "SGD"):即随机梯度下降,是梯度下降的batch版本
SGD | 对于训练数据集,我们首先将其分成n个batch,每个batch包含m个样本。我们每次更新都利用一个batch的数据,而非整个训练集。即: 其中, 为学习率, 为x在t时刻的梯度。 |
好处: 当训练数据太多时,利用整个数据集更新往往时间上不显示。batch的方法可以减少机器的压力,并且可以更快地收敛。 当训练集有很多冗余时(类似的样本出现多次),batch方法收敛更快。以一个极端情况为例,若训练集前一半和后一半梯度相同。那么如果前一半作为一个batch,后一半作为另一个batch,那么在一次遍历训练集时,batch的方法向最优解前进两个step,而整体的方法只前进一个step。 缺点: SGD方法的一个缺点是,其更新方向完全依赖于当前的batch,因而其更新十分不稳定。 |
• AdaDelta (type: "AdaDelta"):Adadelta是对Adagrad的扩展,最初方案依然是对学习率进行自适应约束,但是进行了计算上的简化。
AdaDelta | Adagrad会累加之前所有的梯度平方,而Adadelta只累加固定大小的项,并且也不直接存储这些项,仅仅是近似计算对应的平均值。即: 在此处Adadelta其实还是依赖于全局学习率,经过一定处理,经过牛顿迭代法之后: 其中,E代表求期望。 此时,Adadelta已经不用依赖于全局学习率了。 |
特点: 训练初中期,加速效果不错,很快 训练后期,反复在局部最小值附近抖动
|
• Adaptive Gradient (type: "AdaGrad")
AdaGrad | Adagrad其实是对学习率进行了约束。即: 此处,对 从1到t进行一个递推形成一个约束项regularizer, , 用来保证分母非0。
|
特点: 前期gt 较小的时候, regularizer较大,能够放大梯度 后期gt 较大的时候,regularizer较小,能够约束梯度 适合处理稀疏梯度 缺点: 由公式可以看出,仍依赖于人工设置一个全局学习率η 设置过大的话,会使regularizer过于敏感,对梯度的调节太大,中后期,分母上梯度平方的累加将会越来越大,使gradient→0 ,使得训练提前结束。 |
• Adam (type: "Adam"):本质上是带有动量项的RMSprop,它利用梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率。
Adam | 其中,mt ,nt 分别是对梯度的一阶矩估计和二阶矩估计,可以看作对期望E|gt| ,E|g2t| 的估计;mt^ ,nt^ 是对mt ,nt 的校正,这样可以近似为对期望的无偏估计。 |
Adam的优点主要在于经过偏置校正后,每一次迭代学习率都有个确定范围,使得参数比较平稳。 特点: 结合了Adagrad善于处理稀疏梯度和RMSprop善于处理非平稳目标的优点 对内存需求较小 为不同的参数计算不同的自适应学习率 也适用于大多非凸优化 适用于大数据集和高维空间 |
• Nesterov’s Accelerated Gradient (type: "Nesterov"):nesterov项在梯度更新时做一个校正,避免前进太快,同时提高灵敏度。
Nesterov |
|
momentum首先计算一个梯度(短的蓝色向量),然后在加速更新梯度的方向进行一个大的跳跃(长的蓝色向量)。 nesterov项首先在之前加速的梯度方向进行一个大的跳跃(棕色向量),计算梯度然后进行校正(绿色梯向量)。 |
• RMSprop (type: "RMSProp"):可以算作Adadelta的一个特例。
RMSProp | 当 时, 就变成了求梯度平方和的平均数。 如果再求根的话,就变成了RMS(均方根): 此时,这个RMS就可以作为学习率 的一个约束: |
特点: • 其实RMSprop依然依赖于全局学习率 • RMSprop算是Adagrad的一种发展,和Adadelta的变体,效果趋于二者之间 • 适合处理非平稳目标 • 对于RNN效果很好 |