我们在7.5节(AdaGrad算法)中提到,因为调整学习率时分母上的变量7.6 RMSProp算法 - 图1一直在累加按元素平方的小批量随机梯度,所以目标函数自变量每个元素的学习率在迭代过程中一直在降低(或不变)。因此,当学习率在迭代早期降得较快且当前解依然不佳时,AdaGrad算法在迭代后期由于学习率过小,可能较难找到一个有用的解。为了解决这一问题,RMSProp算法对AdaGrad算法做了一点小小的修改。该算法源自Coursera上的一门课程,即“机器学习的神经网络” [1]。

7.6.1 算法

我们在7.4节(动量法)里介绍过指数加权移动平均。不同于AdaGrad算法里状态变量7.6 RMSProp算法 - 图2是截至时间步7.6 RMSProp算法 - 图3所有小批量随机梯度7.6 RMSProp算法 - 图4按元素平方和,RMSProp算法将这些梯度按元素平方做指数加权移动平均。具体来说,给定超参数7.6 RMSProp算法 - 图5,RMSProp算法在时间步7.6 RMSProp算法 - 图6计算

7.6 RMSProp算法 - 图7%20%5Cboldsymbol%7Bg%7Dt%20%5Codot%20%5Cboldsymbol%7Bg%7D_t.%20%0A#card=math&code=%5Cboldsymbol%7Bs%7D_t%20%5Cleftarrow%20%5Cgamma%20%5Cboldsymbol%7Bs%7D%7Bt-1%7D%20%2B%20%281%20-%20%5Cgamma%29%20%5Cboldsymbol%7Bg%7D_t%20%5Codot%20%5Cboldsymbol%7Bg%7D_t.%20%0A)

和AdaGrad算法一样,RMSProp算法将目标函数自变量中每个元素的学习率通过按元素运算重新调整,然后更新自变量

7.6 RMSProp算法 - 图8

其中7.6 RMSProp算法 - 图9是学习率,7.6 RMSProp算法 - 图10是为了维持数值稳定性而添加的常数,如7.6 RMSProp算法 - 图11。因为RMSProp算法的状态变量7.6 RMSProp算法 - 图12是对平方项7.6 RMSProp算法 - 图13的指数加权移动平均,所以可以看作是最近7.6 RMSProp算法 - 图14#card=math&code=1%2F%281-%5Cgamma%29)个时间步的小批量随机梯度平方项的加权平均。如此一来,自变量每个元素的学习率在迭代过程中就不再一直降低(或不变)。

照例,让我们先观察RMSProp算法对目标函数7.6 RMSProp算法 - 图15%3D0.1x_1%5E2%2B2x_2%5E2#card=math&code=f%28%5Cboldsymbol%7Bx%7D%29%3D0.1x_1%5E2%2B2x_2%5E2)中自变量的迭代轨迹。回忆在7.5节(AdaGrad算法)使用的学习率为0.4的AdaGrad算法,自变量在迭代后期的移动幅度较小。但在同样的学习率下,RMSProp算法可以更快逼近最优解。

  1. %matplotlib inline
  2. import math
  3. import torch
  4. import sys
  5. sys.path.append("..")
  6. import d2lzh_pytorch as d2l
  7. def rmsprop_2d(x1, x2, s1, s2):
  8. g1, g2, eps = 0.2 * x1, 4 * x2, 1e-6
  9. s1 = gamma * s1 + (1 - gamma) * g1 ** 2
  10. s2 = gamma * s2 + (1 - gamma) * g2 ** 2
  11. x1 -= eta / math.sqrt(s1 + eps) * g1
  12. x2 -= eta / math.sqrt(s2 + eps) * g2
  13. return x1, x2, s1, s2
  14. def f_2d(x1, x2):
  15. return 0.1 * x1 ** 2 + 2 * x2 ** 2
  16. eta, gamma = 0.4, 0.9
  17. d2l.show_trace_2d(f_2d, d2l.train_2d(rmsprop_2d))

输出:

  1. epoch 20, x1 -0.010599, x2 0.000000

7.6_output1.png

7.6.2 从零开始实现

接下来按照RMSProp算法中的公式实现该算法。

  1. features, labels = d2l.get_data_ch7()
  2. def init_rmsprop_states():
  3. s_w = torch.zeros((features.shape[1], 1), dtype=torch.float32)
  4. s_b = torch.zeros(1, dtype=torch.float32)
  5. return (s_w, s_b)
  6. def rmsprop(params, states, hyperparams):
  7. gamma, eps = hyperparams['gamma'], 1e-6
  8. for p, s in zip(params, states):
  9. s.data = gamma * s.data + (1 - gamma) * (p.grad.data)**2
  10. p.data -= hyperparams['lr'] * p.grad.data / torch.sqrt(s + eps)

我们将初始学习率设为0.01,并将超参数7.6 RMSProp算法 - 图17设为0.9。此时,变量7.6 RMSProp算法 - 图18可看作是最近7.6 RMSProp算法 - 图19%20%3D%2010#card=math&code=1%2F%281-0.9%29%20%3D%2010)个时间步的平方项7.6 RMSProp算法 - 图20的加权平均。

  1. d2l.train_ch7(rmsprop, init_rmsprop_states(), {'lr': 0.01, 'gamma': 0.9},
  2. features, labels)

输出:

  1. loss: 0.243452, 0.049984 sec per epoch

7.6_output2.png

7.6.3 简洁实现

通过名称为RMSprop的优化器方法,我们便可使用PyTorch提供的RMSProp算法来训练模型。注意,超参数7.6 RMSProp算法 - 图22通过alpha指定。

  1. d2l.train_pytorch_ch7(torch.optim.RMSprop, {'lr': 0.01, 'alpha': 0.9},
  2. features, labels)

输出:

  1. loss: 0.243676, 0.043637 sec per epoch

7.6_output3.png

小结

  • RMSProp算法和AdaGrad算法的不同在于,RMSProp算法使用了小批量随机梯度按元素平方的指数加权移动平均来调整学习率。

参考文献

[1] Tieleman, T., & Hinton, G. (2012). Lecture 6.5-rmsprop: Divide the gradient by a running average of its recent magnitude. COURSERA: Neural networks for machine learning, 4(2), 26-31.


注:除代码外本节与原书此节基本相同,原书传送门