Skip to main content

第4章:Transformer——注意力可以是全部

2017 年,一篇论文用八个字改写了自然语言处理的历史:"Attention Is All You Need"。它宣告:RNN 不是必要的。只要有注意力机制,一切都可以重建。

上一章我们看到,RNN 和 LSTM 通过引入"记忆"解决了序列建模问题,但留下了三个难以根治的伤口:训练无法并行、长距离信息仍然会衰减、模型容量难以大幅扩展。

这些问题悬在那里,等待着一个更大胆的想法。


4.1 Attention Is All You Need

三个痛点,一个激进的答案

2017 年,Google 的研究团队发表了那篇著名论文。他们提出的方案极其大胆:把 RNN 完全去掉。不用隐状态,不用时间步,不用循环。整个模型只依赖一种机制——注意力(Attention)。

这个想法在当时显得有些疯狂。RNN 已经是序列建模的标配,LSTM 更是经过了十多年的打磨。要一刀砍掉,凭什么?

凭的是对三个痛点的精准击中:

RNN 的问题原因Transformer 的解法
训练无法并行计算 hth_t 必须等 ht1h_{t-1}所有位置同时计算,无依赖链
长距离依赖衰减信息要经过 nn 步才能传递任意两位置直接关注,路径长度 O(1)O(1)
模型容量有限隐状态维度受限于序列计算参数量可以自由扩展至数十亿

并行计算意味着:翻译一个 100 词的句子,不需要等 100 个时间步依次完成,所有位置可以在同一批次同时处理,充分利用 GPU 的矩阵运算能力。

长距离路径 O(1)O(1) 意味着:"她"和十句话之前的"玛丽"之间,不需要信息穿越中间所有单词的"中转站"——它们可以直接建立联系。

Self-Attention 的直觉:图书馆里的查询

理解 Self-Attention(自注意力)最好的方式是一个类比。

想象你走进一座图书馆,想找一本关于"气候变化对农业影响"的书。

图书馆的运作方式是这样的:

  1. 你提交一个查询(Query):你把"气候变化对农业的影响"写在一张纸条上,递给图书管理员。

  2. 书架上的每本书有一个索引标签(Key):每本书的封面上写着它的主题关键词——"气候科学"、"农业经济"、"政策分析"……

  3. 每本书里有真正的内容(Value):书的内容本身,是你真正想获取的知识。

图书管理员会把你的查询纸条和每本书的索引标签做匹配打分,分数高的书被重点推荐,你最终得到的是一个加权的"综合阅读摘要"。

这就是 Self-Attention 的工作原理。不同的是,在语言模型里,每个词既是查询者,又是被查询的书。每个词都会向所有其他词发出查询:"我需要来自哪些词的信息?",然后按相关度加权汇总。

Q、K、V 的数学表达

在 Transformer 里,每个词的词向量 x\mathbf{x} 通过三个可学习的权重矩阵,分别变换为三个向量:

Q=xWQ,K=xWK,V=xWV\mathbf{Q} = \mathbf{x} W^Q, \quad \mathbf{K} = \mathbf{x} W^K, \quad \mathbf{V} = \mathbf{x} W^V
  • Q\mathbf{Q}(Query,查询):这个词想要什么信息?
  • K\mathbf{K}(Key,键/索引):这个词能提供什么信息?
  • V\mathbf{V}(Value,值/内容):这个词实际携带的信息是什么?

注意力分数的计算分三步:

第一步:打分。每个查询向量和所有键向量做点积,得到相似度分数:

score(i,j)=QiKj\text{score}(i, j) = \mathbf{Q}_i \cdot \mathbf{K}_j

第二步:归一化。为了防止点积值过大导致梯度消失,除以维度的平方根 dk\sqrt{d_k},然后用 Softmax 转换成概率分布:

αij=softmax ⁣(QiKjdk)\alpha_{ij} = \text{softmax}\!\left(\frac{\mathbf{Q}_i \cdot \mathbf{K}_j}{\sqrt{d_k}}\right)

