# RMSProp——从零开始 我们在[“Adagrad——从零开始”](adagrad-scratch.md)一节里提到,由于调整学习率时分母上的变量$\boldsymbol{s}$一直在累加按元素平方的小批量随机梯度,目标函数自变量每个元素的学习率在迭代过程中一直在降低(或不变)。所以,当学习率在迭代早期降得较快且当前解依然不佳时,Adagrad在迭代后期由于学习率过小,可能较难找到一个有用的解。为了应对这一问题,RMSProp算法对Adagrad做了一点小小的修改 [1]。 下面,我们来描述RMSProp算法。 ## RMSProp算法 我们在[“动量法——从零开始”](momentum-scratch.md)一节里介绍过指数加权移动平均。事实上,RMSProp算法使用了小批量随机梯度按元素平方的指数加权移动平均变量$\boldsymbol{s}$,并将其中每个元素初始化为0。 给定超参数$\gamma$且$0 \leq \gamma < 1$, 在每次迭代中,RMSProp首先计算小批量随机梯度$\boldsymbol{g}$,然后对该梯度按元素平方项$\boldsymbol{g} \odot \boldsymbol{g}$做指数加权移动平均,记为$\boldsymbol{s}$: $$\boldsymbol{s} \leftarrow \gamma \boldsymbol{s} + (1 - \gamma) \boldsymbol{g} \odot \boldsymbol{g}. $$ 然后,和Adagrad一样,将目标函数自变量中每个元素的学习率通过按元素运算重新调整一下: $$\boldsymbol{g}^\prime \leftarrow \frac{\eta}{\sqrt{\boldsymbol{s} + \epsilon}} \odot \boldsymbol{g}, $$ 其中$\eta$是初始学习率且$\eta > 0$,$\epsilon$是为了维持数值稳定性而添加的常数,例如$10^{-8}$。和Adagrad一样,模型参数中每个元素都分别拥有自己的学习率。同样地,最后的自变量迭代步骤与小批量随机梯度下降类似: $$\boldsymbol{x} \leftarrow \boldsymbol{x} - \boldsymbol{g}^\prime $$ 需要强调的是,RMSProp只在Adagrad的基础上修改了变量$\boldsymbol{s}$的更新方法:对平方项$\boldsymbol{g} \odot \boldsymbol{g}$从累加变成了指数加权移动平均。由于变量$\boldsymbol{s}$可看作是最近$1/(1-\gamma)$个时刻的平方项$\boldsymbol{g} \odot \boldsymbol{g}$的加权平均,自变量每个元素的学习率在迭代过程中既可能降低又可能升高。 ## RMSProp的实现 RMSProp的实现很简单。我们只需要把上面的数学公式翻译成代码。 ```{.python .input} # RMSProp def rmsprop(params, sqrs, lr, gamma, batch_size): eps_stable = 1e-8 for param, sqr in zip(params, sqrs): g = param.grad / batch_size sqr[:] = gamma * sqr + (1. - gamma) * nd.square(g) div = lr * g / nd.sqrt(sqr + eps_stable) param[:] -= div ``` ## 实验 首先,导入实验所需的包。 ```{.python .input} %config InlineBackend.figure_format = 'retina' %matplotlib inline import mxnet as mx from mxnet import autograd from mxnet import gluon from mxnet import nd import numpy as np import random import sys sys.path.append('..') import utils ``` 实验中,我们依然以线性回归为例。设数据集的样本数为1000,我们使用权重`w`为[2, -3.4],偏差`b`为4.2的线性回归模型来生成数据集。该模型的平方损失函数即所需优化的目标函数,模型参数即目标函数自变量。 我们把小批量随机梯度按元素平方的指数加权移动平均变量$\boldsymbol{s}$初始化为和模型参数形状相同的零张量。 ```{.python .input n=1} # 生成数据集。 num_inputs = 2 num_examples = 1000 true_w = [2, -3.4] true_b = 4.2 X = nd.random_normal(scale=1, shape=(num_examples, num_inputs)) y = true_w[0] * X[:, 0] + true_w[1] * X[:, 1] + true_b y += .01 * nd.random_normal(scale=1, shape=y.shape) # 初始化模型参数。 def init_params(): w = nd.random_normal(scale=1, shape=(num_inputs, 1)) b = nd.zeros(shape=(1,)) params = [w, b] sqrs = [] for param in params: param.attach_grad() # 把梯度按元素平方的指数加权移动平均变量初始化为和参数形状相同的零张量。 sqrs.append(param.zeros_like()) return params, sqrs ``` 优化函数`optimize`与[“Adagrad——从零开始”](adagrad-scratch.md)一节中的类似。 ```{.python .input n=2} net = utils.linreg squared_loss = utils.squared_loss def optimize(batch_size, lr, gamma, num_epochs, log_interval): [w, b], sqrs = init_params() y_vals = [squared_loss(net(X, w, b), y).mean().asnumpy()] print('batch size', batch_size) for epoch in range(1, num_epochs + 1): for batch_i, (features, label) in enumerate( utils.data_iter(batch_size, num_examples, X, y)): with autograd.record(): output = net(features, w, b) loss = squared_loss(output, label) loss.backward() rmsprop([w, b], sqrs, lr, gamma, batch_size) if batch_i * batch_size % log_interval == 0: y_vals.append(squared_loss(net(X, w, b), y).mean().asnumpy()) print('epoch %d, learning rate %f, loss %.4e' % (epoch, lr, y_vals[-1])) # 为了便于打印,改变输出形状并转化成numpy数组。 print('w:', w.reshape((1, -1)).asnumpy(), 'b:', b.asscalar(), '\n') x_vals = np.linspace(0, num_epochs, len(y_vals), endpoint=True) utils.semilogy(x_vals, y_vals, 'epoch', 'loss') ``` 我们将初始学习率设为0.03,并将$\gamma$(`gamma`)设为0.9。此时,变量$\boldsymbol{s}$可看作是最近$1/(1-0.9) = 10$个时刻的平方项$\boldsymbol{g} \odot \boldsymbol{g}$的加权平均。我们观察到,损失函数在迭代后期较震荡。 ```{.python .input n=3} optimize(batch_size=10, lr=0.03, gamma=0.9, num_epochs=3, log_interval=10) ``` 我们将$\gamma$调大一点,例如0.999。此时,变量$\boldsymbol{s}$可看作是最近$1/(1-0.999) = 1000$个时刻的平方项$\boldsymbol{g} \odot \boldsymbol{g}$的加权平均。这时损失函数在迭代后期较平滑。 ```{.python .input} optimize(batch_size=10, lr=0.03, gamma=0.999, num_epochs=3, log_interval=10) ``` ## 小结 * RMSProp和Adagrad的不同在于,RMSProp使用了小批量随机梯度按元素平方的指数加权移动平均变量来调整学习率。 * 理解指数加权移动平均有助于我们调节RMSProp算法中的超参数,例如$\gamma$。 ## 练习 * 把$\gamma$的值设为0或1,观察并分析实验结果。 ## 讨论 欢迎扫码直达[本节内容讨论区](https://discuss.gluon.ai/t/topic/2275): ![](../img/qr_rmsprop-scratch.svg) ## 参考文献 [1] Hinton, G., Srivastava, N., & Swersky, K. (2012). Lecture 6a Overview of Mini–batch Gradient Descent. Coursera Lecture Slides.