# 1.2 RNN 与 CNN：成就与瓶颈

理解 RNN 和 CNN 在序列建模中的成就与局限，是理解 Transformer 为什么这样设计的前提。每一个 Transformer 的核心设计决策，都可以追溯到对这些前驱架构的某个具体不足的改进。

## 1.2.1 循环神经网络：用“记忆”处理序列

循环神经网络（Recurrent Neural Network，RNN）是第一个真正为序列数据设计的神经网络架构。它的核心思想极为自然：**像人类阅读一样，逐个处理序列中的元素，并维护一个“记忆”（隐藏状态）来积累已经看过的信息。**

具体而言，RNN 在每个时间步 $$t$$ 接收当前输入 $$x\_t$$ 和上一步的**隐藏状态**（Hidden State）$$h\_{t-1}$$。**隐藏状态是网络内部维护的一个数学向量，用于浓缩和表征迄今为止处理过的所有历史信息。** RNN 结合这两个输入，计算出新的隐藏状态：

$$h\_t = \sigma(W\_h h\_{t-1} + W\_x x\_t + b)$$

其中 $$\sigma$$ 是激活函数（通常为 tanh），$$W\_h$$ 和 $$W\_x$$ 是权重矩阵。关键在于：**所有时间步共享同一组参数**（$$W\_h$$、$$W\_x$$ 和 $$b$$），这使得 RNN 天然地能够处理任意长度的序列——第一个核心挑战得以解决。

下图将 RNN 沿时间展开，展示了隐藏状态如何在时间步之间依次传递（$$h\_0$$ 初始化为零向量）：

```mermaid
graph LR
    h0["h₀<br/>(零向量)"] --> A["RNN<br/>hₜ=σ(Wₕh+Wₓx+b)"]
    x1["x₁"] --> A
    A --> h1["h₁"]
    h1 --> B["RNN<br/>hₜ=σ(Wₕh+Wₓx+b)"]
    x2["x₂"] --> B
    B --> h2["h₂"]
    h2 --> C["RNN<br/>hₜ=σ(Wₕh+Wₓx+b)"]
    x3["x₃"] --> C
    C --> h3["h₃"]
    h3 -..-> D[" ··· "]
    D -..-> E["RNN<br/>hₜ=σ(Wₕh+Wₓx+b)"]
    xn["xₙ"] --> E
    E --> hn["hₙ"]

    style A fill:#4a90d9,color:#fff
    style B fill:#4a90d9,color:#fff
    style C fill:#4a90d9,color:#fff
    style E fill:#4a90d9,color:#fff
    style h0 fill:#ecf0f1,stroke:#7f8c8d
```

图 1-2：RNN 沿时间展开示意图，每个时间步共享同一组参数 $$(W\_h, W\_x, b)$$

正如前文所述，隐藏状态 $$h\_t$$ 理论上编码了从序列开头到当前位置的所有信息，可以被视为一个“压缩的历史摘要”。在序列结束时，最终的隐藏状态可以用于分类任务；在每一步，隐藏状态也可以用于序列标注或生成下一个输出。

RNN 在 2010 年代初取得了显著的成功：语言建模、语音识别、机器翻译等任务的性能都得到了大幅提升。然而，当人们试图将 RNN 应用于更复杂的真实场景（尤其是需要全局语境的任务）以及更长的序列时，几个根本性的问题暴露了出来。

## 1.2.2 单向视野：无法“预知未来”

如果仅仅按照从左到右的单向顺序去处理序列，在诸如机器翻译这样的任务中会遇到显著的语义盲区。如前所述，当 RNN 逐词处理序列并输出当前词的隐藏状态时，**它只能看到之前的输入，对后文一无所知**。假设我们要翻译“I arrived in London yesterday.”，当模型编码“arrived”这个词时，它的状态中只有“I arrived”的信息，并不清楚其后的“London”（目的地）和“yesterday”（时间）。但在语言表达中，当前词的真实含义、形态甚至具体用词往往强烈依赖于**后文**的完整语境。**单向 RNN 无法“预知未来”，这使得它在理解序列时极易产生偏差，进而导致基于此表示的生成极有可能“翻译不好”。**

