深度学习优化算法---动量法
动量法(Momentum Method)
梯度下降的问题
import rnn
import torch
import utl
eta = 0.4
def f_2d(x1, x2):
return 0.1 * x1**2 + 2 * x2**2
def gd_2d(x1, x2, s1, s2):
return (x1 - eta * 0.2 * x1, x2 - eta * 4 * x2, 0, 0)
utl.show_trace_2d(f_2d, utl.train_2d(gd_2d))
epoch 20, x1 -0.943467, x2 -0.000073

可以看到目标函数在竖直方向上比在水平方向上的斜率更大。因此,给定学习率,梯度下降迭代自变量时会使自变量在竖直方向比水平方向上的移动更大。
那么我们需要一个较小的学习率防止自变量在竖直方向上超过目标函数的最优解。然而这会造成自变量在水平方向上的移动变慢。
我们试着把学习率调的大些,此时自变量在竖直方向不断越过最优解并逐渐发散。(不能收敛到最优解)
eta = 0.6
utl.show_trace_2d(f_2d, utl.train_2d(gd_2d))
epoch 20, x1 -0.387814, x2 -1673.365109

动量法
动量法是模拟物理中的概念:一个物体的动量是它在运动方向上保持运动的趋势。
动量法是用之前累计的动量来替代真正的梯度,每次迭代的梯度可以看作加速度。
其中$ v_0 = 0 $, $ \gamma $ 为动量因子,$ \eta $为学习率。
这样每个参数的实际更新差值取决于最近一段时间内的梯度的加权平均值。
当某一个参数在最近一段时间内的梯度方向不一致时,其真实参数梯度更新幅度小变小,起到减速作用;当某一个参数在最近一段时间内的梯度方向都一致时,其真实参数梯度更新幅度变大,起到加速作用。
在训练迭代前期,梯度更新方向差不多都一致,动量法起到加速作用,可以帮助前期更快到达最优点附近;在训练迭代后期,梯度更新方向会不一致,在收敛值附近震荡,动量法起到减速作用,增加稳定性。
从某种角度来说,当前梯度叠加上次梯度,一定程度上可以近似看作二阶梯度。
eta, gamma = 0.5, 0.4
def momentum_2d(x1, x2, v1, v2):
v1 = gamma * v1 + eta * 0.2 * x1
v2 = gamma * v2 + eta * 4 * x2
return x1 - v1, x2 - v2, v1, v2
utl.show_trace_2d(f_2d, utl.train_2d(momentum_2d))
epoch 20, x1 -0.076860, x2 0.000071

动量法在竖直方向上移动的更加平滑,且在水平方向上的移动一更快逼近最优解。
下面使用大一点的学习率,此时自变量不在发散。(可以收敛到最优解)
eta = 0.6
utl.show_trace_2d(f_2d, utl.train_2d(momentum_2d))
epoch 20, x1 -0.019779, x2 -0.000452

指数加权移动平均(exponentially weighted moving average)
设
对$ y_i $进行展开:
其中令 $ n = \frac{1}{1-\gamma} $ ,那么$ (1-\frac{1}{n})^n = \gamma^{\frac{1}{1-\gamma}} $,因为:
如果把 $ e_{-1} $ 看作为一个较小的数,我们可以在近似中忽略所有比 $ \frac{1}{1-\gamma} $ 阶更高的系数的项。
如果把 $ y_t $ 看作是对最近 $ \frac{1}{1-\gamma} $ 个时间步的 $ x_t $ 的加权平均。例如,当 $ \gamma = 0.95 $ 时, $ y_t $ 可以看作为最近20个时间步的 $ x_t $ 值的加权平均。
动量法在每个时间步的自变量更新量近似于将最近的$ \frac{1}{1-\gamma} $个时间步更新量做了指数加权平均后再除以 $ 1-\gamma $
features, labels = utl.get_data()
def init_momentum_states():
v_w = torch.zeros((features.shape[1], 1), dtype=torch.float32)
v_b = torch.zeros(1, dtype=torch.float32)
return(v_w, v_b)
def sgd_momentum(params, states, hyperparams):
for p, v in zip(params, states):
v.data = hyperparams['momentum'] * v.data + hyperparams['lr'] * p.grad.data
p.data -= v.data
utl.train_ch7(sgd_momentum, init_momentum_states(), {'lr':0.02, 'momentum':0.5}, features, labels)
loss 0.24, 0.07 sec per epoch
utl.train_ch7(sgd_momentum, init_momentum_states(), {'lr':0.02, 'momentum':0.9}, features, labels)
loss 0.25, 0.08 sec per epoch
utl.train_ch7(sgd_momentum, init_momentum_states(), {'lr':0.004, 'momentum':0.9}, features, labels)
loss 0.24, 0.10 sec per epoch
简洁实现
utl.train_ch7_pytorch(torch.optim.SGD, {'lr':0.004, 'momentum':0.9}, features, labels)
loss 0.24, 0.08 sec per epoch
参考自:
https://tangshusen.me/Dive-into-DL-PyTorch/#/chapter07_optimization/7.2_gd-sgd
《神经网络与深度学习》邱锡鹏

浙公网安备 33010602011771号