00. Transformer:大模型共同的骨架
本系列讲到的几乎所有大语言模型都是 Transformer:Llama、Qwen、DeepSeek、Mistral、Gemma、gpt-oss,以及 BERT、Whisper、多模态模型里的视觉编码器。它们在 MoE、MLA、混合注意力上各有改动,但骨架都来自 2017 年 Google 的论文《Attention Is All You Need》。这一篇先把这副骨架完整地讲一遍:它要解决什么问题,原始结构由哪几块组成,一句话怎样流过编码器和解码器,以及今天的大模型保留了什么、换掉了什么。
读完你应该能画出原始 Transformer 的结构图,说清每一块的作用和输入输出的形状,理解它为什么能取代之前的循环神经网络,并知道后面第 01—08 篇分别是在拆这副骨架的哪一块。
它要解决的问题:按顺序读太慢,也记不住
Transformer 出现之前,处理文字主要用循环神经网络(RNN,以及改进版 LSTM、GRU)。RNN 像人逐字读书:读一个 token,更新一次内部状态,再读下一个。翻译“年假怎么申请?”时,编码器要依次读入“年”“假”“怎么”“申请”“?”,每一步的状态都依赖上一步。
这种做法有两个问题。第一是慢:第 5 步必须等第 4 步算完,一句话有多长就要串行算多少步,GPU 上几千个计算核心只能等着,训练时也没法把一句话的各个位置同时算。第二是远处的信息容易丢:“年”的信息要经过 4 次状态更新才传到“?”,句子一长,早期的内容被反复压缩进一个固定大小的向量,越传越淡。
当时已经有人在 RNN 上加注意力:生成译文的每个词时,回头看一眼原文的所有位置,按相关程度加权取信息。Transformer 的做法更彻底,论文标题就是答案:只要注意力,不要循环。每个位置直接和其他所有位置计算相关程度,任意两个词之间只隔一步;所有位置的计算可以写成几次矩阵乘法一次算完。论文用一张表比较了三种结构(n 是序列长度,d 是向量维度):
| 层的类型 | 每层计算量 | 必须串行的步数 | 任意两位置的最长路径 |
|---|---|---|---|
| 自注意力 | O(n² · d) | O(1) | O(1) |
| 循环(RNN) | O(n · d²) | O(n) | O(n) |
| 卷积(核宽 k) | O(k · n · d²) | O(1) | O(log_k n) |
自注意力把串行步数和路径长度都降到常数,代价是计算量里出现了 n²:5 个 token 要算 25 对关系,10 万个 token 就是 100 亿对。这个平方项是后来长上下文研究的主要对象(第 11 篇)。在 2017 年的翻译任务里,句子通常只有几十个 token,n 远小于 d(512),自注意力反而比 RNN 更省。
原始结构:编码器读,解码器写
原始 Transformer 是为翻译设计的,分成两半。编码器(encoder)读入整段原文,把每个位置变成一个包含上下文信息的向量;解码器(decoder)一个词一个词地写译文,每写一个词都去查编码器的输出。
论文的 base 配置是:向量维度 d_model = 512,编码器和解码器各 6 层,每个注意力 8 个头、每头 64 维,FFN 中间维度 2048。图里从下往上读:
- 输入嵌入:每个 token 查表得到一个 512 维向量,乘以 √512 ≈ 22.6,再加上同样 512 维的位置编码。原文 5 个 token 得到形状
[5, 512]的矩阵。 - 编码器的一层:先做多头自注意力,每个位置看整句所有位置(双向);再做前馈网络(FFN),对每个位置单独做 512 → 2048 → 512 的变换。两个子层后面都有“残差相加 + 层归一化”。输出形状不变,仍是
[5, 512],6 层叠起来。 - 编码器输出:最后一层的
[5, 512]就是原文的“记忆”,解码器每一层都会读它。 - 解码器的一层:三个子层。第一个是带掩码的自注意力,只能看已经写出的译文,看不到右边还没写的词;第二个是交叉注意力(cross-attention),查询(Q)来自译文,键(K)和值(V)来自编码器输出,这一步把原文的信息搬进译文;第三个是 FFN。每个子层后面同样是残差相加和层归一化。
- 输出层:解码器最后一层的向量经过一个线性层,映射到词表大小(英德翻译约 37,000 个 token),softmax 变成概率,取最后一个位置的概率分布挑出下一个词。
以翻译为例:译文已经写到“How to apply”,加上开头的起始符 <s>,解码器输入是 4 个 token。写下一个词时,“apply”这个位置在交叉注意力里会重点看原文的“申请”和“年假”,最终输出层给“for”最高的概率。“for”接到译文末尾,再算下一步,直到输出结束符。这就是自回归生成,今天的大模型也是这样一个 token 一个 token 地写(第 01 篇)。
五个零件
整副骨架只由五种零件组成,后面的章节逐个展开。
注意力。 每个位置生成三个向量:查询 Q(我在找什么)、键 K(我能提供什么)、值 V(我的内容)。用 Q 和所有位置的 K 做点积得到相关程度,除以 √64 防止数值过大,softmax 变成权重,再对 V 加权求和。论文把它写成一行:
text
Attention(Q, K, V) = softmax(Q · Kᵀ / √d_k) · V
Q、K、V:[序列长度, 64],d_k = 64
Q · Kᵀ:[序列长度, 序列长度],每对位置一个分数解码器里的掩码,就是把“看右边”的那些分数在 softmax 之前设成负无穷,权重变成 0。用 4 个 token 手算一遍的完整过程见第 03 篇。
多头。 一次注意力只能给出一组权重,而一个词往往要同时关注几种关系(语法上的主语、语义上的修饰对象)。多头注意力把 512 维切成 8 份,每份 64 维各自做一次注意力,最后拼回 512 维再经过一个线性层。8 个头的总计算量和一个 512 维的头差不多。今天的模型在此基础上让多个头共享 K、V,以缩小推理时的缓存(第 04 篇)。
前馈网络(FFN)。 注意力负责在位置之间搬运信息,FFN 负责对每个位置单独加工:先升到 2048 维,过一个 ReLU(负数变 0),再降回 512 维。它不看其他位置,但参数比注意力多:base 配置每层 FFN 约 210 万参数,自注意力约 105 万。到了今天的大模型,FFN 占全部参数的七成左右(第 05 篇)。
残差连接与层归一化。 每个子层的输出不是替换输入,而是加到输入上(残差连接),这样信息和梯度都有一条直通的路,几十层也能训练。原始论文把层归一化放在相加之后,写成 LayerNorm(x + 子层(x)),叫 Post-Norm。这种放法在层数多时训练不稳定,需要学习率预热(warmup)小心呵护,后来的模型都改成先归一化再进子层(第 06 篇)。
位置编码。 注意力只看内容的相关程度,不知道词的先后:把“年假怎么申请”打乱成“申请怎么年假”,每个词得到的注意力结果完全一样。原始论文用一组不同频率的正弦、余弦函数生成位置向量,加在词嵌入上:
text
PE(pos, 2i) = sin(pos / 10000^(2i/512))
PE(pos, 2i+1) = cos(pos / 10000^(2i/512))
pos:位置编号;i:第几对维度(0—255)
第 0、1 维(i = 0,波长 2π ≈ 6.3 个位置):
pos 0 → (0, 1) pos 1 → (0.8415, 0.5403)
pos 2 → (0.9093, -0.4161) pos 3 → (0.1411, -0.9900)
第 256、257 维(i = 128,波长约 628 个位置):
pos 0 → (0, 1) pos 1 → (0.0100, 1.0000) pos 3 → (0.0300, 0.9996)低维变化快,能区分相邻的位置;高维变化慢,最长的波长约 6 万个位置,能表达远距离。今天的模型改用 RoPE,把位置信息作为旋转角度作用在 Q、K 上,直接体现两个 token 的相对距离(第 07 篇)。
另外两个配套部件是词嵌入和输出层。原始论文让原文、译文的词嵌入和输出层共用同一个矩阵,这种“共享”在今天的小模型里仍然常见(第 02 篇、第 08 篇)。
参数、训练与结果
按 base 配置可以手算参数量(包含原始论文里各线性层的偏置):
text
编码器每层:自注意力 4 × (512×512 + 512) = 1,050,624
FFN 512×2048 + 2048 + 2048×512 + 512 = 2,099,712
两个 LayerNorm 2 × 1,024 = 2,048 合计 3,152,384
解码器每层:两个注意力 2,101,248 + FFN 2,099,712 + 三个 LayerNorm 3,072 = 4,204,032
共享词嵌入:37,000 × 512 = 18,944,000
总计:6 × 3,152,384 + 6 × 4,204,032 + 18,944,000 ≈ 6308 万论文报告 base 约 6500 万参数,big 版本(d_model 1024、FFN 4096、16 个头)约 2.13 亿,按同样方法手算 big 得 2.14 亿。两者的小差别来自词表的确切大小和计数口径。
训练用的是 WMT 2014 英德(约 450 万句对)和英法(约 3600 万句对)翻译数据,在一台 8 张 NVIDIA P100 的机器上:base 训练 10 万步约 12 小时,big 训练 30 万步约 3.5 天。优化器是 Adam(β₂ = 0.98),学习率先在 4000 步内线性升高、再按步数的平方根倒数下降,这个预热在 Post-Norm 结构下几乎是必需的;另外用了 0.1 的 dropout 和标签平滑。big 模型在英德翻译上 BLEU 28.4,英法 41.8,当时都超过了此前的最好成绩,而训练成本只是此前模型的一小部分。
训练时还有一点和生成时不同:译文是已知的,解码器可以把整段目标译文一次输入,靠掩码保证每个位置只看左边,所有位置的“预测下一个词”同时计算损失。这种做法叫 teacher forcing。它让训练可以充分并行,也是 Transformer 比 RNN 训练快得多的主要原因。今天大模型的预训练同样如此:一段 8000 个 token 的文本,一次前向就得到 8000 个位置的预测和损失(训练系列第 02 篇)。
从翻译模型到大语言模型
原始 Transformer 发表后,研究者拿走了它的不同部分:BERT 只用编码器做理解,GPT 只用解码器做生成,T5 保留整体做“文本到文本”。到今天,几乎所有大语言模型都是只保留解码器、去掉交叉注意力的版本,原因和证据在第 09 篇讨论。
骨架留下来了,零件几乎全换了一遍:
拿资料助手用的 Llama-3.1-8B 对照:它的每一层仍然是“注意力 + FFN,各带残差”,这是 2017 年的结构;但归一化换成 RMSNorm 并放到子层之前,FFN 换成 SwiGLU,位置换成 RoPE,32 个查询头只配 8 组 K、V,线性层去掉了偏置,训练时也不再用 dropout。层数从 6 加到 32,向量从 512 维加到 4096 维,参数从 6500 万到 80 亿,多了一百多倍。
Transformer 也不只用在文字上。图像切成小块以后可以当 token 输入(ViT,第 17 篇);语音识别模型 Whisper 至今仍是编码器-解码器结构;检索用的向量模型多是编码器;生成图像的扩散模型里,去噪网络也越来越多地换成 Transformer。这些模型和大语言模型共用同一套零件,读懂本系列的第一部分,再看它们会容易很多。
它的局限
原始 Transformer 的几处设计,恰好是后来改动最多的地方,也就是后面各篇要回答的问题:
- 平方成本:上下文从几十个 token 变成几十万个以后,n² 的注意力计算和随长度增长的 KV Cache 成了主要成本(第 04 篇、第 11 篇、第 12 篇)。
- 长度外推:正弦位置编码理论上能算任意位置,但模型只在训练长度内见过这些组合,直接用到更长的输入效果明显下降(第 07 篇)。
- 深层训练不稳:Post-Norm 在层数加深时容易发散,需要很长的预热(第 06 篇)。
- 参数全激活:每个 token 都要经过全部 FFN 参数,模型越大每个 token 越贵,于是有了只激活一部分专家的 MoE(第 10 篇)。
这四处改动加在一起,就是从 2017 年的翻译模型到今天的大语言模型之间的主要距离。
骨架已经完整走过一遍。下一篇第 01 篇换成今天的 decoder-only 大模型,以 Llama-3.1-8B 为例看一句话怎样走完 32 层:每一步的张量形状,80 亿参数分别在哪里,每个 token 的计算花在哪里。