为了解决“只能看到过去”的单向视野问题，研究人员随后引入了**双向 RNN（Bidirectional RNN）**。它通过同时运行一个正向（从左到右）和一个逆向（从右向左）的 RNN，将两个方向的隐藏状态拼接起来，使得序列中每个位置的表征都能同时包含过去与未来的完整上下文：$$\vec{h}*i = \text{RNN}*\text{fwd}(x\_1, \ldots, x\_i)$$，$$\overleftarrow{h}*i = \text{RNN}*\text{bwd}(x\_n, \ldots, x\_i)$$，最终表示为 $$\[\vec{h}\_i; \overleftarrow{h}\_i]$$。这一改进大幅弥补了单向视野的缺陷，成为了理解类任务（如序列标注、句子分类）的重要基石。但即便有了双向结构，RNN 仍然面临着梯度消失问题（在两个方向上都存在），且依然无法实现完全并行化。

## 1.2.3 梯度消失：RNN 的致命弱点

RNN 的训练依赖**时间反向传播**（Backpropagation Through Time，BPTT）：将 RNN 沿时间展开为一个很深的前馈网络，然后用标准的反向传播算法计算梯度。

问题出在梯度的链式传播上。当梯度从时间步 $$T$$ 回传到时间步 $$1$$ 时，需要连续乘以 $$T$$ 次权重矩阵 $$W\_h$$ 的转置。如果 $$W\_h$$ 的谱范数（最大奇异值）小于 1，这种连乘会导致梯度**指数级衰减**——这就是**梯度消失**（Vanishing Gradient）问题。

$$\frac{\partial h\_T}{\partial h\_1} = \prod\_{t=2}^{T} \frac{\partial h\_t}{\partial h\_{t-1}} \approx (W\_h)^{T-1}$$

