Files
openmlsys-zh/appendix_machine_learning_introduction/gradient_descent.md
2022-03-08 11:19:02 +08:00

94 lines
7.2 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 梯度下降与反向传播
上面大体上介绍了经典神经网络的内容,那么现在有一个问题,这些网络中的参数是如何确定的呢?如果要解决的问题是一个小感知器就能解决的话,参数可以人为地去确定。但是如果是一个深度网络的话,参数的确定需要自动化,也就是所谓的网络训练,而这个过程需要我们设定一个**损失函数**Loss
Function来作为训练优化的一个方向。
常见的损失函数有1用来衡量向量之间距离的均方误差(Mean Squared
ErrorMSE)
$\mathcal{L} = \frac{1}{N}\|{y}-\hat{{y}}\|^{2}_{2} = \frac{1}{N}\sum_{i=1}^N(y_{i}-\hat{y}_{i})^{2}$
和 平均绝对误差(Mean Absolute ErrorMAE)
$\mathcal{L} = \frac{1}{N}\sum_{i=1}^{N}|y_{i}-\hat{y}_{i}|$
,其中$N$代表数据样本的数量,用以求平均用,而$y$代表真实标签Ground
Truth、$\hat{y}$代表网络输出的预测标签。
2分类任务可以用的交叉熵损失Cross Entropy
$\mathcal{L} = - \frac{1}{N} \sum_{i=1}^N \bigg(y_{i}\log\hat{y}_{i} + (1 - y_{i})\log(1 - \hat{y}_{i})\bigg)$来作为损失数,当且仅当输出标签和预测标签一样的时候损失值才为零。
有了损失值之后,我们就可以利用大量真实标签的数据和优化方法来更新模型参数了,其中最常用的方法是**梯度下降**Gradient
Descent。如 :numref:`gradient_descent2`所示,
开始的时候,模型的参数${w}$是随机选取的,然后求出损失值对参数的偏导数$\frac{\partial \mathcal{L}}{\partial {w}}$,通过反复迭代
${w}:={w}-\alpha\frac{\partial \mathcal{L}}{\partial {w}}$完成优化。这个优化的过程其实就可以降低损失值以达到任务目标,其中$\alpha$是控制优化幅度的**学习率**Learning
Rate
在实践中,梯度下降最终得到的最小值很大可能是一个局部最小值,而不是全局最小值。不过由于深度神经网络能提供一个很强的数据表达能力,所以局部最小值可以很接近全局最小值,损失值可以足够小。
![梯度下降介绍。(左图)只有一个可以训练的参数$w$;(右图)有两个可以训练的参数${w}=[w_1,w_2]$。在不断更新迭代参数后,损失值$\mathcal{L}$会逐渐地减小。但是由于存在很多局部最优解,我们往往不能更新到全局最优解。](../img/ch_basic/gradient_descent2.png)
:width:`600px`
:label:`gradient_descent2`
那么接下来,在深度神经网络中如何实现梯度下降呢,这需要计算出网络中每层参数的偏导数$\frac{\partial \mathcal{L}}{\partial {w}}$,我们可以用**反向传播**Back-Propagation :cite:`rumelhart1986learning,lecun2015deep`来实现。
接下来,
我们引入一个中间量${\delta}=\frac{\partial \mathcal{L}}{\partial {z}}$来表示损失函数$\mathcal{L}$
对于神经网络输出${z}$(未经过激活函数,不是$a$)的偏导数,
并最终得到$\frac{\partial \mathcal{L}}{\partial {w}}$。
我们下面用一个例子来介绍反向传播算法,
我们设层序号为$l=1, 2, \ldots L$(输出层(最后一层)序号为$L$)。
对于每个网络层,我们有输出${z}^l$,中间值${\delta}^l=\frac{\partial \mathcal{L}}{\partial {z}^l}$和一个激活值输出${a}^l=f({z}^l)$
(其中$f$为激活函数)。
我们假设模型是使用Sigmoid激活函数的多层感知器损失函数是均方误差MSE。也就是说我们设定
- 网络结构${z}^{l}={W}^{l}{a}^{l-1}+{b}^{l}$
- 激活函数${a}^l=f({z}^l)=\frac{1}{1+{\rm e}^{-{z}^l}}$
- 损失函数$\mathcal{L}=\frac{1}{2}\|{y}-{a}^{L}\|^2_2$
我们可以直接算出激活输出对于原输出的偏导数:
- $\frac{\partial {a}^l}{\partial {z}^l}=f'({z}^l)=f({z}^l)(1-f({z}^l))={a}^l(1-{a}^l)$
和损失函数对于激活输出的偏导数:
- $\frac{\partial \mathcal{L}}{\partial {a}^{L}}=({a}^{L}-{y})$
有了这些后,为了进一步得到损失函数对于每一个参数的偏导数,可以使用**链式法则**Chain
Rule细节如下
首先,从输出层($l=L$,最后一层)开始向后方传播误差,根据链式法则,我们先计算输出层的中间量:
- ${\delta}^{L}
=\frac{\partial \mathcal{L}}{\partial {z}^{L}}
=\frac{\partial \mathcal{L}}{\partial {a}^{L}}\frac{\partial {a}^L}{\partial {z}^{L}}=({a}^L-{y})\odot({a}^L(1-{a}^L))$
除了输出层($l=L$)的中间值${\delta}^{L}$,其他层($l=1, 2, \ldots , L-1$)的中间值${\delta}^{l}$如何计算呢?
- 已知模型结构${z}^{l+1}={W}^{l+1}{a}^{l}+{b}^{l+1}$,我们可以直接得到$\frac{\partial {z}^{l+1}}{\partial {a}^{l}}={W}^{l+1}$;而且我们已知$\frac{\partial {a}^l}{\partial {z}^l}={a}^l(1-{a}^l)$
- 那么根据链式法则,我们可以得到 ${\delta}^{l}
=\frac{\partial \mathcal{L}}{\partial {z}^{l}}
=\frac{\partial \mathcal{L}}{\partial {z}^{l+1}}\frac{\partial {z}^{l+1}}{\partial {a}^{l}}\frac{\partial {a}^{l}}{\partial {z}^{l}}
=({W}^{l+1})^\top{\delta}^{l+1}\odot({a}^l(1-{a}^l))$
根据上面的计算有所有层的中间值${\delta}^l, l=1, 2, \ldots , L$后,我们就可以在此基础上求出损失函数对于每层参数的偏导数:$\frac{\partial \mathcal{L}}{\partial {W}^l}$和$\frac{\partial \mathcal{L}}{\partial {b}^l}$,以此来根据梯度下降的方法来更新每一层的参数。
- 已知模型结构${z}^l={W}^l{a}^{l-1}+{b}^l$,我们可以求出
$\frac{\partial {z}^{l}}{\partial {W}^l}={a}^{l-1}$ 和
$\frac{\partial {z}^{l}}{\partial {b}^l}=1$
- 那么根据链式法则,我们可以得到$\frac{\partial \mathcal{L}}{\partial {W}^l}=\frac{\partial \mathcal{L}}{\partial {z}^l}\frac{\partial {z}^l}{\partial {W}^l}={\delta}^l({a}^{l-1})^\top$
,
$\frac{\partial \mathcal{L}}{\partial {b}^l}=\frac{\partial \mathcal{L}}{\partial {z}^l}\frac{\partial {z}^l}{\partial {b}^l}={\delta}^l$
求得所有偏导数$\frac{\partial \mathcal{L}}{\partial {W}^l}$ 和
$\frac{\partial \mathcal{L}}{\partial {b}^l}$后,我们就可以用梯度下降更新所有参数${W}^l$
和 ${b}^l$
- ${W}^l:={W}^l-\alpha\frac{\partial \mathcal{L}}{\partial {W}^l}$,
${b}^l:={b}^l-\alpha\frac{\partial \mathcal{L}}{\partial {b}^l}$
但是还有一个问题需要解决,那就是梯度下降的时候每更新一次参数,都需要计算一次当前参数下的损失值。然而,当训练数据集很大时($N$很大),若每次更新都用整个训练集来计算损失值的话,计算量会非常巨大。
为了减少计算量,我们使用**随机梯度下降**Stochastic Gradient
DescentSGD来计算损失值。具体来说我们计算损失值不用全部训练数据而是从训练集中随机选取一些数据样本来计算损失值比如选取16、32、64或者128个数据样本样本的数量被称为**批大小**Batch
Size
此外,学习率的设定也非常重要。如果学习率太大,可能无法接近最小值的山谷,如果太小,训练又太慢。
自适应学习率例如Adam :cite:`KingmaAdam2014`、RMSProp :cite:`tieleman2012rmsprop`
Adagrad :cite:`duchi2011adagrad`等,在训练的过程中通过自动的方法来修改学习率,实现训练的快速收敛,到达最小值点。