# 第 6 章　循环神经网络

> 来源：https://deeplearning.bnuai.com/chapters/06-rnn ｜ 作者：唐志清（北京师范大学） ｜ 更新：2026-11-01 ｜ 内容依据邱锡鹏《神经网络与深度学习》（https://nndl.ai/）及作者公开课件

## 学习目标

<div class="goals">

- 理解前馈网络在处理序列数据时的局限，以及给网络增加记忆能力的几种方法；
- 掌握**简单循环网络**的状态更新公式，了解其通用近似性与图灵完备性；
- 掌握循环网络在机器学习中的三种应用模式；
- 能推导**随时间反向传播**（BPTT），理解**梯度消失 / 爆炸**与**长程依赖问题**；
- 掌握 **LSTM** 与 **GRU** 的门控机制；
- 了解堆叠 / 双向循环网络、递归神经网络与图网络，以及循环网络的典型应用。

</div>

## 课件幻灯片

> 本章课件共 64 页，可在网页中逐页浏览：https://deeplearning.bnuai.com/chapters/06-rnn；PDF 下载：https://deeplearning.bnuai.com/pdf/nndl-ch06.pdf

## 讲义

### 6.1 给网络增加记忆能力

前馈网络中，连接只存在于层与层之间，信息单向传递；输入和输出的维数固定，无法处理变长的序列数据；并且假设每次输入都相互独立，输出只依赖于当前输入。[幻灯片 3](#slide-3) [幻灯片 4](#slide-4) 然而很多任务（例如有限状态自动机、图灵机所刻画的计算过程）需要**记忆能力**：当前输出不仅依赖当前输入，还依赖过去的状态。[幻灯片 5](#slide-5)–[幻灯片 7](#slide-7)

为网络增加短期记忆的几种方法：

- **延时神经网络**（TDNN）：建立额外的延时单元存储网络的历史信息（输入、输出、隐状态等）。第 $l$ 层的神经元依赖于前一层最近 $K$ 个时刻的输出：$\boldsymbol h_t^{(l)}=f\big(\boldsymbol h_t^{(l-1)},\boldsymbol h_{t-1}^{(l-1)},\cdots,\boldsymbol h_{t-K}^{(l-1)}\big)$。[幻灯片 8](#slide-8)
- **有外部输入的非线性自回归模型**（NARX）：$\boldsymbol y_t=f\big(\boldsymbol x_t,\boldsymbol x_{t-1},\cdots,\boldsymbol x_{t-K_x},\boldsymbol y_{t-1},\cdots,\boldsymbol y_{t-K_y}\big)$。[幻灯片 10](#slide-10)
- **循环神经网络**（RNN）：使用带自反馈的神经元，理论上能处理任意长度的序列。[幻灯片 11](#slide-11)

### 6.2 简单循环网络

循环神经网络通过隐状态 $\boldsymbol h_t$ 存储历史信息：$\boldsymbol h_t=f(\boldsymbol h_{t-1},\boldsymbol x_t)$，其中 $\boldsymbol h_t$ 称为**状态**或**隐状态**。按时间展开后，循环网络可以看作一个在时间维度上共享参数的深层前馈网络。[幻灯片 12](#slide-12)

**简单循环网络**（Simple Recurrent Network, SRN，又称 Elman 网络）只有一个隐藏层，状态更新为 [幻灯片 13](#slide-13)

$$
\boldsymbol z_t=\boldsymbol U\boldsymbol h_{t-1}+\boldsymbol W\boldsymbol x_t+\boldsymbol b,\qquad \boldsymbol h_t=f(\boldsymbol z_t),
$$

其中 $\boldsymbol U$ 为状态-状态权重矩阵，$\boldsymbol W$ 为状态-输入权重矩阵，$f$ 一般取 Logistic 或 Tanh。

```mermaid
flowchart LR
  x1["x₁"] --> h1["h₁"]
  x2["x₂"] --> h2["h₂"]
  x3["x₃"] --> h3["h₃"]
  xT["x_T"] --> hT["h_T"]
  h0["h₀"] -- U --> h1 -- U --> h2 -- U --> h3 -. ⋯ .-> hT
  h1 --> y1["ŷ₁"]
  h2 --> y2["ŷ₂"]
  h3 --> y3["ŷ₃"]
  hT --> yT["ŷ_T"]
```

**表达能力**。一个完全连接的循环网络是任何非线性动力系统的近似器：对于由 $\boldsymbol s_t=g(\boldsymbol s_{t-1},\boldsymbol x_t)$、$\boldsymbol y_t=o(\boldsymbol s_t)$ 描述的动力系统，只要隐藏神经元足够多，循环网络可以以任意精度近似它。此外，一个使用 Sigmoid 型激活函数的全连接循环网络可以模拟所有的图灵机，即**图灵完备**——前馈网络能模拟任何函数，循环网络能模拟任何程序（计算过程）。[幻灯片 14](#slide-14)

### 6.3 循环网络的三种应用模式

循环网络既可以作为输入-输出映射的机器学习模型，也可以作为联想记忆模型（存储器）。作为机器学习模型，它主要有三种模式：[幻灯片 17](#slide-17)

1. **序列到类别**：输入序列、输出一个类别，如文本情感分类。可以取最后时刻的状态 $\boldsymbol h_T$ 作为整个序列的表示，或对所有状态取平均 $\bar{\boldsymbol h}=\frac1T\sum_t\boldsymbol h_t$，再送入分类器：$\hat y=g(\boldsymbol h_T)$。[幻灯片 18](#slide-18) [幻灯片 19](#slide-19)
2. **同步的序列到序列**：每个时刻都有输入和输出，且长度相同，如中文分词（BMES 标注）、词性标注、信息抽取；输出 $\hat y_t=g(\boldsymbol h_t)$。语音识别中输入输出长度不同，可以借助 **CTC** 引入空白符号 $\varphi$ 来对齐。[幻灯片 21](#slide-21)–[幻灯片 23](#slide-23)
3. **异步的序列到序列**（编码器-解码器）：输入和输出不需要严格对应，也不需要等长，如机器翻译。先用编码器读入整个输入序列得到其编码，再用解码器自回归地生成输出：[幻灯片 25](#slide-25)

$$
\boldsymbol h_t=f_1(\boldsymbol h_{t-1},\boldsymbol x_t),\ t\in[1,T];\qquad
\boldsymbol h_{T+t}=f_2(\boldsymbol h_{T+t-1},\hat{\boldsymbol y}_{t-1}),\quad \hat y_t=g(\boldsymbol h_{T+t}),\ t\in[1,M].
$$

### 6.4 参数学习：随时间反向传播

以同步序列到序列为例，给定样本 $(\boldsymbol x_{1:T},\boldsymbol y_{1:T})$，时刻 $t$ 的瞬时损失为 $\mathcal L_t=\mathcal L(y_t,g(\boldsymbol h_t))$，总损失为 $\mathcal L=\sum_{t=1}^T\mathcal L_t$。[幻灯片 26](#slide-26)

**随时间反向传播**（Backpropagation Through Time, BPTT）把循环网络看作展开后的多层前馈网络，“每一层”对应一个时刻，所有层共享参数。定义 $\boldsymbol\delta_{t,k}=\dfrac{\partial\mathcal L_t}{\partial\boldsymbol z_k}$ 为第 $t$ 时刻的损失对第 $k$ 步隐藏神经元净输入的导数（$k\le t$），则 [幻灯片 27](#slide-27)

$$
\boldsymbol\delta_{t,k}=\frac{\partial\boldsymbol h_k}{\partial\boldsymbol z_k}\frac{\partial\boldsymbol z_{k+1}}{\partial\boldsymbol h_k}\frac{\partial\mathcal L_t}{\partial\boldsymbol z_{k+1}}=\mathrm{diag}\big(f'(\boldsymbol z_k)\big)\boldsymbol U^\top\boldsymbol\delta_{t,k+1}.
$$

由于参数在各时刻共享，梯度是所有时刻、所有路径上梯度之和：

$$
\frac{\partial\mathcal L}{\partial\boldsymbol U}=\sum_{t=1}^{T}\sum_{k=1}^{t}\boldsymbol\delta_{t,k}\boldsymbol h_{k-1}^\top,\qquad
\frac{\partial\mathcal L}{\partial\boldsymbol W}=\sum_{t=1}^{T}\sum_{k=1}^{t}\boldsymbol\delta_{t,k}\boldsymbol x_{k}^\top,\qquad
\frac{\partial\mathcal L}{\partial\boldsymbol b}=\sum_{t=1}^{T}\sum_{k=1}^{t}\boldsymbol\delta_{t,k}.
$$

BPTT 需要保存所有时刻的中间结果，空间复杂度较高；另一种算法**实时循环学习**（RTRL）通过前向传播计算梯度，无需反向传播，适合在线学习。

### 6.5 长程依赖问题

把误差项的递推展开：[幻灯片 28](#slide-28)

$$
\boldsymbol\delta_{t,k}=\prod_{\tau=k}^{t-1}\Big(\mathrm{diag}\big(f'(\boldsymbol z_\tau)\big)\boldsymbol U^\top\Big)\boldsymbol\delta_{t,t}.
$$

若记 $\gamma\cong\|\mathrm{diag}(f'(\boldsymbol z_\tau))\boldsymbol U^\top\|$，则 $\boldsymbol\delta_{t,k}\cong\gamma^{t-k}\boldsymbol\delta_{t,t}$。当 $\gamma>1$ 且 $t-k\to\infty$ 时，$\gamma^{t-k}\to\infty$，出现**梯度爆炸**；当 $\gamma<1$ 时，$\gamma^{t-k}\to0$，出现**梯度消失**。循环网络在时间维度上非常“深”，因此实际上只能学到短周期的依赖关系，这就是**长程依赖问题**。[幻灯片 29](#slide-29)

改进方法：

- **梯度爆炸**：权重衰减（限制参数取值范围），以及**梯度截断**——当梯度的模超过阈值时将其缩放（见第 7 章）；
- **梯度消失**：主要靠改进模型。例如把循环边改为线性依赖 $\boldsymbol h_t=\boldsymbol h_{t-1}+g(\boldsymbol x_t;\theta)$，此时 $\partial\boldsymbol h_t/\partial\boldsymbol h_{t-1}=\boldsymbol I$，不再有梯度消失，但丢失了对历史状态的非线性变换能力；进一步加入非线性 $\boldsymbol h_t=\boldsymbol h_{t-1}+g(\boldsymbol x_t,\boldsymbol h_{t-1};\theta)$（类似残差网络），但仍存在梯度爆炸和**记忆容量**问题——隐状态不断累积信息会饱和。解决方案是引入**门控机制**，有选择地加入新信息、遗忘旧信息。[幻灯片 30](#slide-30)

调节循环权重与序列长度，观察 $\partial\boldsymbol h_T/\partial\boldsymbol h_k$ 随时间距离的指数衰减（或爆炸），并与 LSTM 细胞状态通路对比：

> （此处为交互演示，请在网页中使用：https://deeplearning.bnuai.com/chapters/06-rnn）

<p class="demo-link">在单独页面中打开：<a href="/demos/rnn-gradient">RNN 梯度消失与爆炸</a></p>

### 6.6 门控循环网络：LSTM 与 GRU

**长短期记忆网络**（LSTM）引入一个新的内部状态（记忆单元）$\boldsymbol c_t$ 专门进行线性的循环信息传递，并用三个门控制信息流动：[幻灯片 31](#slide-31)

$$
\begin{aligned}
\tilde{\boldsymbol c}_t&=\tanh(\boldsymbol W_c\boldsymbol x_t+\boldsymbol U_c\boldsymbol h_{t-1}+\boldsymbol b_c) &&\text{候选状态}\\
\boldsymbol i_t&=\sigma(\boldsymbol W_i\boldsymbol x_t+\boldsymbol U_i\boldsymbol h_{t-1}+\boldsymbol b_i) &&\text{输入门}\\
\boldsymbol f_t&=\sigma(\boldsymbol W_f\boldsymbol x_t+\boldsymbol U_f\boldsymbol h_{t-1}+\boldsymbol b_f) &&\text{遗忘门}\\
\boldsymbol o_t&=\sigma(\boldsymbol W_o\boldsymbol x_t+\boldsymbol U_o\boldsymbol h_{t-1}+\boldsymbol b_o) &&\text{输出门}\\
\boldsymbol c_t&=\boldsymbol f_t\odot\boldsymbol c_{t-1}+\boldsymbol i_t\odot\tilde{\boldsymbol c}_t\\
\boldsymbol h_t&=\boldsymbol o_t\odot\tanh(\boldsymbol c_t)
\end{aligned}
$$

遗忘门 $\boldsymbol f_t$ 控制上一时刻的记忆需要遗忘多少，输入门 $\boldsymbol i_t$ 控制当前候选状态需要保存多少，输出门 $\boldsymbol o_t$ 控制记忆单元有多少信息输出到隐状态。门的取值在 $(0,1)$ 之间，以“软”的方式控制信息流。由于 $\boldsymbol c_t$ 与 $\boldsymbol c_{t-1}$ 之间是线性关系（逐元素乘以 $\boldsymbol f_t$），只要遗忘门接近 1，梯度就能长距离传播。实践中遗忘门的偏置通常初始化为较大的正值（如 1 或 2）。

隐状态 $\boldsymbol h$ 每个时刻都会被重写，可以看作**短期记忆**；网络参数可以看作**长期记忆**；记忆单元 $\boldsymbol c$ 保存的信息生命周期介于两者之间，因此称为“长的短期记忆”。

**LSTM 的变体**：[幻灯片 32](#slide-32)

- 无遗忘门的 LSTM（最早的版本）：$\boldsymbol c_t=\boldsymbol c_{t-1}+\boldsymbol i_t\odot\tilde{\boldsymbol c}_t$，记忆单元会不断增大；
- **耦合输入门和遗忘门**：令 $\boldsymbol f_t=\boldsymbol 1-\boldsymbol i_t$，减少冗余；
- **peephole 连接**：三个门不仅依赖 $\boldsymbol x_t$ 和 $\boldsymbol h_{t-1}$，还依赖记忆单元 $\boldsymbol c_{t-1}$（或 $\boldsymbol c_t$）。

**门控循环单元**（GRU）是比 LSTM 更简洁的门控网络，不引入额外的记忆单元，用**更新门** $\boldsymbol z_t$ 在当前状态中平衡历史信息与新信息，用**重置门** $\boldsymbol r_t$ 控制候选状态对历史状态的依赖程度：[幻灯片 33](#slide-33)

$$
\begin{aligned}
\boldsymbol z_t&=\sigma(\boldsymbol W_z\boldsymbol x_t+\boldsymbol U_z\boldsymbol h_{t-1}+\boldsymbol b_z),\qquad
\boldsymbol r_t=\sigma(\boldsymbol W_r\boldsymbol x_t+\boldsymbol U_r\boldsymbol h_{t-1}+\boldsymbol b_r),\\
\tilde{\boldsymbol h}_t&=\tanh\big(\boldsymbol W_h\boldsymbol x_t+\boldsymbol U_h(\boldsymbol r_t\odot\boldsymbol h_{t-1})+\boldsymbol b_h\big),\\
\boldsymbol h_t&=\boldsymbol z_t\odot\boldsymbol h_{t-1}+(\boldsymbol 1-\boldsymbol z_t)\odot\tilde{\boldsymbol h}_t .
\end{aligned}
$$

当 $\boldsymbol z_t=\boldsymbol 0,\boldsymbol r_t=\boldsymbol 1$ 时，GRU 退化为简单循环网络；当 $\boldsymbol z_t=\boldsymbol 0,\boldsymbol r_t=\boldsymbol 0$ 时，当前状态只与当前输入有关；当 $\boldsymbol z_t=\boldsymbol 1$ 时，当前状态直接复制上一时刻的状态。

### 6.7 深层循环网络

**堆叠循环神经网络**（Stacked RNN）把多个循环网络堆叠起来，第 $l$ 层在时刻 $t$ 的状态为 $\boldsymbol h_t^{(l)}=f\big(\boldsymbol U^{(l)}\boldsymbol h_{t-1}^{(l)}+\boldsymbol W^{(l)}\boldsymbol h_t^{(l-1)}+\boldsymbol b^{(l)}\big)$，$\boldsymbol h_t^{(0)}=\boldsymbol x_t$。[幻灯片 35](#slide-35)

**双向循环神经网络**（Bi-RNN）由两层方向相反的循环网络组成，使每个时刻的输出同时依赖过去和未来的信息：[幻灯片 36](#slide-36)

$$
\boldsymbol h_t^{(1)}=f\big(\boldsymbol U^{(1)}\boldsymbol h_{t-1}^{(1)}+\boldsymbol W^{(1)}\boldsymbol x_t+\boldsymbol b^{(1)}\big),\quad
\boldsymbol h_t^{(2)}=f\big(\boldsymbol U^{(2)}\boldsymbol h_{t+1}^{(2)}+\boldsymbol W^{(2)}\boldsymbol x_t+\boldsymbol b^{(2)}\big),\quad
\boldsymbol h_t=\boldsymbol h_t^{(1)}\oplus\boldsymbol h_t^{(2)} .
$$

### 6.8 扩展到图结构：递归神经网络与图网络

如果把循环网络按时间展开的链式结构扩展为树结构或图结构，就得到更一般的模型。[幻灯片 37](#slide-37)

**递归神经网络**（Recursive Neural Network, RecNN）在**有向无环图**上共享一个组合函数。程序语言和自然语言的句法结构都是树结构。[幻灯片 39](#slide-39) [幻灯片 40](#slide-40) 例如给定语法树 $p_2\to a\,p_1$，$p_1\to b\,c$，则 [幻灯片 43](#slide-43)

$$
\boldsymbol h_1=f\Big(\boldsymbol W\begin{bmatrix}\boldsymbol x_b\\\boldsymbol x_c\end{bmatrix}+\boldsymbol b\Big),\qquad
\boldsymbol h_2=f\Big(\boldsymbol W\begin{bmatrix}\boldsymbol x_a\\\boldsymbol h_1\end{bmatrix}+\boldsymbol b\Big).
$$

当树退化为线性序列时，递归神经网络就等价于简单循环网络。[幻灯片 42](#slide-42)

**图网络**。知识图谱、社交网络、分子结构等很多数据是图结构的，前馈网络和循环网络很难处理。[幻灯片 44](#slide-44) [幻灯片 45](#slide-45) 图网络在每个节点 $v$ 上维护状态 $\boldsymbol h^{(v)}$，通过**消息传递**更新：[幻灯片 48](#slide-48)

$$
\boldsymbol m_t^{(v)}=\sum_{u\in\mathcal N(v)}f\big(\boldsymbol h_{t-1}^{(v)},\boldsymbol h_{t-1}^{(u)},\boldsymbol e^{(u,v)}\big),\qquad
\boldsymbol h_t^{(v)}=g\big(\boldsymbol h_{t-1}^{(v)},\boldsymbol m_t^{(v)}\big),
$$

其中 $\mathcal N(v)$ 为 $v$ 的邻居，$\boldsymbol e^{(u,v)}$ 为边的特征。经过 $T$ 次迭代后，用**读出函数** $\boldsymbol o=g\big(\{\boldsymbol h_T^{(v)}\mid v\in\mathcal V\}\big)$ 得到整个图的表示。图卷积网络（GCN）、图注意力网络（GAT）等都可以纳入这一框架（教材第二版单独设一章讲图神经网络）。

### 6.9 循环网络的应用

- **语言模型**：估计句子的概率 $p(x_{1:T})=\prod_t p(x_t\mid x_{1:(t-1)})$。N 元语言模型只看前 $N-1$ 个词，循环网络语言模型理论上可以利用全部历史。[幻灯片 50](#slide-50) 训练好的语言模型可以用来生成 Linux 内核代码、歌词、诗歌等。[幻灯片 52](#slide-52)–[幻灯片 54](#slide-54)
- **机器翻译**：传统统计机器翻译由多个模块组成 [幻灯片 55](#slide-55)；基于序列到序列的神经机器翻译用一个 RNN 编码、另一个 RNN 解码，端到端训练。[幻灯片 56](#slide-56)
- **看图说话**（Image Captioning）：卷积网络编码图像，循环网络生成描述 [幻灯片 57](#slide-57)；视觉问答（VQA）[幻灯片 59](#slide-59)。
- **手写体生成**：把书写轨迹看作一串点，每个点记为相对前一点的偏移 $(\Delta x,\Delta y)$ 和是否“提笔”。[幻灯片 60](#slide-60)
- **对话系统**。[幻灯片 61](#slide-61)

**总结**：循环网络引入了记忆，并且图灵完备；但存在长程依赖问题、记忆容量问题，且计算必须按时间步顺序进行，**并行能力差**——后者正是 Transformer（第 8 章）要解决的问题。[幻灯片 62](#slide-62)

## 本章小结

- 循环网络通过隐状态 $\boldsymbol h_t=f(\boldsymbol U\boldsymbol h_{t-1}+\boldsymbol W\boldsymbol x_t+\boldsymbol b)$ 引入记忆，是图灵完备的。
- 三种应用模式：序列到类别、同步序列到序列、异步序列到序列（编码器-解码器）。
- BPTT 沿时间展开计算梯度；误差项中的连乘导致梯度消失 / 爆炸，形成长程依赖问题。
- 梯度爆炸用梯度截断，梯度消失靠门控：LSTM（输入 / 遗忘 / 输出门 + 记忆单元）、GRU（更新门 + 重置门）。
- 堆叠、双向结构增加深度与上下文；递归神经网络和图网络把循环网络推广到树和图。

## 思考题与习题 {#exercises}

<div class="exercise-list">

1. **（推导）** 写出简单循环网络中 $\boldsymbol\delta_{t,k}$ 的递推公式，并推导 $\partial\mathcal L/\partial\boldsymbol W$。
2. **（分析）** 设 $f=\tanh$，$\boldsymbol U$ 的最大奇异值为 $\lambda_1$。证明当 $\lambda_1<1$ 时梯度必然随 $t-k$ 指数衰减。
3. **（概念）** LSTM 中为什么用 $\tanh$ 生成候选状态、用 Logistic 函数生成门？如果把遗忘门恒定为 1、输入门恒定为 1，会发生什么？
4. **（计算）** 输入维度为 $d$、隐状态维度为 $h$ 时，简单 RNN、LSTM、GRU 各有多少参数？
5. **（设计）** 分别为以下任务选择合适的循环网络模式并画出结构：命名实体识别、文本分类、机器翻译、语音识别。
6. **（编程）** 用 LSTM 实现字符级语言模型，在唐诗语料上训练并生成诗句；比较不同采样温度下的生成效果。
7. **（编程）** 训练一个循环网络做多位数加法（如输入 `123+456`，输出 `579`），比较 SRN 与 LSTM 在不同位数下的准确率。

</div>

**部分提示**

- 第 2 题：$\|\mathrm{diag}(f'(\boldsymbol z))\boldsymbol U^\top\|\le\|\mathrm{diag}(f')\|\cdot\|\boldsymbol U\|\le1\cdot\lambda_1$。
- 第 4 题：SRN $h(h+d+1)$；LSTM $4h(h+d+1)$；GRU $3h(h+d+1)$（均不含输出层）。

## 延伸阅读

- 教材：邱锡鹏《神经网络与深度学习》第 6 章「循环神经网络」。
- Hochreiter S, Schmidhuber J. Long short-term memory, 1997；Cho K, et al. Learning phrase representations using RNN encoder–decoder, 2014。
- Battaglia P W, et al. Relational inductive biases, deep learning, and graph networks, 2018。
- 实践：[nndl-practice](https://github.com/nndl/nndl-practice) 第 6 章「循环神经网络」、第 9 章「图神经网络」。
- 交互：[RNN 梯度消失与爆炸](https://deeplearning.bnuai.com/demos/rnn-gradient)。
- 第二版对应章节：第 6 章「循环神经网络」，图网络部分见第 9 章「图神经网络」。
