提交 6345c65a 编写于 作者: A Aston Zhang

revise momentum and adagrad

上级 7d1a13a6
......@@ -39,11 +39,9 @@ $$\hat{y}_1, \hat{y}_2, \hat{y}_3 = \text{softmax}(o_1, o_2, o_3),$$
其中
$$
\begin{aligned}
\hat{y}_1 = \frac{ \exp(o_1)}{\sum_{i=1}^3 \exp(o_i)},\\
\hat{y}_2 = \frac{ \exp(o_2)}{\sum_{i=1}^3 \exp(o_i)},\\
\hat{y}_1 = \frac{ \exp(o_1)}{\sum_{i=1}^3 \exp(o_i)},\quad
\hat{y}_2 = \frac{ \exp(o_2)}{\sum_{i=1}^3 \exp(o_i)},\quad
\hat{y}_3 = \frac{ \exp(o_3)}{\sum_{i=1}^3 \exp(o_i)}.
\end{aligned}
$$
容易看出$\hat{y}_1 + \hat{y}_2 + \hat{y}_3 = 1$且$\hat{y}_1 > 0, \hat{y}_2 > 0, \hat{y}_3 > 0$,因此$\hat{y}_1, \hat{y}_2, \hat{y}_3$是一个合法的概率分布。这时候,如果$\hat y_2=0.8$,不管其他两个值多少,我们都知道有80%概率图片里是猫。此外,可以注意到
......
......@@ -8,30 +8,31 @@ x_1 \leftarrow x_1 - \eta \frac{\partial{f}}{\partial{x_1}}, \quad
x_2 \leftarrow x_2 - \eta \frac{\partial{f}}{\partial{x_2}}.
$$
[“动量法”](./momentum.md)一节里我们看到当$x_1$和$x_2$的梯度值有较大差别时(我们使用的例子是20倍不同),我们需要选择足够小的学习率使得梯度较大的维度不发散,但这样会导致梯度较小的维度收敛缓慢。动量依赖指数加权移动平均来使得自变量的更新方向更加一致来降低发散可能。这一节我们介绍Adagrad算法,它根据每个维度的数值大小来自动调整学习率,从而避免统一的学习率难以适应所有维度的问题。
[“动量法”](./momentum.md)一节里我们看到当$x_1$和$x_2$的梯度值有较大差别时,我们需要选择足够小的学习率使得自变量在梯度值较大的维度上不发散。但这样会导致自变量在梯度值较小的维度上迭代过慢。动量法依赖指数加权移动平均使得自变量的更新方向更加一致,从而降低发散的可能。这一节我们介绍Adagrad算法,它根据自变量在每个维度的梯度值的大小来调整各个维度上的学习率,从而避免统一的学习率难以适应所有维度的问题。
## Adagrad算法
Adagrad的算法对每个维度维护其所有时间步里梯度的平法累加。在算法开始前我们定义累加变量$\boldsymbol{s}$,其元素个数等于自变量的个数,并将其中每个元素初始化为0。在每次迭代中,假设小批量随机梯度为$\boldsymbol{g}$,我们将该梯度按元素平方后累加到变量$\boldsymbol{s}$里
Adagrad的算法会使用一个小批量随机梯度按元素平方的累加变量$\boldsymbol{s}$,其形状与自变量形状相同。开始时将变量$\boldsymbol{s}$中每个元素初始化为0。在每次迭代中,首先计算小批量随机梯度$\boldsymbol{g}$,然后将该梯度按元素平方后累加到变量$\boldsymbol{s}$
$$\boldsymbol{s} \leftarrow \boldsymbol{s} + \boldsymbol{g} \odot \boldsymbol{g}, $$
$$\boldsymbol{s} \leftarrow \boldsymbol{s} + \boldsymbol{g} \odot \boldsymbol{g},$$
这里$\odot$是按元素相乘(请参见[“数学基础”](../chapter_appendix/math.md)一节)。
其中$\odot$是按元素相乘(请参见[“数学基础”](../chapter_appendix/math.md)一节)。接着,我们将目标函数自变量中每个元素的学习率通过按元素运算重新调整一下:
在自变量更新前,我们将梯度中的每个元素除以累加变量中对应元素的平方根,这样使得每个元素数值在同样的尺度下,然后再乘以学习率后更新:
$$\boldsymbol{g}' \leftarrow \frac{\eta}{\sqrt{\boldsymbol{s} + \epsilon}} \odot \boldsymbol{g},$$
$$\boldsymbol{x} \leftarrow \boldsymbol{x} - \frac{\eta}{\sqrt{\boldsymbol{s} + \epsilon}} \odot \boldsymbol{g},$$
其中$\eta$是初始学习率且$\eta > 0$,$\epsilon$是为了维持数值稳定性而添加的常数,例如$10^{-7}$。我们需要注意,其中开方、除法和乘法的运算都是按元素进行的。这些按元素运算使得目标函数自变量中每个元素都分别拥有自己的学习率。
这里开方、除法和乘法的运算都是按元素进行的,$\epsilon$是为了使得除数不为0来而添加的正常数,例如$10^{-7}$。
最后,自变量的迭代步骤与小批量随机梯度下降类似。只是这里梯度前的学习率已经被调整过了:
## Adagrad特性
$$\boldsymbol{x} \leftarrow \boldsymbol{x} - \boldsymbol{g}'.$$
为了更好的理解累加变量是如何将每个自变量的更新变化到同样尺度,我们来看时间步1时的更新,这时候$\boldsymbol{s} = \boldsymbol{g} \odot \boldsymbol{g}$,忽略掉$\epsilon$的话,这时候的更新是$\boldsymbol{x} \leftarrow \boldsymbol{x} - \eta\cdot\textrm{sign}(\boldsymbol{g})$,这里$\textrm{sign}$是按元素的取符号。就是说,不管梯度具体值是多少,此时的每个自变量的更新量只是$\eta$,$-\eta$或0。
从另一个角度来看,如果自变量中某个元素取值总是另外一个元素的数倍,例如$x_2=20x_1$,那么其梯度也是20被的关系,那么在Adagrad里这两个元素的更新量总是一样,而不是20倍的关系。
此外,由于累加变量里我们总是累加,所以其会变得越来越大,等效于我们一直减低学习率。例如如果每个时间步的梯度都是常数$c$,那么时间步$t$的学习率就是$\frac{\eta}{c\sqrt{t}}$,其以平方根的速度依次递减。
## Adagrad的特点
需要强调的是,小批量随机梯度按元素平方的累加变量$\boldsymbol{s}$出现在含调整后学习率的梯度$\boldsymbol{g}'$的分母项。因此,如果目标函数有关自变量中某个元素的偏导数一直都较大,那么就让该元素的学习率下降快一点;反之,如果目标函数有关自变量中某个元素的偏导数一直都较小,那么就让该元素的学习率下降慢一点。然而,由于$\boldsymbol{s}$一直在累加按元素平方的梯度,自变量中每个元素的学习率在迭代过程中一直在降低(或不变)。所以,当学习率在迭代早期降得较快且当前解依然不佳时,Adagrad在迭代后期由于学习率过小,可能较难找到一个有用的解。
......@@ -50,49 +51,7 @@ import numpy as np
import math
```
我们先实现一个简单的针对二维目标函数$f(\boldsymbol{x})=0.1x_1^2+2x_2$的Adagrad来查看其自变量更新轨迹。
```{.python .input}
f = lambda x1, x2: 0.1*x1**2 + 2*x2**2
f_grad = lambda x1, x2: (0.2*x1, 2*x2)
def adagrad(eta):
x1, x2 = -5, -2
sx1, sx2 = 0, 0
eps = 1e-7
res = [(x1, x2)]
for i in range(15):
gx1, gx2 = f_grad(x1, x2)
sx1 += gx1 ** 2
sx2 += gx2 ** 2
x1 -= eta / math.sqrt(sx1 + eps) * gx1
x2 -= eta / math.sqrt(sx2 + eps) * gx2
res.append((x1, x2))
return res
def show(res):
x1, x2 = zip(*res)
gb.set_figsize((3.5, 2.5))
gb.plt.plot(x1, x2, '-o')
x1 = np.arange(-5.0, 1.0, .1)
x2 = np.arange(min(-3.0, min(x2)-1), max(3.0, max(x2)+1), .1)
x1, x2 = np.meshgrid(x1, x2)
gb.plt.contour(x1, x2, f(x1, x2), colors='g')
gb.plt.xlabel('x1')
gb.plt.ylabel('x2')
show(adagrad(.9))
```
可以看到使用$\eta=0.9$,Adagrad的更新轨迹非常平滑。但由于其自有的降低学习率特性,我们看到在后期收敛比较缓慢。这个特性同样也使得我们可以在Adagrad中使用更大的学习率。
```{.python .input}
show(adagrad(2))
```
接下来我们以之前介绍过的线性回归为例。设数据集的样本数为1000,我们使用权重`w`为[2, -3.4],偏差`b`为4.2的线性回归模型来生成数据集。该模型的平方损失函数即所需优化的目标函数,模型参数即目标函数自变量。
实验中,我们以之前介绍过的线性回归为例。设数据集的样本数为1000,我们使用权重`w`为[2, -3.4],偏差`b`为4.2的线性回归模型来生成数据集。该模型的平方损失函数即所需优化的目标函数,模型参数即目标函数自变量。
我们把梯度按元素平方的累加变量初始化为和模型参数形状相同的零张量。
......@@ -119,7 +78,7 @@ def init_params():
return params, sqrs
```
接下来基于NDArray来实现Adagrad
接下来基于NDArray实现Adagrad算法
```{.python .input n=1}
def adagrad(params, sqrs, lr, batch_size):
......@@ -148,7 +107,8 @@ def optimize(batch_size, lr, num_epochs, log_interval):
adagrad([w, b], sqrs, lr, batch_size)
if batch_i * batch_size % log_interval == 0:
ls.append(loss(net(features, w, b), labels).mean().asnumpy())
print('w:', w, '\nb:', b, '\n')
print('w[0]=%.2f, w[1]=%.2f, b=%.2f'
% (w[0].asscalar(), w[1].asscalar(), b.asscalar()))
es = np.linspace(0, num_epochs, len(ls), endpoint=True)
gb.semilogy(es, ls, 'epoch', 'loss')
```
......
因为 它太大了无法显示 source diff 。你可以改为 查看blob
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册