第三步:加权求和。用注意力权重 αij\alpha_{ij} 对值向量加权求和,得到这个词的新表示:

outputi=jαijVj\text{output}_i = \sum_j \alpha_{ij} \mathbf{V}_j

整个过程写成矩阵形式就是:

Attention(Q,K,V)=softmax ⁣(QKTdk)V\text{Attention}(Q, K, V) = \text{softmax}\!\left(\frac{QK^T}{\sqrt{d_k}}\right) V

:::tip 直觉小结 Q、K、V 的分离是关键设计。同一个词的"我想问什么"(Q)和"我能回答什么"(K)是不同的——就像一个人提问和被提问时的角色是不同的。分开训练让模型能学到更丰富的语义关系。 :::

Multi-Head Attention:同时关注多个维度

一个注意力头只能捕捉一种关注维度。但语言是多维的:句法依赖、语义关联、指代关系……同时发生。

Transformer 的解法是多头注意力(Multi-Head Attention):把词向量切成 hh 份,每份独立做一组 Q/K/V 变换,跑一遍注意力,最后把所有头的输出拼接起来:

MultiHead(Q,K,V)=Concat(head1,,headh)WO\text{MultiHead}(Q,K,V) = \text{Concat}(\text{head}_1, \ldots, \text{head}_h)\, W^O

不同的头可以学到不同的关注模式。实验发现,某些头专门关注句法依赖,某些头关注语义相关性,某些头处理指代消解。整个模型在同一层内就能同时处理多种语言结构。


4.2 Transformer 解决了什么,又留下了什么

架构全貌

完整的 Transformer 由**编码器(Encoder)解码器(Decoder)**两部分组成,堆叠多层(原论文用了 6 层):

输入序列

[词嵌入 + 位置编码]

┌─────────────────────┐
│ Encoder Layer × 6 │
│ ┌─────────────────┐│
│ │ Multi-Head Attn ││ ← 自注意力:每个词关注所有词
│ │ Add & LayerNorm ││
│ │ Feed-Forward ││ ← 逐位置的全连接层
│ │ Add & LayerNorm ││
│ └─────────────────┘│
└─────────────────────┘
↓ 编码器输出 (K, V)
┌─────────────────────┐
│ Decoder Layer × 6 │
│ ┌─────────────────┐│
│ │ Masked Attn ││ ← 只看已生成的词(防止"作弊")
│ │ Cross Attn ││ ← 用编码器的 K/V,解码器的 Q
│ │ Feed-Forward ││
│ └─────────────────┘│
└─────────────────────┘

输出序列

问题一:去掉 RNN 后,位置信息怎么表示?

RNN 天然知道"顺序"——h1h_1h2h_2 之前处理,顺序被编码进了时间步本身。但 Transformer 的自注意力对所有位置一视同仁,本质上是一个无序的集合操作

如果不做任何处理,"猫咬了狗"和"狗咬了猫"对模型来说将毫无区别。

解决方案是位置编码(Positional Encoding):给每个位置手工注入一个独特的信号,叠加到词嵌入上。

原论文使用正弦/余弦函数:

PE(pos,2i)=sin ⁣(pos100002i/dmodel)\text{PE}_{(pos, 2i)} = \sin\!\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right) PE(pos,2i+1)=cos ⁣(pos100002i/dmodel)\text{PE}_{(pos, 2i+1)} = \cos\!\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right)

其中 pospos 是位置,ii 是维度索引。

这种设计的直觉:不同频率的正弦波叠加,就像时钟的秒针、分针、时针——组合起来可以唯一标识任意时刻。不同维度对应不同"频率",低频维度区分远距离位置,高频维度区分近距离位置。

:::info 位置编码的演进 原始的固定正弦编码在后来的工作中被不断改进:BERT 使用可学习的位置嵌入,现代 LLM 普遍使用 RoPE(旋转位置编码),甚至 ALiBi(基于偏置的线性注意力)。位置表示至今仍是活跃的研究方向。 :::

