# Adagrad——从零开始 在我们之前介绍过的优化算法中,无论是梯度下降、随机梯度下降、小批量随机梯度下降还是使用动量法,目标函数自变量的每一个元素在相同时刻都使用同一个学习率来自我迭代。 举个例子,假设目标函数为$f$,自变量为一个多维向量$[x_1, x_2]^\top$,该向量中每一个元素在更新时都使用相同的学习率。例如在学习率为$\eta$的梯度下降中,元素$x_1$和$x_2$都使用相同的学习率$\eta$来自我迭代: $$ x_1 \leftarrow x_1 - \eta \frac{\partial{f}}{\partial{x_1}}, \\ x_2 \leftarrow x_2 - \eta \frac{\partial{f}}{\partial{x_2}}. $$ 如果让$x_1$和$x_2$使用不同的学习率自我迭代呢?实际上,Adagrad就是一个在迭代过程中不断自我调整学习率,并让模型参数中每个元素都使用不同学习率的优化算法 [1]。 下面,我们将介绍Adagrad算法。关于本节中涉及到的按元素运算,例如标量与向量计算以及按元素相乘$\odot$,请参见[“数学基础”](../chapter_appendix/math.md)一节。 ## Adagrad算法 Adagrad的算法会使用一个小批量随机梯度按元素平方的累加变量$\boldsymbol{s}$,并将其中每个元素初始化为0。在每次迭代中,首先计算小批量随机梯度$\boldsymbol{g}$,然后将该梯度按元素平方后累加到变量$\boldsymbol{s}$: $$\boldsymbol{s} \leftarrow \boldsymbol{s} + \boldsymbol{g} \odot \boldsymbol{g}. $$ 然后,我们将目标函数自变量中每个元素的学习率通过按元素运算重新调整一下: $$\boldsymbol{g}^\prime \leftarrow \frac{\eta}{\sqrt{\boldsymbol{s} + \epsilon}} \odot \boldsymbol{g},$$ 其中$\eta$是初始学习率且$\eta > 0$,$\epsilon$是为了维持数值稳定性而添加的常数,例如$10^{-7}$。我们需要注意其中按元素开方、除法和乘法的运算。这些按元素运算使得目标函数自变量中每个元素都分别拥有自己的学习率。 最后,自变量的迭代步骤与小批量随机梯度下降类似。只是这里梯度前的学习率已经被调整过了: $$\boldsymbol{x} \leftarrow \boldsymbol{x} - \boldsymbol{g}^\prime.$$ ## Adagrad的特点 需要强调的是,小批量随机梯度按元素平方的累加变量$\boldsymbol{s}$出现在含调整后学习率的梯度$\boldsymbol{g}^\prime$的分母项。因此,如果目标函数有关自变量中某个元素的偏导数一直都较大,那么就让该元素的学习率下降快一点;反之,如果目标函数有关自变量中某个元素的偏导数一直都较小,那么就让该元素的学习率下降慢一点。然而,由于$\boldsymbol{s}$一直在累加按元素平方的梯度,自变量中每个元素的学习率在迭代过程中一直在降低(或不变)。所以,当学习率在迭代早期降得较快且当前解依然不佳时,Adagrad在迭代后期由于学习率过小,可能较难找到一个有用的解。 ## Adagrad的实现 Adagrad的实现很简单。我们只需要把上面的数学公式翻译成代码。 ```{.python .input n=1} # Adagrad算法。 def adagrad(params, sqrs, lr, batch_size): eps_stable = 1e-7 for param, sqr in zip(params, sqrs): g = param.grad / batch_size sqr[:] += 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 ``` 实验中,我们以之前介绍过的线性回归为例。我们使用权重w为[2, -3.4],偏差b为4.2的线性回归模型来生成数据集。数据集的样本数为1000。目标函数为平方损失函数。 我们把梯度按元素平方的累加变量初始化为和参数形状相同的零张量。 ```{.python .input n=2} # 生成数据集。 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`与[“梯度下降和随机梯度下降”](gd-sgd-scratch.md)一节中的类似。需要指出的是,这里的初始学习率`lr`无需自我衰减。 ```{.python .input n=3} net = utils.linreg squared_loss = utils.squared_loss def optimize(batch_size, lr, 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() adagrad([w, b], sqrs, lr, 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') ``` 最终,优化所得的模型参数值与它们的真实值较接近。 ```{.python .input n=4} optimize(batch_size=10, lr=0.9, num_epochs=3, log_interval=10) ``` ## 小结 * Adagrad在迭代过程中不断调整学习率,并让目标函数自变量中每个元素都分别拥有自己的学习率。 * 使用Adagrad时,自变量中每个元素的学习率在迭代过程中一直在降低(或不变)。 ## 练习 * 在介绍Adagrad的特点时,我们提到了它可能存在的问题。你能想到什么办法来应对这个问题? ## 讨论 欢迎扫码直达[本节内容讨论区](https://discuss.gluon.ai/t/topic/2273): ![](../img/qr_adagrad-scratch.svg) ## 参考文献 [1] Duchi, J., Hazan, E., & Singer, Y. (2011). Adaptive Subgradient Methods for Online Learning and Stochastic Optimization. Journal of Machine Learning Research, 12(Jul), 2121-2159.