上式为简化表达，省略了激活函数 $$\sigma$$ 的 Jacobian 矩阵。准确形式应为 $$\frac{\partial h\_T}{\partial h\_1} = \prod\_{t=2}^{T} \text{diag}(\sigma'(z\_t)) W\_h$$，其中 $$z\_t = W\_h h\_{t-1} + W\_x x\_t$$。关键的是，即使激活函数的导数（如 tanh 的 $$\sigma'(z) \leq 1$$）也会进一步缩小梯度。当序列长度 $$T$$ 较大时，如果权重矩阵的谱范数 $$\rho(W\_h) < 1$$，连乘后的梯度按 $$\rho(W\_h)^{T-1}$$ 衰减，趋近于零；如果 $$\rho(W\_h) > 1$$，则梯度**爆炸**。这意味着 RNN 实际上**无法学习长距离依赖**：距离较远的两个词之间的关联信号在回传过程中会消失殆尽。

Bengio 等人在 1994 年的经典论文中系统分析了这一问题，指出这不是工程细节，而是 RNN 架构的**内在数学缺陷**。

## 1.2.4 LSTM 与 GRU：改善梯度消失

为了缓解梯度消失问题，Hochreiter 和 Schmidhuber 在 1997 年提出了**长短期记忆网络**（Long Short-Term Memory，LSTM）。

LSTM 的核心创新是引入了**门控机制**（Gating Mechanism）和一条独立的**记忆单元通路**（Cell State）。记忆单元 $$c\_t$$ 通过线性的自循环路径传递信息，不经过激活函数的压缩：

这三个门（遗忘门 $$f\_t$$、输入门 $$i\_t$$、输出门 $$o\_t$$）的本质是**结构完全相同的全连接层**。它们都接收当前输入 $$x\_t$$ 和上一时刻的隐状态 $$h\_{t-1}$$，通过仿射变换（乘以各自的权重矩阵再加上偏置）后，再经过一个 **Sigmoid 激活函数（**$$\sigma$$**）**：

$$f\_t = \sigma(W\_{fx} x\_t + W\_{fh} h\_{t-1} + b\_f)$$ $$i\_t = \sigma(W\_{ix} x\_t + W\_{ih} h\_{t-1} + b\_i)$$ $$o\_t = \sigma(W\_{ox} x\_t + W\_{oh} h\_{t-1} + b\_o)$$

> \[!TIP] **门控的值到底长什么样？**
>
> 无论是 $$f\_t$$、$$i\_t$$ 还是 $$o\_t$$，由于它们最后经过了 Sigmoid 函数的挤压，它们的输出不是一个单独的数字，而是一个**和记忆单元** $$c\_t$$ **维度完全一样的向量**（例如 512 维）。在这个向量里，每一个数字都被极其严格地限制在 **0 到 1 之间**（比如 `[0.01, 0.98, 0.5, ...]`）。 当这个向量跟记忆单元做逐元素相乘时：如果在某个维度上门控值为接近 0 的数字（如 0.01），就意味着这个维度的信息被“关门”阻断了；如果是接近 1 的数字（如 0.98），信息被放行；如果是 0.5，则通过一半。

对于主干道上的记忆更新和隐状态输出： $$c\_t = f\_t \odot c\_{t-1} + i\_t \odot \tilde{c}\_t$$ $$h\_t = o\_t \odot \tanh(c\_t)$$

我们可以这样直观地理解 LSTM 中这三个门控（阀门）的具体分工（符号 $$\odot$$ 表示逐元素相乘 Hadamard Product）：

1. **遗忘门** $$f\_t$$**（负责“除旧”）**：控制上一时刻的老记忆（$$c\_{t-1}$$）有多少能保留下来。
2. **输入门** $$i\_t$$**（负责“纳新”）**：控制由当前输入提取出的新知识（候选记忆 $$\tilde{c}\_t$$）有多少能写到当前记忆里。 *经过前面这两个门的加和，真正的“当前长期记忆”* $$c\_t$$ *此时已经更新完毕。*
3. **输出门** $$o\_t$$**（负责“展示”）**：控制刚刚更新好的内部记忆 $$c\_t$$，有多少应该被立刻“公开”出去作为当前的短期输出（隐状态 $$h\_t$$）。

> \[!NOTE] **为什么既有记忆单元** $$c\_t$$ **又有隐藏状态** $$h\_t$$**？**
>
> 在传统 RNN 中，唯一的隐状态 $$h\_t$$ 要同时承担**长期记忆转运**和**当前短期对外输出**双重任务，导致其在每次更新时极易被新输入冲刷覆盖。LSTM 的核心精妙之处在于将这两种职能解耦：
>
> 1. **内部长期私密日记本（**$$c\_t$$**）**：它是模型私有的底稿，里面记录了所有的背景故事和时间线，主要通过简单的线性加法平滑更新。这种设计为梯度回传提供了一条无阻碍的“状态高速公路”（State Highway），从根本上缓解了梯度消失。
> 2. **外部当前工作简报（**$$h\_t$$**）**：为了不把繁杂的历史底稿全部暴露给当前时刻的计算节点，$$c\_t$$ 会被 `tanh` 函数整流，再由负责“展示”的**输出门** $$o\_t$$ 进行过滤。模型会根据当前上下文动态决定：“虽然我有一大堆历史记忆（$$c\_t$$），但在这个时间步只会向外展示对当前预测有关联的那一小部分”，从而将其按比例提取作为 $$h\_t$$ 对外输出。这不仅用于当前步的任务预测，也会作为最新焦点传递给下一个时间步。
>
> **那为什么不直接拿** $$c\_t$$ **当作当前时间步的输出，彻底干掉** $$h\_t$$ **呢？**
>
> 1. **保持长程友好的梯度通道**：$$c\_t$$ 之所以能解决梯度消失，就是因为它在时间线上的更新几乎只有加法（$$+$$），没有被任意非线性函数（如 sigmoid 或 tanh）反复挤压。如果直接强迫 $$c\_t$$ 去负责每个时刻的具体输出任务，为了适应输出的非线性边界，它自身的数值分布就会被严重扭曲干扰，这就毁了这条专用于长程传递的“加法状态高速公路”。
> 2. **数值范围的稳定性**：$$c\_t$$ 是一直累加的，如果没有干预，它里面的数值可能会长得非常大或非常小。直接把这种无界的数值暴露给下一层网络是不稳定的。引入 $$h\_t = o\_t \odot \tanh(c\_t)$$ 后，`tanh` 强行把底稿的数值压缩到 `[-1, 1]` 的安全区间内，而输出门 $$o\_t$$ 则进一步屏蔽掉那些对当前无用的剧烈波动。
> 3. **与残差网络（ResNet）的共鸣**：$$c\_t = f\_t \odot c\_{t-1} + \dots$$ 这种主要依靠 **加法（**$$+$$**）** 传递前一层状态的设计，启发了 18 年后（2015年）提出的残差网络（ResNet）。它们在数学本质上是一致的，都是在深层计算图（LSTM 是时间深度，ResNet 是空间深度）中添加一条线性的恒等映射旁路（Identity Connection/Highway），让梯度信息在反向传播时能畅通无阻地直接跨越层级回传，从而解决困扰深度学习多年的梯度消失难题。

下图展示了 LSTM 单元的内部结构，详细还原了各个门控与这两条主线的协同运算过程：

```mermaid
graph TD
    %% 输入
    x_t["xₜ<br/>(当前输入向量)"]
    h_prev["hₜ₋₁<br/>(上一隐状态向量)"]
    c_prev["cₜ₋₁<br/>(上一记忆向量)"]

    %% 输出
    c_next["cₜ<br/>(当前记忆向量)"]
    h_next["hₜ<br/>(当前隐状态向量)"]

    %% 门控网络层
    f_gate["遗忘门 fₜ<br/>(Sigmoid)"]
    i_gate["输入门 iₜ<br/>(Sigmoid)"]
    c_cand["候选记忆 c̃ₜ<br/>(tanh)"]
    o_gate["输出门 oₜ<br/>(Sigmoid)"]

    %% 运算节点
    op_mul_f(("⊗"))
    op_mul_i(("⊗"))
    op_add(("⊕"))
    op_tanh("tanh")
    op_mul_o(("⊗"))

    %% 连线：输入到各个门
    x_t --> f_gate & i_gate & c_cand & o_gate
    h_prev --> f_gate & i_gate & c_cand & o_gate

    %% 连线：遗忘机制 (主干道)
    c_prev -->|"状态高速公路"| op_mul_f
    f_gate -.->|"控制遗忘比例<br/>例: [0.01, 0.98, ..., 0.5]"| op_mul_f

    %% 连线：写入机制
    i_gate -.->|"控制写入比例<br/>例: [0.85, 0.05, ..., 0.7]"| op_mul_i
    c_cand -->|"提取的新知识<br/>例: [0.6, -0.9, ..., 0.2]"| op_mul_i

    op_mul_f --> op_add
    op_mul_i --> op_add

    op_add -->|"更新后的记忆"| c_next

    %% 连线：输出机制
    op_add --> op_tanh
    op_tanh --> op_mul_o
    o_gate -.->|"控制输出比例<br/>例: [0.95, 0.12, ..., 0.4]"| op_mul_o
    op_mul_o --> h_next

    %% 样式美化
    style c_prev fill:#f39c12,color:#fff,stroke:#e67e22,stroke-width:2px
    style c_next fill:#f39c12,color:#fff,stroke:#e67e22,stroke-width:2px
    style f_gate fill:#e74c3c,color:#fff,stroke:#c0392b,stroke-width:2px
    style i_gate fill:#2ecc71,color:#fff,stroke:#27ae60,stroke-width:2px
    style o_gate fill:#3498db,color:#fff,stroke:#2980b9,stroke-width:2px
    style c_cand fill:#9b59b6,color:#fff,stroke:#8e44ad,stroke-width:2px
    style op_mul_f fill:#ecf0f1,stroke:#7f8c8d,stroke-width:2px
    style op_mul_i fill:#ecf0f1,stroke:#7f8c8d,stroke-width:2px
    style op_add fill:#ecf0f1,stroke:#7f8c8d,stroke-width:2px
    style op_mul_o fill:#ecf0f1,stroke:#7f8c8d,stroke-width:2px
    style op_tanh fill:#ecf0f1,stroke:#7f8c8d,stroke-width:2px
```

图 1-3：LSTM 单元结构，记忆单元 $$c\_t$$ 通过线性通路传递信息

这种设计的精妙之处在于：当遗忘门接近 1 且输入门接近 0 时，$$c\_t \approx c\_{t-1}$$，信息可以几乎无损地沿时间传递。这条“高速公路”使得梯度能够跨越较长的距离而不至于消失，从而显著改善了长距离依赖的学习能力。

2014 年提出的**门控循环单元**（Gated Recurrent Unit，GRU）进一步简化了 LSTM 的结构，将遗忘门和输入门合并为一个“更新门”，减少了参数和计算量，同时保持了相近的性能。

LSTM 和 GRU 极大地扩展了 RNN 的能力边界，在 2014-2017 年间主导了几乎所有的 NLP 任务。但它们并没有从根本上解决 RNN 家族的另一个致命缺陷——**串行计算瓶颈**。计算效率太低，就意味着无法真正实用。

## 1.2.5 串行计算：无法逾越的效率壁垒

无论是基础 RNN、LSTM 还是 GRU，它们都有一个共同的结构特征：$$h\_t$$ 的计算必须等待 $$h\_{t-1}$$ 完成。这种**严格的顺序依赖**意味着序列中的元素无法并行处理。

对于长度为 $$n$$ 的序列，RNN 的前向计算需要 $$O(n)$$ 个顺序步骤。在训练阶段，这意味着 GPU 上数千个计算核心中，每个时间步只有一小部分被有效利用。随着训练数据和模型规模的增长，这种效率瓶颈变得越来越不可接受。

下面的对比清楚地说明了这一问题：

| 特性      | RNN/LSTM   | 理想方案       |
| ------- | ---------- | ---------- |
| 顺序操作数   | $$O(n)$$   | $$O(1)$$   |
| 并行度     | 低（串行）      | 高（全并行）     |
| 长距离依赖路径 | $$O(n)$$ 步 | $$O(1)$$ 步 |
| GPU 利用率 | 低          | 高          |

表 1-1：RNN/LSTM 与理想方案的性能对比

正是这种对并行计算的渴望，推动了两个方向的探索：一是将 CNN 引入序列建模，二是注意力机制的诞生。

## 1.2.6 CNN 在序列建模中的尝试

卷积神经网络（Convolutional Neural Network，CNN）最初为图像处理设计，但研究者发现一维卷积同样可以对序列进行建模，而且**天然支持并行计算**。

一维 CNN 通过滑动固定大小的滤波器（核）在序列上提取局部特征，不同位置的卷积运算可以同时进行。2017 年前后，Facebook 提出的 ConvS2S（卷积序列到序列模型）证明了纯 CNN 架构也能胜任机器翻译任务，且训练速度远快于 RNN。

然而，CNN 的根本局限在于其**有限的感受野**（Receptive Field）。单层 CNN 只能看到局部窗口内的几个元素。要捕捉距离较远的依赖关系，必须堆叠多层卷积，使感受野逐层扩大。对于普通连续卷积，距离为 $$d$$ 的两个元素需要大约 $$O(d/k)$$ 层卷积（$$k$$ 为核大小）才能使两者的感受野相互覆盖；只有采用空洞卷积或指数扩张的结构时，路径长度才可能降到 $$O(\log\_k n)$$。这意味着 CNN 仍必须经过多层才能跨越长距离，梯度消失问题没有被根本消除。

此外，CNN 对序列中元素的绝对位置不太敏感——同样的滤波器在不同位置提取的是相同模式的特征，这在某些需要区分位置的任务中是一个缺点。

RNN 的串行瓶颈和 CNN 的局部视野限制，共同指向了一个关键需求：**是否存在一种机制，既能实现全并行计算，又能在任意两个位置之间建立直接连接？** 答案是 [注意力机制](/llm_internals/di-yi-bu-fen-ji-chu-pian/01_introduction/1.3_attention_birth.md)。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://yeasy.gitbook.io/llm_internals/di-yi-bu-fen-ji-chu-pian/01_introduction/1.2_rnn_cnn_limits.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
