Transformer

论文:Attention Is All You Needopen in new window(2017)

Transformer 是一种基于**自注意力(Self-Attention)**的序列建模架构,不依赖循环或卷积即可对序列进行编码,成为 BERT、GPT 等大模型的基础。


自注意力(Self-Attention)

区别于传统 RNN 需要按位置逐个传递、串行计算,自注意力允许所有位置同时并行计算,并通过注意力权重直接建模任意位置之间的依赖关系,因此更易捕捉长程依赖,也便于并行加速。

多头注意力(Multi-Head Attention)

将 Q、K、V 在特征维度上拆成多组,每组独立做一次自注意力,最后把多组输出拼接再线性变换。这样模型可以在不同子空间里学习不同类型的依赖关系(如局部语法、长程指代等)。

Q、K、V

自注意力通过三组向量表示每个位置的角色:

  • Q(Query):当前位置「想找什么」—— 用于与其它位置的 Key 做匹配。
  • K(Key):当前位置「能提供什么」—— 供其它位置的 Query 来检索。
  • V(Value):当前位置的「实际内容」—— 匹配后用于加权聚合的信息。

模型通过训练学习 WQ、WK、WV 等投影矩阵,将输入映射为 Q、K、V;运行时可理解为用这些矩阵表达模型学到的「如何查询、如何提供、如何聚合」的知识。

V 不是注意力的产物,而是输入的投影。Q、K、V 三者地位对称,都是 token 自己经过一次线性变换得到的,互不依赖。真正混合上下文信息、流向下一层的,是注意力公式输出的 Z(attention output)—— 它对所有位置的 V 按权重加权求和而成。简记:V 是「素材」,Z 才是「成品」

计算示例

为便于直观理解,设序列有 3 个 token,dk = dv = 2。输入经 WQ、WK、WV 投影后得到:

  • Q1 = [1, 0],Q2 = [0, 1],Q3 = [1, 1]
  • K1 = [1, 0],K2 = [0, 1],K3 = [1, 1]
  • V1 = [1, 2],V2 = [3, 4],V3 = [5, 6]

以计算位置 3 的输出为例(即用 Q3 去查询所有位置):

  1. 点积相似度:Q3 · K1 = 1,Q3 · K2 = 1,Q3 · K3 = 2。
  2. 缩放:除以 √dk = √2,得 ≈ [0.707, 0.707, 1.414](缩放原因见下文「缩放点积注意力」)。
  3. softmax 归一化 为注意力权重:≈ [0.25, 0.25, 0.50]。
  4. 按权重聚合 V:0.25·V1 + 0.25·V2 + 0.50·V3 ≈ [3.5, 4.5]。

可以看到 Q3 与 K3 最相似(同为 [1,1]),因此 V3 在最终输出里占主导,这正符合「用 Q 查询、按 K 的相似度、加权聚合 V」的直觉。每个位置都会执行同样的计算(位置 1 用 Q1、位置 2 用 Q2……),因此整个序列可以一次性并行算完,这也是 Transformer 相比 RNN 的核心优势。

KV Cache

推理阶段是自回归的(逐个生成新 token),例如 "A" → "AB" → "ABC" → "ABCD"。若生成 D 时仍对整段序列重新算一遍注意力,会产生大量冗余计算。

QKV 结构适合做 KV Cache:生成 D 时只需新算 (Q_D)、(K_D)、(V_D),而注意力计算所依赖的 (K_A)、(V_A)、(K_B)、(V_B) 等可直接复用上一轮已算好的结果;当前步只关心「用 D 的 Query 去查前面所有 Key/Value」,因此不需要保留或复用之前的 (Q_A)、(Q_B) 等。

缩放点积注意力(Scaled Dot-Product Attention)

核心计算为:用 Query 与所有 Key 做点积得到相似度,经 softmax 得到注意力权重,再对 Value 加权求和。公式中会对点积结果除以 √(dk)(dk 为 Key 维度),以缓解维度较大时点积数值过大导致 softmax 梯度变小的问题。


多层堆叠

实际的 Transformer 由 L 层「Attention + FFN」串联而成(GPT-3 96 层、LLaMA-7B 32 层)。每一层都有自己独立的一套 WQ、WK、WV、WO 与 FFN 参数,相互不共享。因此一个 token 在一次前向中要经历 L 次 QKV 计算,每层的输入(上一层的输出)和投影矩阵都不同。

不同层学到的注意力模式有明显分工:浅层偏重局部语法与邻近依赖,中层捕捉短语结构与指代关系,深层聚焦语义、推理与长程依赖。

能否预先缓存 token × W 的结果?