问题二:注意力的 O(n2)O(n^2) 计算复杂度

Transformer 解决了并行化和长距离依赖,但它的自注意力机制带来了一个新的根本性问题。

计算注意力矩阵 QKTQK^T 时,对于长度为 nn 的序列,我们需要计算 n×nn \times n 个位置对之间的分数。这意味着:

  • 时间复杂度O(n2d)O(n^2 \cdot d),其中 dd 是向量维度
  • 空间复杂度O(n2)O(n^2),需要存储整个注意力矩阵

这在短序列下完全可以接受。但当序列变长,代价将以平方速度增长。


4.3 O(n2)O(n^2) 的诅咒——长上下文的代价

平方增长意味着什么?

让我们用具体数字感受一下:

序列长度注意力矩阵大小相对开销
512 token512×512=262,144512 \times 512 = 262{,}1441×1\times
2K token2048×20484×1062048 \times 2048 \approx 4\times10^616×16\times
8K token8192×819267×1068192 \times 8192 \approx 67\times10^6256×256\times
32K token32768×3276810932768 \times 32768 \approx 10^94096×4096\times
128K token131072×1310721.7×1010131072 \times 131072 \approx 1.7\times10^{10}65536×65536\times

从 512 扩展到 32K,上下文窗口增长了 64 倍,但注意力计算的开销增长了 4096 倍

这不只是一个理论上的不优雅。它带来了非常实际的限制:

  • 原始 Transformer 论文只用了 512 token 的上下文窗口
  • BERT(2018)也只有 512 token
  • GPT-2(2019)扩展到 1024 token,已经被认为是进步
  • 2021 年之前,绝大多数模型都卡在 2048 token 以内

一个 2048 token 的窗口大约相当于一篇中等长度的新闻文章。无法处理一本书,无法阅读一份完整的合同,无法理解一段长对话的上下文。

为什么这是一条贯穿全书的问题线

O(n2)O(n^2) 的限制不是一个可以被轻易修补的 bug,它是注意力机制的结构性代价——每个词要关注所有词,这本身就是平方复杂度的来源。

:::warning 悬而未决的问题 这个问题将在接下来的数年里催生大量研究工作:

  • 稀疏注意力:不是每对位置都计算,只算重要的(Sparse Transformer, Longformer)
  • 线性注意力:用近似方法把复杂度降到 O(n)O(n)(Performer, Linear Transformer)
  • 滑动窗口注意力:只关注局部窗口(Longformer, BigBird)
  • Flash Attention:不改变复杂度,但极大优化了内存访问模式
  • KV Cache 优化:在推理阶段减少重复计算

每一种方案都是对"如何在保持注意力表达能力的同时,打破平方复杂度"这一问题的不同回答。 :::

这条问题线将贯穿我们此后讨论的几乎所有注意力优化工作。先记住这个数字:4096 倍。从 512 到 32K,付出的代价。


本章小结

概念核心要点
Self-Attention每个词向所有词发出查询,按相关度加权汇总信息
Q / K / VQuery(想要什么)/ Key(能提供什么)/ Value(实际内容)
注意力公式softmax(QKT/dk)V\text{softmax}(QK^T/\sqrt{d_k})V
Multi-Head Attention多头并行,捕捉不同维度的语言关系
位置编码用正弦/余弦信号为无序的注意力注入位置信息
O(n2)O(n^2) 复杂度注意力矩阵随序列长度平方增长,是长上下文的根本瓶颈
并行化优势去掉 RNN 后,所有位置同时计算,充分利用 GPU

Transformer 像一次清场:它扫掉了 RNN 时代积累的三大痛点,用一个优雅的机制重建了序列建模。但它也留下了一张新的账单——O(n2)O(n^2)

下一章,我们将看到这张账单是怎么被第一次"部分兑现"的:BERT 和 GPT 如何基于 Transformer,走向截然不同的两条路,并各自开创了一个时代。