CS231N Lecture 7: Recurrent Neural Networks
| 字段 | 内容 |
|---|---|
| 作者/整理 | 基于 Zane Durante 授课内容整理 |
| 来源 | Stanford University |
| 日期 | 2025 |

引言:从固定输入到序列建模
在之前的课程中,我们一直讨论的是标准的非循环(non-recurrent)神经网络:固定大小的输入映射到固定大小的输出。这种范式适合图像分类等任务,但真实世界中存在大量变长序列数据——语言、视频、音乐、时间序列等。本讲将引入循环神经网络(Recurrent Neural Networks, RNNs),这是一种专门为序列建模设计的神经网络架构。
来源:Slides 第5页。
序列建模的五种范式
- One-to-one:固定输入 \(\rightarrow\) 固定输出(如图像分类)
- One-to-many:固定输入 \(\rightarrow\) 变长输出(如图像描述生成)
- Many-to-one:变长输入 \(\rightarrow\) 固定输出(如视频分类、情感分析)
- Many-to-many(异步):变长输入 \(\rightarrow\) 变长输出(如机器翻译)
- Many-to-many(同步):每个输入对应一个输出(如逐帧视频标注)
课程前期回顾
讲者首先对上节课的内容进行了几点澄清:
- Dropout 的缩放:超参数 \(p\) 在不同实现中可能表示丢弃概率或保留概率。核心原则是测试时的期望输出应与训练时一致——若训练时丢弃 25% 的激活值,测试时需乘以 0.75 进行缩放。
- Layer Normalization 与权重初始化:Layer Norm 可以缓解不良初始化带来的问题,但无法完全替代良好的权重初始化。在某些需要精确空间信息的任务中,Layer Norm 可能反而损害性能。
本章小结
神经网络不再局限于固定大小的输入输出。通过引入序列建模,我们能够处理语言、视频、对话等变长数据。RNN 正是为这类任务设计的核心架构。
循环神经网络基础
RNN 的核心思想
RNN 的核心特征是引入了一个隐藏状态(hidden state),该状态在处理序列时不断更新。每接收一个新输入,RNN 都会基于当前输入和上一步的隐藏状态,计算新的隐藏状态。
来源:Slides 第7页。
RNN 的数学公式
RNN 在每个时间步 \(t\) 执行以下计算:
隐藏状态更新:
输出计算:
其中:
- \(h_t\):时间步 \(t\) 的隐藏状态向量
- \(x_t\):时间步 \(t\) 的输入向量
- \(f_W\):带参数 \(W\) 的状态转移函数(每个时间步共享相同的参数)
- \(f_Y\):将隐藏状态映射到输出的函数
Vanilla RNN 的具体形式
最常见的 RNN 形式(称为 Vanilla RNN)使用 \(\tanh\) 作为激活函数:
来源:Slides 第10页。
为什么使用 激活函数
\(\tanh\) 的输出范围为 \([-1, 1]\),具有以下优势:
- 有界性:反复应用不会导致隐藏状态爆炸
- 零中心:能表示正负值,比 sigmoid 更适合作为隐藏状态的激活函数
- 非线性:提供必要的表达能力
但 \(\tanh\) 的导数最大值为 1(在 \(x=0\) 处),这意味着梯度在反向传播时几乎总是在衰减——这是导致梯度消失的重要原因之一。
三组权重矩阵
RNN 中有三组关键的权重矩阵:
| 权重矩阵 | 作用 | 维度 |
|---|---|---|
| \(W_xh\) | 将输入 \(x_t\) 变换到隐藏状态维度 | \(d_h × d_x\) |
| \(W_hh\) | 将上一步隐藏状态变换为当前步的贡献 | \(d_h × d_h\) |
| \(W_hy\) | 将隐藏状态映射到输出 | \(d_y × d_h\) |
所有时间步共享同一组权重
RNN 在每个时间步使用完全相同的权重矩阵 \(W_{hh}\)、\(W_{xh}\)、\(W_{hy}\)。这意味着:
- 模型大小不随序列长度增长
- 梯度计算时需要将各时间步的梯度累加
- 模型对序列中不同位置施加相同的变换规则
本章小结
RNN 通过引入隐藏状态和参数共享的循环结构,实现了对变长序列的建模。其核心计算包含两个矩阵乘法(输入到隐藏、隐藏到隐藏)加一个非线性激活函数,以及一个从隐藏状态到输出的线性映射。
手工构造 RNN:一个玩具示例
为了深入理解 RNN 的前向传播过程,讲者通过一个手工设计的玩具示例,展示了如何构造权重矩阵来实现特定功能。
任务定义
目标:给定一个 0 和 1 的序列,当连续出现两个 1 时输出 1,否则输出 0。
来源:Slides 第12页。
隐藏状态设计
为了完成这个任务,隐藏状态需要记录两条信息:当前输入值和上一步的输入值。讲者将隐藏状态设计为三维向量 \(h_t = [\text{current}, \text{previous}, 1]^T\),其中第三个维度固定为 1(用于输出阶段的偏置计算)。
来源:Slides 第13页。
权重矩阵构造
输入权重 \(W_{xh}\):
当 \(x_t = 1\) 时,\(W_{xh} x_t = [1, 0, 0]^T\)(记录当前值为 1);当 \(x_t = 0\) 时,结果为零向量。
隐藏状态权重 \(W_{hh}\):
- 第一行全零:当前值完全由 \(W_{xh} x_t\) 决定
- 第二行 \([1,0,0]\):将上一步的“当前值”复制为这一步的“前一步值”
- 第三行 \([0,0,1]\):保持常数 1 不变
输出权重 \(W_{hy}\):
输出 \(y_t = \text{ReLU}(W_{hy} h_t) = \text{ReLU}(\text{current} + \text{previous} - 1)\)。只有当 current \(= 1\) 且 previous \(= 1\) 时,\(y_t = 1\)。
来源:Slides 第16页。
从手工构造到梯度学习
这个玩具示例的目的是帮助理解 RNN 的计算过程。在实际应用中:
- 我们不需要手工设计权重矩阵——通过梯度下降自动学习
- 隐藏状态的维度通常远大于 3(可以是数百到数千维)
- 隐藏状态中学到的“特征”往往是高度抽象且不易解释的
本章小结
通过手工构造 RNN 的权重矩阵,我们直观地看到了 RNN 如何通过隐藏状态在时间步之间传递信息。两组权重矩阵分别负责“处理当前输入”和“继承历史信息”,它们的加和经过激活函数后形成新的隐藏状态。
RNN 的训练:时间反向传播
计算图与损失函数
在 many-to-many 设置中,RNN 在每个时间步都产生输出并计算损失。总损失为各时间步损失之和:
来源:Slides 第18页。
时间反向传播(BPTT)
由于所有时间步共享相同的权重矩阵 \(W\),计算 \(\frac{\partial L}{\partial W}\) 时需要:
- 分别计算每个时间步对 \(W\) 的梯度贡献
- 将所有时间步的梯度累加
这个过程称为Backpropagation Through Time (BPTT)。可以将其理解为:在概念上将 \(W\) 视为每个时间步使用了不同的“副本”,分别计算各副本的梯度,最后因为它们实际上是同一个 \(W\) 而将梯度求和。
BPTT 的内存问题
对于长序列,BPTT 需要在内存中保存所有时间步的激活值和梯度。这会导致 GPU 内存迅速耗尽。
来源:Slides 第20页。
截断 BPTT
解决方案:将长序列分割为固定长度的“块”(chunk),在每个块内独立执行前向传播和反向传播。
来源:Slides 第21页。
具体流程:
- 处理第一个块:从 \(h_0\) 开始,计算隐藏状态和损失,执行反向传播,更新权重
- 处理第二个块:用第一个块的最终隐藏状态作为起始 \(h_0\),但不传播梯度
- 重复直到序列结束
截断 BPTT 的信息损失
截断 BPTT 是一种近似方法:
- 前向传播:隐藏状态在块之间传递,信息得以保留
- 反向传播:梯度在块边界被截断,长距离依赖的梯度信号会丢失
在 many-to-many 设置中,信息损失更为明显,因为每个块只看到局部的损失。理想情况下当然是将整个序列放入内存一次性计算,但这对长序列不可行。
本章小结
RNN 的训练通过 BPTT 实现,本质上是在展开的计算图上执行标准反向传播。截断 BPTT 通过将序列分块来控制内存消耗,但代价是丢失了跨块的梯度信息。
字符级语言模型
自回归生成
RNN 可以训练为字符级语言模型:给定前面的字符,预测下一个字符。这本质上是一个逐时间步的分类问题。
来源:Slides 第23页。
语言模型的训练与推理
训练时:
- 输入真实序列,目标是下一个字符
- 每个时间步计算交叉熵损失
- 无需人工标注——文本本身就是监督信号(自监督学习)
推理时(生成):
- 给定一个起始字符(或起始 token)
- 从输出概率分布中采样一个字符
- 将采样结果作为下一步的输入
- 重复直到生成结束 token 或达到最大长度
Embedding 层
在实际实现中,通常不直接使用 one-hot 编码作为输入,而是使用Embedding 层:一个可学习的查找表,将每个离散 token 映射为一个稠密向量。
One-hot vs Embedding
- One-hot:稀疏、高维(维度 = 词表大小),不包含语义信息
- Embedding:稠密、低维(通常 64--512 维),通过训练自动学习语义关系
- 数学上,用 one-hot 向量做矩阵乘法等价于从 Embedding 矩阵中取出对应行
- Embedding 在优化上更高效,且能学到有意义的表示
采样策略
贪心解码的问题
如果每次都选择概率最大的字符(贪心解码),模型会反复生成相同的序列。实际做法是:
- 随机采样:按概率分布采样
- 温度调节:\(p_i = \frac{e^{z_i / T}}{\sum_j e^{z_j / T}}\),温度 \(T\) 控制分布的“尖锐度”
- Beam Search:同时维护多个候选序列,选择整体概率最高的
- Top-k / Top-p 采样:只从概率最高的 \(k\) 个(或累积概率达到 \(p\) 的)token 中采样
生成效果展示
用仅 112 行 Python 代码实现的字符级 RNN,训练在莎士比亚十四行诗上,可以生成风格相似的文本。随着训练的进行,生成质量逐步提升:
来源:Slides 第26页。
来源:Slides 第27页。
这正是当今大语言模型的雏形——从字符级预测发展到 token 级预测,从 RNN 发展到 Transformer,但自回归预测下一个 token 的核心思想始终未变。
本章小结
字符级语言模型展示了 RNN 在文本生成方面的潜力。其核心思想——自回归地预测下一个 token——直接启发了现代大语言模型的设计。从 Karpathy 2015 年的“The Unreasonable Effectiveness of Recurrent Neural Networks”到 GPT 系列,这一范式延续至今。
RNN 隐藏状态的可解释性
RNN 的一个有趣特性是,我们可以直接观察隐藏状态中各个神经元的激活值,并发现其中一些具有高度可解释的语义。
来源:Slides 第29页。
讲者展示了在代码和文本数据上训练的 RNN 中发现的几种可解释的隐藏单元:
来源:Slides 第30页。
来源:Slides 第31页。
来源:Slides 第32页。
来源:Slides 第34页。
RNN 的自发特征学习
这些可解释的隐藏单元从未被明确设计或监督——它们完全是模型在训练过程中自发学会的。这与我们手工构造的玩具 RNN 形成了有趣的对比:
- 玩具示例中,我们手动指定隐藏状态追踪“当前值”和“前一步值”
- 训练好的 RNN 自动学会追踪引号状态、代码深度等更复杂的概念
- 但并非所有隐藏单元都可解释——大部分单元的激活模式看起来是随机的
本章小结
RNN 隐藏状态中的某些神经元能够自发地学习到有意义的特征,如引号追踪、行长度计数、代码嵌套深度等。这种可解释性是 RNN 架构的一个独特优势。
RNN 的优势与局限
优势
来源:Slides 第35页。
- 可处理任意长度的输入:没有上下文长度限制(与 Transformer 形成对比)
- 理论上可利用任意远的历史信息:如果隐藏状态有效地编码了历史,计算可以依赖很久以前的输入
- 模型大小不随输入长度增长:参数数量固定,只由隐藏状态维度决定
- 每个时间步应用相同的变换:对称性和概念简洁性
局限
- 训练时难以并行化:计算 \(h_t\) 必须先计算 \(h_{t-1}\),这是一个严格的顺序依赖
- 实际上难以访问远距离信息:固定大小的隐藏状态无法无损地编码任意长的历史
- 梯度消失/爆炸:长序列的反向传播中,梯度会指数级衰减或增长
训练的不可并行性是 RNN 的致命弱点
现代深度学习的成功很大程度上依赖于 GPU 的大规模并行计算能力。RNN 的顺序依赖结构使得它无法充分利用 GPU 的并行性——这正是 Transformer 最终取代 RNN 的核心原因之一。在训练时,Transformer 可以同时处理序列中的所有位置,而 RNN 必须逐步计算。
本章小结
RNN 在理论上具有处理无限长序列的能力,但在实践中受到训练效率(不可并行)和梯度流动(消失/爆炸)的严重限制。这些局限性推动了 LSTM、GRU 以及最终 Transformer 的诞生。
RNN 在计算机视觉中的应用
图像描述生成(Image Captioning)
最经典的 CNN + RNN 应用是图像描述生成:用 CNN 提取图像特征,用 RNN 生成自然语言描述。
来源:Slides 第38页。
来源:Slides 第39页。
来源:Slides 第41页。
共现偏置与幻觉
早期的图像描述模型存在严重的共现偏置问题:
- 看到海滩就假设有冲浪板
- 看到手持物体就猜测是常见搭配
- 看到棒球运动就默认是“投球”而非“接球”
这种幻觉(hallucination)问题在今天的视觉语言模型中依然普遍存在。其根源在于模型学习了训练数据中的统计关联,而非真正理解场景内容。
视觉问答(Visual Question Answering)
来源:Slides 第43页。
其他应用
RNN 在计算机视觉中的其他应用包括:
- 视觉对话(Visual Dialogue):围绕图像进行多轮对话
- 视觉导航(Visual Navigation):根据视觉输入生成移动指令序列
本章小结
RNN 与 CNN 的结合开创了视觉-语言多模态模型的先河。尽管这些方法已被 Transformer 基础的模型所取代,但 CNN+RNN 的架构思想——将视觉编码器与序列生成器组合——在今天的视觉语言模型(如 LLaVA、GPT-4V)中以不同形式延续。
多层 RNN
来源:Slides 第47页。
多层 RNN 的组织方式如下:
- 层内:同一层的所有时间步共享权重,隐藏状态沿时间维度传递
- 层间:第 \(l\) 层的输出作为第 \(l+1\) 层的输入
- 每一层有独立的权重矩阵
这形成了一个二维的计算网格(时间 \(\times\) 深度),进一步加剧了训练时的计算瓶颈。
本章小结
多层 RNN 通过堆叠 RNN 层来增加模型的表达能力,但也使得计算图更加复杂,训练更加困难。
梯度消失/爆炸与 LSTM
梯度消失问题的根源
反向传播中,隐藏状态之间的梯度为:
这个乘积在时间步之间反复累乘,导致:
来源:Slides 第49页。
梯度消失/爆炸的数学分析
- \(\tanh\) 导数:\(\tanh'(x) \in [0, 1]\),只有在 \(x = 0\) 时取最大值 1。多次乘以小于 1 的值 \(\rightarrow\) 指数衰减(梯度消失)。
-
\(W_{hh}\) 的奇异值:
-
最大奇异值 \(\sigma_{\max} > 1\):梯度爆炸
- 最大奇异值 \(\sigma_{\max} < 1\):梯度消失
梯度爆炸可以通过梯度裁剪(gradient clipping)解决:当梯度范数超过阈值时进行缩放。但梯度消失更加棘手——这正是 LSTM 要解决的问题。
LSTM 的核心思想
LSTM(Long Short-Term Memory)通过引入门控机制和细胞状态(cell state)来缓解梯度消失问题。
来源:Slides 第50页。
LSTM 的四个门:
- Gate Gate(候选值):计算要写入的候选信息 \(\tilde{c}_t = \tanh(W_g [h_{t-1}, x_t])\)
- Input Gate:决定是否写入 \(i_t = \sigma(W_i [h_{t-1}, x_t])\)
- Forget Gate:决定遗忘多少历史信息 \(f_t = \sigma(W_f [h_{t-1}, x_t])\)
- Output Gate:决定输出多少 \(o_t = \sigma(W_o [h_{t-1}, x_t])\)
细胞状态更新:\(c_t = f_t \odot c_{t-1} + i_t \odot \tilde{c}_t\)
隐藏状态输出:\(h_t = o_t \odot \tanh(c_t)\)
LSTM 解决梯度消失的关键:Cell State Highway
细胞状态 \(c_t\) 的更新路径(图中顶部通道)不经过 \(\tanh\) 激活函数,只涉及逐元素乘法(forget gate)和加法(input gate)。这意味着:
- 只要 forget gate 不为零,梯度就能相对无损地沿时间反向传播
- 类似于 ResNet 中的跳跃连接——提供了梯度的“高速公路”
- LSTM 并不保证解决梯度消失,但使得模型更容易学习长距离依赖
LSTM 与 ResNet 的联系
来源:Slides 第52页。
跳跃连接的普适原理
ResNet 和 LSTM 解决的问题在本质上是相同的:
- ResNet:随着网络深度增加,梯度消失使得深层难以训练 \(\rightarrow\) 引入跳跃连接
- LSTM:随着序列长度增加,梯度消失使得远距离信息难以利用 \(\rightarrow\) 引入 cell state highway
核心思想是一致的:提供一条不经过非线性变换的“直达路径”,让信息和梯度能够更自由地流动。
GRU 简介
GRU(Gated Recurrent Unit)是 LSTM 的一个简化变体,将 forget gate 和 input gate 合并为一个 update gate,减少了参数量,同时保持了类似的性能。
本章小结
梯度消失是 Vanilla RNN 难以处理长序列的根本原因。LSTM 通过引入门控机制和无激活函数的 cell state 通道来缓解这一问题。这与 ResNet 中的跳跃连接异曲同工——都通过提供梯度“高速公路”来改善深层/长距离的训练。
现代回归:状态空间模型
RNN 思想的复兴
尽管 Transformer 在过去几年统治了深度学习,但 RNN 的核心思想正在以状态空间模型(State Space Models, SSMs)的形式回归。
来源:Slides 第53页。
状态空间模型相比 Transformer 的关键优势:
- 无限上下文长度:像 RNN 一样,没有固定的上下文窗口限制
- 线性时间推理:计算量与序列长度成线性关系,而非 Transformer 的二次方
- 固定内存占用:推理时只需维护固定大小的状态,不需要 KV cache 的线性增长
RNN vs Transformer 的核心权衡
- Transformer:训练可并行(快),推理时 KV cache 随序列增长(慢),\(O(n^2)\) 注意力开销
- RNN/SSM:训练难以并行(慢),推理时固定状态(快),\(O(n)\) 线性开销
- 当前研究热点:如何兼得两者优势——训练时的并行性 + 推理时的效率
本章小结
以 Mamba 和 RWKV 为代表的状态空间模型正在挑战 Transformer 的统治地位,特别是在长上下文场景中。RNN 的核心概念——循环状态更新、线性时间推理——正以更现代的形式焕发新生。
拓展阅读
- Andrej Karpathy: The Unreasonable Effectiveness of Recurrent Neural Networks, 2015 http://karpathy.github.io/2015/05/21/rnn-effectiveness/
- Hochreiter & Schmidhuber: Long Short-Term Memory, Neural Computation, 1997
- Gu & Dao: Mamba: Linear-Time Sequence Modeling with Selective State Spaces, 2023 https://arxiv.org/abs/2312.00752
- Peng et al.: RWKV: Reinventing RNNs for the Transformer Era, 2023 https://arxiv.org/abs/2305.13048
总结与延伸
讲者的核心总结
Zane Durante 在课程结尾总结了三个要点:
- Vanilla RNN 概念简洁但性能有限
- LSTM 等变体通过门控机制选择性传递信息,缓解了梯度消失问题
- RNN 的核心思想(循环状态、线性复杂度)正在通过状态空间模型回归研究前沿
全课知识图谱
关键 Takeaways
五条核心原则
- 隐藏状态是 RNN 的灵魂:它在时间步之间传递信息,编码序列的“记忆”
- 参数共享使模型泛化:所有时间步共享权重,使 RNN 能处理任意长度的输入
- 梯度消失是长序列的天敌:\(\tanh\) 和权重矩阵的反复累乘导致远距离梯度信号衰减
- LSTM 的 cell state 是解药:无激活函数的直通路径,类似 ResNet 的跳跃连接
- RNN 并未过时:状态空间模型正在以线性复杂度挑战 Transformer 的二次方开销