直觉上似乎可以把「每个 token 经过每层 W 矩阵的结果」打成查找表,避开运行时矩阵乘法。但这条路只对第 1 层理论可行,从第 2 层起原理上不成立:

  • 第 1 层:输入是 token embedding 本身,可以预计算 embedding[v] · W^Q_1 等查找表。但实务中很少这么做——投影矩阵乘法在整个前向中占比很小,而查找表显存(vocab × d_model)反而比矩阵(d_model × d_model)大几十倍;绝对位置编码还会让查找表进一步爆炸到 vocab × max_pos。
  • 第 2 层及以后:每层输入是上一层注意力的输出,已经融合了上下文里所有 token 的信息。同一个 token 在不同句子、不同位置拿到的输入向量都不同——这正是 LLM 能做上下文相关表示的根本,也是它和静态词向量(word2vec)的本质区别。

工程上真正在做的「缓存」是 KV CachePrefix Cache:缓存的是当前会话或共享前缀的中间结果,是请求级别的,不是 token 级别的全局表。


并行与串行

「自注意力可并行」是相对 RNN 而言的——核心认知是:「依赖其他 token」 ≠ 「必须串行」。把一层 attention 的计算拆成三阶段就一目了然:

阶段 1:投影(每个 token 独立)
  Q_i = X_i · W^Q
  K_i = X_i · W^K     ← 只需 token i 自己的输入
  V_i = X_i · W^V

阶段 2:位置编码(每个 token 独立)
  对 RoPE,按 token i 自己的位置旋转 Q_i 和 K_i
  (V 不旋转)

阶段 3:注意力(跨 token,但仍是并行矩阵乘)
  scores  = Q · K^T / √d_k   ← 一次 N×N 矩阵乘
  weights = softmax(scores)
  Z       = weights · V      ← 一次矩阵乘

阶段 1、2 是逐 token 独立的,所有位置一起算;阶段 3 虽然每个 Qi 都要「看」所有 Kj,但本质是一次矩阵乘法——GPU 上 N×N 个分数同时算出,并不串行。「读所有位置」(数据已就绪)和「等某个位置」(必须等计算完成)是完全不同的依赖类型

形式数据依赖能否并行
RNN:ht = f(ht-1, xt)当前步前一步算完❌ 串行
Self-Attention:Zi = f(Qi, K1..N, V1..N)当前步所有位置(已就绪)✅ 并行

下面具体看训练和推理的并行情况。

训练 / 推理 prefill 阶段

输入是一整段已知文本,可一次性塞进模型:

维度并行 / 串行
同层内不同位置的 Q/K/V 投影并行(一次大矩阵乘)
同层内多头之间并行
batch 内多个样本并行
层与层之间串行(layer 2 必须等 layer 1 输出)

「位置维度可并行」是 Transformer 相比 RNN 的核心优势——RNN 必须 t-1 算完才能算 t。

推理 decode 阶段

进入自回归生成后,每步只产出一个新 token,并行度被打破一部分:

维度并行 / 串行
新 token 之间串行(必须先有 D 才能算 E)
当前步的多头之间并行
层与层之间串行
位置维度退化为 1×N 向量乘(当前步只有 1 个新 Q,K/V 来自缓存)

这就是为什么推理优化里有大量花活:KV Cache 复用已算的 K/V;Continuous Batching 把不同请求拼到同一 batch,把「用户之间」变成新的并行维度;投机采样用小模型并行验证多个候选 token,把「token 之间」的串行部分变回并行(详见 inference.md)。


架构变体

标准 Transformer 的每一层都是「Attention + 稠密 FFN」,所有权重在每次前向都参与计算。围绕「用更多参数装下更多知识,但单次推理算力可控」这一目标,业界发展出若干架构变体,最具代表性的就是稀疏激活与 MoE。

稠密模型 vs 稀疏激活模型

对比稠密(Dense)稀疏激活(常指 MoE 等)
在算什么单次前向里,绝大部分(通常全部)权重都会参与当前计算单次前向里,只对当前输入激活一部分子网络;其余权重本次不算,但仍占显存
直觉参数多,单次计算也跟着「满负荷」总参数量可以很大,单次激活参数量却可控制在较小范围
实现结构相对单纯需要路由、负载均衡等,工程更复杂

易混概念:「权重稀疏」指矩阵里大量元素为 0(压缩 / 剪枝语境),和 MoE 的「每次只激活部分专家」不是一回事,阅读论文或新闻时要分清主语是矩阵结构还是计算路径

混合专家模型(MoE)

MoE 是稀疏激活的典型实现,作为对标准 Transformer 中 FFN 子层的替换:网络里并列多个专家(小型前馈子层等),对**每个 token(或每块输入)门控 / 路由器(router)**动态选出 Top-k 个专家参与计算,其余专家不参与这一次前向。

  • 为何常用:在相近训练算力下堆出更大总参数,同时让推理或训练时的激活 FLOPs 可控;也常与负载均衡、辅助损失等训练技巧一起出现,避免少数专家被「打爆」、多数闲置。
  • 专家在学什么:专家通常没有人工标注的「领域名」;事后分析里有时会看到近似分工,但因模型与数据而异,不宜把某种模式当成 MoE 的定义。
  • 与注意力的关系:MoE 通常只替换 FFN,注意力层仍是稠密的;因此前文「多层堆叠」「并行与串行」中关于注意力的讨论对 MoE 同样适用。