提交 dccabe0a 编写于 作者: A Aston Zhang

2pass gd sgd

上级 c6307085
# 梯度下降和随机梯度下降
本节中,我们将介绍梯度下降(gradient descent)的工作原理。虽然梯度下降在深度学习中很少被直接使用,但理解梯度的意义和为什么沿着梯度反方向更新自变量可以降低目标函数值是学习后续优化算法的基础。随后我们引出随机梯度下降(stochastic gradient descent),这是深度学习中常用的优化算法的最简单形式
本节中,我们将介绍梯度下降(gradient descent)的工作原理。虽然梯度下降在深度学习中很少被直接使用,但理解梯度的意义以及沿着梯度反方向更新自变量可能降低目标函数值的原因是学习后续优化算法的基础。随后,我们将引出随机梯度下降(stochastic gradient descent)
## 一维梯度下降
我们先以简单的一维梯度下降为例,解释梯度下降算法可降低目标函数值的原因。假设连续可导的函数$f: \mathbb{R} \rightarrow \mathbb{R}$的输入和输出都是标量。给定绝对值足够小的数$\epsilon$,根据泰勒展开公式(参见[“数学基础”](../chapter_appendix/math.md)一节),我们得到以下的近似
我们先以简单的一维梯度下降为例,解释梯度下降算法可降低目标函数值的原因。假设连续可导的函数$f: \mathbb{R} \rightarrow \mathbb{R}$的输入和输出都是标量。给定绝对值足够小的数$\epsilon$,根据泰勒展开公式(参见[“数学基础”](../chapter_appendix/math.md)一节),我们得到以下的近似
$$f(x + \epsilon) \approx f(x) + \epsilon f'(x) .$$
这里$f'(x)$是函数$f$在$x$处的梯度。一维函数的梯度是一个标量,也称导数。
接下来我们找一个常数$\eta > 0$,使得$\left|\eta f'(x)\right|$足够小,那么可以将$\epsilon$替换为$-\eta f'(x)$得到
接下来,找到一个常数$\eta > 0$,使得$\left|\eta f'(x)\right|$足够小,那么可以将$\epsilon$替换为$-\eta f'(x)$并得到
$$f(x - \eta f'(x)) \approx f(x) - \eta f'(x)^2.$$
......@@ -18,11 +18,11 @@ $$f(x - \eta f'(x)) \approx f(x) - \eta f'(x)^2.$$
$$f(x - \eta f'(x)) \lesssim f(x).$$
这意味着,如果我们通过以下规则来更新$x$:
这意味着,如果我们通过
$$x \leftarrow x - \eta f'(x),$$
$$x \leftarrow x - \eta f'(x)$$
函数$f(x)$的值可能被降低。一般来说,我们选取一个初始值$x$和常数$\eta > 0$,然后不断的通过上式来迭代$x$,直到达到停止条件,例如$f'(x)^2$的值已经足够小
来迭代$x$,函数$f(x)$的值可能会降低。因此在梯度下降中,我们先选取一个初始值$x$和常数$\eta > 0$,然后不断通过上式来迭代$x$,直到达到停止条件,例如$f'(x)^2$的值已足够小或迭代次数已达到某个值
下面我们以目标函数$f(x)=x^2$为例来看一看梯度下降是如何执行的。虽然我们知道最小化$f(x)$的解为$x=0$,这里我们依然使用这个简单函数来观察$x$是如何被迭代的。首先,导入本节实验所需的包或模块。
......@@ -37,14 +37,14 @@ from mxnet import nd
import numpy as np
```
接下来我们使用$x=10$作为初始值,设$\eta=0.2$。使用梯度下降对$x$迭代10次,可见最后$x$的值较接近最优解。
接下来我们使用$x=10$作为初始值,并设$\eta=0.2$。使用梯度下降对$x$迭代10次,可见最终$x$的值较接近最优解。
```{.python .input n=4}
def gd(eta):
x = 10
results = [x]
for i in range(10):
x -= eta * 2 * x # f(x) = x*x 的导数为 f'(x) = 2x
x -= eta * 2 * x # f(x) = x * x 的导数为 f'(x) = 2 * x
results.append(x)
print('epoch 10, x:', x)
return results
......@@ -52,7 +52,7 @@ def gd(eta):
res = gd(0.2)
```
下面将绘制出$x$的迭代过程。
下面将绘制出自变量$x$的迭代过程。
```{.python .input n=5}
def show_trace(res):
......@@ -69,13 +69,13 @@ show_trace(res)
## 学习率
上述梯度下降算法中的正数$\eta$通常叫做学习率。这是一个超参数,需要人工设定。如果使用过小的学习率,会导致$x$更新缓慢从而需要更多的迭代才能得到较好的解。下面展示了使用$\eta=0.05$时$x$的迭代过程
上述梯度下降算法中的正数$\eta$通常叫做学习率。这是一个超参数,需要人工设定。如果使用过小的学习率,会导致$x$更新缓慢从而需要更多的迭代才能得到较好的解。下面展示了使用学习率$\eta=0.05$时自变量$x$的迭代过程。可见,同样迭代10次后,当学习率过小时,最终$x$的值依然与最优解存在较大偏差
```{.python .input n=6}
show_trace(gd(0.05))
```
如果使用过大的学习率,$\left|\eta f'(x)\right|$可能会过大从而使前面提到的一阶泰勒展开公式不再成立:这时我们无法保证迭代$x$会降低$f(x)$的值。举个例子,当我们设$\eta=1.1$时,可以看到$x$不断越过(overshoot)最优解$x=0$并逐渐发散。
如果使用过大的学习率,$\left|\eta f'(x)\right|$可能会过大从而使前面提到的一阶泰勒展开公式不再成立:这时我们无法保证迭代$x$会降低$f(x)$的值。举个例子,当我们设学习率$\eta=1.1$时,可以看到$x$不断越过(overshoot)最优解$x=0$并逐渐发散。
```{.python .input n=7}
show_trace(gd(1.1))
......@@ -83,29 +83,29 @@ show_trace(gd(1.1))
## 多维梯度下降
接下来考虑一种更广义的情况:目标函数的输入为向量,输出为标量。假设目标函数$f: \mathbb{R}^d \rightarrow \mathbb{R}$的输入是一个$d$维向量$\boldsymbol{x} = [x_1, x_2, \ldots, x_d]^\top$。目标函数$f(\boldsymbol{x})$有关$\boldsymbol{x}$的梯度是一个由$d$个偏导数组成的向量:
在了解了一维梯度下降之后,我们再考虑一种更广义的情况:目标函数的输入为向量,输出为标量。假设目标函数$f: \mathbb{R}^d \rightarrow \mathbb{R}$的输入是一个$d$维向量$\boldsymbol{x} = [x_1, x_2, \ldots, x_d]^\top$。目标函数$f(\boldsymbol{x})$有关$\boldsymbol{x}$的梯度是一个由$d$个偏导数组成的向量:
$$\nabla_{\boldsymbol{x}} f(\boldsymbol{x}) = \bigg[\frac{\partial f(\boldsymbol{x})}{\partial x_1}, \frac{\partial f(\boldsymbol{x})}{\partial x_2}, \ldots, \frac{\partial f(\boldsymbol{x})}{\partial x_d}\bigg]^\top.$$
为表示简洁,我们用$\nabla f(\boldsymbol{x})$代替$\nabla_{\boldsymbol{x}} f(\boldsymbol{x})$。梯度中每个偏导数元素$\partial f(\boldsymbol{x})/\partial x_i$代表着$f$在$\boldsymbol{x}$有关输入$x_i$的变化率。为了测量$f$沿着单位向量$\boldsymbol{u}$(即$\|\boldsymbol{u}\|=1$)方向上的变化率,在多元微积分中,我们定义$f$在$\boldsymbol{x}$上沿着$\boldsymbol{u}$方向的方向导数为
$$D_{\boldsymbol{u}} f(\boldsymbol{x}) = \lim_{h \rightarrow 0} \frac{f(\boldsymbol{x} + h \boldsymbol{u}) - f(\boldsymbol{x})}{h}.$$
$$\text{D}_{\boldsymbol{u}} f(\boldsymbol{x}) = \lim_{h \rightarrow 0} \frac{f(\boldsymbol{x} + h \boldsymbol{u}) - f(\boldsymbol{x})}{h}.$$
依据方向导数性质 \[1,14.6节定理三\]方向导数可以改写为
依据方向导数性质 \[1,14.6节定理三\]以上的方向导数可以改写为
$$D_{\boldsymbol{u}} f(\boldsymbol{x}) = \nabla f(\boldsymbol{x}) \cdot \boldsymbol{u}.$$
$$\text{D}_{\boldsymbol{u}} f(\boldsymbol{x}) = \nabla f(\boldsymbol{x}) \cdot \boldsymbol{u}.$$
方向导数$D_{\boldsymbol{u}} f(\boldsymbol{x})$给出了$f$在$\boldsymbol{x}$上沿着所有可能方向的变化率。为了最小化$f$,我们希望找到$f$能被降低最快的方向。因此,我们可以通过单位向量$\boldsymbol{u}$来最小化方向导数$D_{\boldsymbol{u}} f(\boldsymbol{x})$。
方向导数$\text{D}_{\boldsymbol{u}} f(\boldsymbol{x})$给出了$f$在$\boldsymbol{x}$上沿着所有可能方向的变化率。为了最小化$f$,我们希望找到$f$能被降低最快的方向。因此,我们可以通过单位向量$\boldsymbol{u}$来最小化方向导数$\text{D}_{\boldsymbol{u}} f(\boldsymbol{x})$。
由于$D_{\boldsymbol{u}} f(\boldsymbol{x}) = \|\nabla f(\boldsymbol{x})\| \cdot \|\boldsymbol{u}\| \cdot \text{cos} (\theta) = \|\nabla f(\boldsymbol{x})\| \cdot \text{cos} (\theta)$,
其中$\theta$为梯度$\nabla f(\boldsymbol{x})$和单位向量$\boldsymbol{u}$之间的夹角,当$\theta = \pi$,$\text{cos}(\theta)$取得最小值$-1$。因此,当$\boldsymbol{u}$在梯度方向$\nabla f(\boldsymbol{x})$的相反方向时,方向导数$D_{\boldsymbol{u}} f(\boldsymbol{x})$被最小化。所以,我们可能通过下面的梯度下降算法来不断降低目标函数$f$的值:
由于$\text{D}_{\boldsymbol{u}} f(\boldsymbol{x}) = \|\nabla f(\boldsymbol{x})\| \cdot \|\boldsymbol{u}\| \cdot \text{cos} (\theta) = \|\nabla f(\boldsymbol{x})\| \cdot \text{cos} (\theta)$,
其中$\theta$为梯度$\nabla f(\boldsymbol{x})$和单位向量$\boldsymbol{u}$之间的夹角,当$\theta = \pi$时,$\text{cos}(\theta)$取得最小值$-1$。因此,当$\boldsymbol{u}$在梯度方向$\nabla f(\boldsymbol{x})$的相反方向时,方向导数$\text{D}_{\boldsymbol{u}} f(\boldsymbol{x})$被最小化。所以,我们可能通过梯度下降算法来不断降低目标函数$f$的值:
$$\boldsymbol{x} \leftarrow \boldsymbol{x} - \eta \nabla f(\boldsymbol{x}).$$
相同地,其中$\eta$(取正数)称作学习率。
下面我们构造一个输入为二维向量$\boldsymbol{x} = [x_1, x_2]^\top$和输出为标量的目标函数$f(\boldsymbol{x})=x_1^2+2x_2^2$。可以知道$\nabla f(\boldsymbol{x}) = [2x_1, 4x_2]^\top$。然后观察梯度下降从初始点$[5,2]$开始对$\boldsymbol{x}$的更新轨迹。首先定义两个辅助函数,第一个使用给定的自变量更新函数来从初始点$[5,2]$开始迭代$\boldsymbol{x}$20次,第二个函数可视化$\boldsymbol{x}$的更新轨迹。
下面我们构造一个输入为二维向量$\boldsymbol{x} = [x_1, x_2]^\top$和输出为标量的目标函数$f(\boldsymbol{x})=x_1^2+2x_2^2$。那么,梯度$\nabla f(\boldsymbol{x}) = [2x_1, 4x_2]^\top$。我们将观察梯度下降从初始位置$[5,2]$开始对自变量$\boldsymbol{x}$的迭代轨迹。我们先定义两个辅助函数。第一个函数使用给定的自变量更新函数,从初始位置$[5,2]$开始迭代自变量$\boldsymbol{x}$共20次。第二个函数将可视化自变量$\boldsymbol{x}$的迭代轨迹。
```{.python .input n=10}
def train_2d(trainer): # 本函数将保存在 gluonbook 包中方便以后使用。
......@@ -125,7 +125,7 @@ def show_trace_2d(f, results): # 本函数将保存在 gluonbook 包中方便
gb.plt.ylabel('x2')
```
然后观察学习率为$0.1$的自变量更新
然后,观察学习率为$0.1$时自变量的迭代轨迹。使用梯度下降对自变量$\boldsymbol{x}$迭代20次后,可见最终$\boldsymbol{x}$的值较接近最优解$[0,0]$
```{.python .input n=15}
eta = 0.1
......@@ -141,7 +141,7 @@ show_trace_2d(f_2d, train_2d(gd_2d))
## 随机梯度下降
在深度学习里,通常目标函数是训练样本上损失函数的平均。设$f_i(\boldsymbol{x})$是有关索引为$i$的训练数据样本的损失函数,$n$是训练数据样本数,$\boldsymbol{x}$是模型的参数向量,那么目标函数定义为
在深度学习里,目标函数通常是训练数据集中有关各个样本的损失函数的平均。设$f_i(\boldsymbol{x})$是有关索引为$i$的训练数据样本的损失函数,$n$是训练数据样本数,$\boldsymbol{x}$是模型的参数向量,那么目标函数定义为
$$f(\boldsymbol{x}) = \frac{1}{n} \sum_{i = 1}^n f_i(\boldsymbol{x}).$$
......@@ -151,17 +151,17 @@ $$\nabla f(\boldsymbol{x}) = \frac{1}{n} \sum_{i = 1}^n \nabla f_i(\boldsymbol{x
如果使用梯度下降,每次自变量迭代的计算开销为$\mathcal{O}(n)$,它随着$n$线性增长。因此,当训练数据样本数很大时,梯度下降每次迭代的计算开销很高。
随机梯度下降(stochastic gradient descent,简称SGD)减少了每次迭代的计算开销。在随机梯度下降中,每次迭代我们随机均匀采样的一个样本索引$i\in[1,n]$,并计算梯度$\nabla f_i(\boldsymbol{x})$来迭代$\boldsymbol{x}$:
随机梯度下降(stochastic gradient descent,简称SGD)减少了每次迭代的计算开销。在随机梯度下降的每次迭代中,我们随机均匀采样的一个样本索引$i\in\{1,\ldots,n\}$,并计算梯度$\nabla f_i(\boldsymbol{x})$来迭代$\boldsymbol{x}$:
$$\boldsymbol{x} \leftarrow \boldsymbol{x} - \eta \nabla f_i(\boldsymbol{x}).$$
这里$\eta$同样是学习率。可以看到每次迭代的开销从梯度下降的$\mathcal{O}(n)$降到了常数$\mathcal{O}(1)$。因为随机梯度$\nabla f_i(\boldsymbol{x})$是对梯度$\nabla f(\boldsymbol{x})$的无偏估计:
这里$\eta$同样是学习率。可以看到每次迭代的计算开销从梯度下降的$\mathcal{O}(n)$降到了常数$\mathcal{O}(1)$。值得强调的是,随机梯度$\nabla f_i(\boldsymbol{x})$是对梯度$\nabla f(\boldsymbol{x})$的无偏估计:
$$\mathbb{E}_i \nabla f_i(\boldsymbol{x}) = \frac{1}{n} \sum_{i = 1}^n \nabla f_i(\boldsymbol{x}) = \nabla f(\boldsymbol{x}).$$
里的期望是对随机变量$i$。这个意味着平均上来说随机梯度是一个对梯度很好的估计。同梯度下降一样,如果选取合适的学习率,平均上每次迭代可以下降目标函数值
意味着,平均来说,随机梯度是对梯度的一个良好的估计
下面我们通过在梯度中加入均值为0的随机噪音来模拟随机梯度下降,以此来比较它与梯度下降的区别。
下面我们通过在梯度中添加均值为0的随机噪音来模拟随机梯度下降,以此来比较它与梯度下降的区别。
```{.python .input n=17}
def sgd_2d(x1, x2, s1, s2):
......@@ -171,20 +171,20 @@ def sgd_2d(x1, x2, s1, s2):
show_trace_2d(f_2d, train_2d(sgd_2d))
```
可以看到随机梯度下降的更新轨迹相对于梯度下降更加曲折。因为加入的噪音(实际中,它来自样本的噪音)使得梯度的准确度下降,所以在使用同样的超参数的情况下,随机梯度下降收敛到的值相对梯度下降来说离最优值更远。但因为随机梯度下降每一次迭代的计算比梯度下降更加简单,在同样运行时间下,随机梯度下降可以进行更多次的自变量迭代,它最终得到的解的质量可能会比梯度下降更优
可以看到,随机梯度下降中自变量的迭代轨迹相对于梯度下降中的来说更为曲折。这是由于实验所添加的噪音使得模拟的随机梯度的准确度下降。在实际中,这些噪音通常来自于训练数据集中的各个样本
## 小结
* 使用适当的学习率,沿着梯度反方向更新自变量可降低目标函数值。梯度下降重复这一更新过程直到得到满足要求的解。
* 使用适当的学习率,沿着梯度反方向更新自变量可降低目标函数值。梯度下降重复这一更新过程直到得到满足要求的解。
* 学习率过大过小都有问题。一个合适的学习率通常是需要通过多次实验找到的。
* 当训练数据较大,梯度下降每次迭代计算开销较大,因而随机梯度下降可能更受青睐。
* 当训练数据集的样本较多时,梯度下降每次迭代计算开销较大,因而随机梯度下降通常更受青睐。
## 练习
* 使用一个不同的目标函数来观察收敛。
* 在二维目标函数例子中尝试不同的学习率,看看其对收敛的影响。
* 随机梯度下降中我们通常会对学习率进行衰减,例如假设当前为第$i$次迭代,$i=1,2,\ldots$,尝试使用$\eta/\sqrt{i}$或者$\eta/i$作为学习率。
* 使用一个不同的目标函数,观察梯度下降和随机梯度下降中自变量的迭代轨迹。
* 在二维梯度下降的实验中尝试使用不同的学习率,观察并分析实验现象。
## 扫码直达[讨论区](https://discuss.gluon.ai/t/topic/1877)
......
......@@ -135,7 +135,7 @@ gb.plt.ylabel('y');
在上图的鞍点位置,目标函数在$x$轴方向上是局部最小值,而在$y$轴方向上是局部最大值。
假设一个函数的输入为$k$维向量,输出为标量,那么它的黑塞矩阵(Hessian matrix)有$k$个特征值。该函数在梯度为零的位置上可能是局部最小值、局部最大值或者鞍点:
假设一个函数的输入为$k$维向量,输出为标量,那么它的黑塞矩阵(Hessian matrix)有$k$个特征值(参见[“数学基础”](../chapter_appendix/math.md)一节)。该函数在梯度为零的位置上可能是局部最小值、局部最大值或者鞍点:
* 当函数的黑塞矩阵在梯度为零的位置上的特征值全为正时,该函数得到局部最小值。
* 当函数的黑塞矩阵在梯度为零的位置上的特征值全为负时,该函数得到局部最大值。
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册