# 6.6 通过时间反向传播
在前面两节中,如果不裁剪梯度,模型将无法正常训练。为了深刻理解这一现象,本节将介绍循环神经网络中梯度的计算和存储方法,即通过时间反向传播(back-propagation through time)。
我们在3.14节(正向传播、反向传播和计算图)中介绍了神经网络中梯度计算与存储的一般思路,并强调正向传播和反向传播相互依赖。正向传播在循环神经网络中比较直观,而通过时间反向传播其实是反向传播在循环神经网络中的具体应用。我们需要将循环神经网络按时间步展开,从而得到模型变量和参数之间的依赖关系,并依据链式法则应用反向传播计算并存储梯度。
## 6.6.1 定义模型
简单起见,我们考虑一个无偏差项的循环神经网络,且激活函数为恒等映射(`$ \phi(x)=x $`)。设时间步 `$ t $` 的输入为单样本 `$ \boldsymbol{x}_t \in \mathbb{R}^d $`,标签为 `$ y_t $`,那么隐藏状态 `$ \boldsymbol{h}_t \in \mathbb{R}^h $`的计算表达式为
```[tex]
\boldsymbol{h}_t = \boldsymbol{W}_{hx} \boldsymbol{x}_t + \boldsymbol{W}_{hh} \boldsymbol{h}_{t-1},
```
其中`$ \boldsymbol{W}_{hx} \in \mathbb{R}^{h \times d} $`和`$ \boldsymbol{W}_{hh} \in \mathbb{R}^{h \times h} $`是隐藏层权重参数。设输出层权重参数`$ \boldsymbol{W}_{qh} \in \mathbb{R}^{q \times h} $`,时间步`$ t $`的输出层变量`$ \boldsymbol{o}_t \in \mathbb{R}^q $`计算为
```[tex]
\boldsymbol{o}_t = \boldsymbol{W}_{qh} \boldsymbol{h}_{t}.
```
设时间步`$ t $`的损失为`$ \ell(\boldsymbol{o}_t, y_t) $`。时间步数为$T$的损失函数`$ L $`定义为
```[tex]
L = \frac{1}{T} \sum_{t=1}^T \ell (\boldsymbol{o}_t, y_t).
```
我们将`$ L $`称为有关给定时间步的数据样本的目标函数,并在本节后续讨论中简称为目标函数。
## 6.6.2 模型计算图
为了可视化循环神经网络中模型变量和参数在计算中的依赖关系,我们可以绘制模型计算图,如图6.3所示。例如,时间步3的隐藏状态`$ \boldsymbol{h}_3 $`的计算依赖模型参数`$ \boldsymbol{W}_{hx} $`、`$ \boldsymbol{W}_{hh} $`、上一时间步隐藏状态`$ \boldsymbol{h}_2 $`以及当前时间步输入`$ \boldsymbol{x}_3 $`。
:-: ![](https://img.kancloud.cn/76/71/7671356b04af5d6ab74abcc7aa318307.svg)
<div align=center>图6.3 时间步数为3的循环神经网络模型计算中的依赖关系。方框代表变量(无阴影)或参数(有阴影),圆圈代表运算符</div>
## 6.6.3 方法
刚刚提到,图6.3中的模型的参数是 `$ \boldsymbol{W}_{hx} $`, `$ \boldsymbol{W}_{hh} $` 和 `$ \boldsymbol{W}_{qh} $`。与3.14节(正向传播、反向传播和计算图)中的类似,训练模型通常需要模型参数的梯度`$ \partial L/\partial \boldsymbol{W}_{hx} $`、`$ \partial L/\partial \boldsymbol{W}_{hh} $`和`$ \partial L/\partial \boldsymbol{W}_{qh} $`。
根据图6.3中的依赖关系,我们可以按照其中箭头所指的反方向依次计算并存储梯度。为了表述方便,我们依然采用3.14节中表达链式法则的运算符prod。
首先,目标函数有关各时间步输出层变量的梯度`$ \partial L/\partial \boldsymbol{o}_t \in \mathbb{R}^q $`很容易计算:
```[tex]
\frac{\partial L}{\partial \boldsymbol{o}_t} = \frac{\partial \ell (\boldsymbol{o}_t, y_t)}{T \cdot \partial \boldsymbol{o}_t}.
```
下面,我们可以计算目标函数有关模型参数`$ \boldsymbol{W}_{qh} $`的梯度`$ \partial L/\partial \boldsymbol{W}_{qh} \in \mathbb{R}^{q \times h} $`。根据图6.3,`$ L $`通过`$ \boldsymbol{o}_1, \ldots, \boldsymbol{o}_T $`依赖`$ \boldsymbol{W}_{qh} $`。依据链式法则,
```[tex]
\frac{\partial L}{\partial \boldsymbol{W}_{qh}}
= \sum_{t=1}^T \text{prod}\left(\frac{\partial L}{\partial \boldsymbol{o}_t}, \frac{\partial \boldsymbol{o}_t}{\partial \boldsymbol{W}_{qh}}\right)
= \sum_{t=1}^T \frac{\partial L}{\partial \boldsymbol{o}_t} \boldsymbol{h}_t^\top.
```
其次,我们注意到隐藏状态之间也存在依赖关系。
在图6.3中,`$ L $`只通过`$ \boldsymbol{o}_T $`依赖最终时间步$T$的隐藏状态`$ \boldsymbol{h}_T $`。因此,我们先计算目标函数有关最终时间步隐藏状态的梯度`$ \partial L/\partial \boldsymbol{h}_T \in \mathbb{R}^h $`。依据链式法则,我们得到
```[tex]
\frac{\partial L}{\partial \boldsymbol{h}_T} = \text{prod}\left(\frac{\partial L}{\partial \boldsymbol{o}_T}, \frac{\partial \boldsymbol{o}_T}{\partial \boldsymbol{h}_T} \right) = \boldsymbol{W}_{qh}^\top \frac{\partial L}{\partial \boldsymbol{o}_T}.
```
接下来对于时间步`$ t < T $`, 在图6.3中,`$ L $`通过`$ \boldsymbol{h}_{t+1} $` 和`$ \boldsymbol{o}_t $`依赖`$ \boldsymbol{h}_t $`。依据链式法则,
目标函数有关时间步`$ t < T $`的隐藏状态的梯度`$ \partial L/\partial \boldsymbol{h}_t \in \mathbb{R}^h $`需要按照时间步从大到小依次计算:
```[tex]
\frac{\partial L}{\partial \boldsymbol{h}_t}
= \text{prod} (\frac{\partial L}{\partial \boldsymbol{h}_{t+1}}, \frac{\partial \boldsymbol{h}_{t+1}}{\partial \boldsymbol{h}_t}) + \text{prod} (\frac{\partial L}{\partial \boldsymbol{o}_t}, \frac{\partial \boldsymbol{o}_t}{\partial \boldsymbol{h}_t} ) = \boldsymbol{W}_{hh}^\top \frac{\partial L}{\partial \boldsymbol{h}_{t+1}} + \boldsymbol{W}_{qh}^\top \frac{\partial L}{\partial \boldsymbol{o}_t}
```
将上面的递归公式展开,对任意时间步`$ 1 \leq t \leq T $` ,我们可以得到目标函数有关隐藏状态梯度的通项公式
```[tex]
\frac{\partial L}{\partial \boldsymbol{h}_t}
= \sum_{i=t}^T {\left(\boldsymbol{W}_{hh}^\top\right)}^{T-i} \boldsymbol{W}_{qh}^\top \frac{\partial L}{\partial \boldsymbol{o}_{T+t-i}}.
```
由上式中的指数项可见,当时间步数 `$ T $` 较大或者时间步 `$ t $` 较小时,目标函数有关隐藏状态的梯度较容易出现衰减和爆炸。这也会影响其他包含`$ \partial L / \partial \boldsymbol{h}_t $`项的梯度,例如隐藏层中模型参数的梯度`$ \partial L / \partial \boldsymbol{W}_{hx} \in \mathbb{R}^{h \times d} $`和`$ \partial L / \partial \boldsymbol{W}_{hh} \in \mathbb{R}^{h \times h} $`。
在图6.3中,`$ L $`通过`$ \boldsymbol{h}_1, \ldots, \boldsymbol{h}_T $`依赖这些模型参数。
依据链式法则,我们有
```[tex]
\begin{aligned}
\frac{\partial L}{\partial \boldsymbol{W}_{hx}}
&= \sum_{t=1}^T \text{prod}\left(\frac{\partial L}{\partial \boldsymbol{h}_t}, \frac{\partial \boldsymbol{h}_t}{\partial \boldsymbol{W}_{hx}}\right)
= \sum_{t=1}^T \frac{\partial L}{\partial \boldsymbol{h}_t} \boldsymbol{x}_t^\top,\\
\frac{\partial L}{\partial \boldsymbol{W}_{hh}}
&= \sum_{t=1}^T \text{prod}\left(\frac{\partial L}{\partial \boldsymbol{h}_t}, \frac{\partial \boldsymbol{h}_t}{\partial \boldsymbol{W}_{hh}}\right)
= \sum_{t=1}^T \frac{\partial L}{\partial \boldsymbol{h}_t} \boldsymbol{h}_{t-1}^\top.
\end{aligned}
```
我们已在3.14节里解释过,每次迭代中,我们在依次计算完以上各个梯度后,会将它们存储起来,从而避免重复计算。例如,由于隐藏状态梯度`$ \partial L/\partial \boldsymbol{h}_t $`被计算和存储,之后的模型参数梯度`$ \partial L/\partial \boldsymbol{W}_{hx} $`和`$ \partial L/\partial \boldsymbol{W}_{hh} $`的计算可以直接读取`$ \partial L/\partial \boldsymbol{h}_t $`的值,而无须重复计算它们。此外,反向传播中的梯度计算可能会依赖变量的当前值。它们正是通过正向传播计算出来的。
举例来说,参数梯度`$ \partial L/\partial \boldsymbol{W}_{hh} $`的计算需要依赖隐藏状态在时间步`$ t = 0, \ldots, T-1 $`的当前值`$ \boldsymbol{h}_t $`(`$ \boldsymbol{h}_0 $`是初始化得到的)。这些值是通过从输入层到输出层的正向传播计算并存储得到的。
## 小结
* 通过时间反向传播是反向传播在循环神经网络中的具体应用。
* 当总的时间步数较大或者当前时间步较小时,循环神经网络的梯度较容易出现衰减或爆炸。
------------
> 注:本节与原书基本相同,[原书传送门](https://zh.d2l.ai/chapter_recurrent-neural-networks/bptt.html)
- Home
- Introduce
- 1.深度学习简介
- 深度学习简介
- 2.预备知识
- 2.1环境配置
- 2.2数据操作
- 2.3自动求梯度
- 3.深度学习基础
- 3.1 线性回归
- 3.2 线性回归的从零开始实现
- 3.3 线性回归的简洁实现
- 3.4 softmax回归
- 3.5 图像分类数据集(Fashion-MINST)
- 3.6 softmax回归的从零开始实现
- 3.7 softmax回归的简洁实现
- 3.8 多层感知机
- 3.9 多层感知机的从零开始实现
- 3.10 多层感知机的简洁实现
- 3.11 模型选择、反向传播和计算图
- 3.12 权重衰减
- 3.13 丢弃法
- 3.14 正向传播、反向传播和计算图
- 3.15 数值稳定性和模型初始化
- 3.16 实战kaggle比赛:房价预测
- 4 深度学习计算
- 4.1 模型构造
- 4.2 模型参数的访问、初始化和共享
- 4.3 模型参数的延后初始化
- 4.4 自定义层
- 4.5 读取和存储
- 4.6 GPU计算
- 5 卷积神经网络
- 5.1 二维卷积层
- 5.2 填充和步幅
- 5.3 多输入通道和多输出通道
- 5.4 池化层
- 5.5 卷积神经网络(LeNet)
- 5.6 深度卷积神经网络(AlexNet)
- 5.7 使用重复元素的网络(VGG)
- 5.8 网络中的网络(NiN)
- 5.9 含并行连结的网络(GoogLeNet)
- 5.10 批量归一化
- 5.11 残差网络(ResNet)
- 5.12 稠密连接网络(DenseNet)
- 6 循环神经网络
- 6.1 语言模型
- 6.2 循环神经网络
- 6.3 语言模型数据集(周杰伦专辑歌词)
- 6.4 循环神经网络的从零开始实现
- 6.5 循环神经网络的简单实现
- 6.6 通过时间反向传播
- 6.7 门控循环单元(GRU)
- 6.8 长短期记忆(LSTM)
- 6.9 深度循环神经网络
- 6.10 双向循环神经网络
- 7 优化算法
- 7.1 优化与深度学习
- 7.2 梯度下降和随机梯度下降
- 7.3 小批量随机梯度下降
- 7.4 动量法
- 7.5 AdaGrad算法
- 7.6 RMSProp算法
- 7.7 AdaDelta
- 7.8 Adam算法
- 8 计算性能
- 8.1 命令式和符号式混合编程
- 8.2 异步计算
- 8.3 自动并行计算
- 8.4 多GPU计算
- 9 计算机视觉
- 9.1 图像增广
- 9.2 微调
- 9.3 目标检测和边界框
- 9.4 锚框
- 10 自然语言处理
- 10.1 词嵌入(word2vec)
- 10.2 近似训练
- 10.3 word2vec实现
- 10.4 子词嵌入(fastText)
- 10.5 全局向量的词嵌入(Glove)
- 10.6 求近义词和类比词
- 10.7 文本情感分类:使用循环神经网络
- 10.8 文本情感分类:使用卷积网络
- 10.9 编码器--解码器(seq2seq)
- 10.10 束搜索
- 10.11 注意力机制
- 10.12 机器翻译