09. 从 Transformer 到 GPT:三种结构
前八篇把一个 decoder-only 模型从分词拆到输出,上一篇停在“根据”这个 token 被选出来。这一篇往回看:这套结构不是凭空出现的。2017 年的原始 Transformer 是为翻译设计的编码器-解码器,之后分出了只用编码器的 BERT、保留两半的 T5、只用解码器的 GPT 三条路线,最后几乎所有大语言模型都走了第三条。读完你应该能说清三种结构在“谁能看见谁”上的区别、各自适合什么任务,理解 decoder-only 成为主流有哪些证据、哪些只是工程与生态上的原因,并能指出从 GPT-2 到 Llama 换掉了哪些零件。
2017:为翻译设计的编码器-解码器
设想资料助手要把员工手册的“年假申请”一节翻成英文。整段中文原文一开始就全部已知,要生成的是另一种语言的一段新文本。2017 年的原始 Transformer 正是为这类任务设计的:编码器用双向自注意力读原文,解码器用只看左边的因果自注意力写译文,并通过交叉注意力(Q 来自译文,K、V 来自编码器输出)查看原文。完整结构、各部件的形状和 base 配置(d_model 512、6+6 层、约 6500 万参数)在第 00 篇已经走过一遍。这里只记住它和今天模型的几处不同:位置用正弦函数算好加在输入上,归一化放在残差相加之后(Post-Norm),FFN 用 ReLU,词嵌入和输出层共享一份权重。这些零件在后来几年被逐个换掉,本篇最后一节会对照。
三种结构的差别,在于谁能看见谁
原始 Transformer 发表后一两年里,研究者分别拿走了它的一半或保留整体,形成三条路线。区别最直观的看法是画出注意力的可见范围。
图里每个方格的行是当前位置、列是被看的位置。编码器是满格,解码器是下三角,编码器-解码器两样都有,外加一块“输出看全部输入”的交叉注意力。这个差别直接决定了训练目标和能做的任务。
BERT(2018):只读不写的编码器。 BERT 只保留编码器,训练时随机遮住 15% 的 token,让模型根据两边的上下文把它们填回来(掩码语言模型,MLM),早期还加了一个“判断两句话是否相邻”的任务。base 版 12 层、768 维、12 头,约 1.1 亿参数;large 版 24 层、1024 维,约 3.4 亿。双向注意力让每个位置的向量同时吸收左右两边的信息,非常适合“读懂一段已知文本”:分类、实体识别、把文本变成检索向量、给检索结果重排序。代价是它不能自然地逐字生成,因为每个位置都要看右边,而生成时右边还不存在。
T5(2019):把所有任务写成文本到文本。 T5 保留了完整的编码器-解码器,把翻译、摘要、分类、问答统一成“输入一段文字、输出一段文字”,比如分类任务的输出就是类别名称这几个字。预训练目标是片段填空(span corruption):在输入里挖掉若干段,用占位符代替,让解码器写出被挖掉的内容。它的零件已经比 2017 年现代:位置改成按距离分桶的相对偏置,LayerNorm 改成无 bias、不减均值的简化形式(和后来的 RMSNorm 一样),归一化放在子层之前。base 版编码器、解码器各 12 层,约 2.2 亿参数,最大 110 亿。
GPT(2018—2020):只保留解码器。 GPT-1 去掉了编码器和交叉注意力,只剩因果自注意力加 FFN,12 层、768 维、上下文 512,在书籍语料上做“预测下一个 token”的预训练,再针对每个下游任务微调。GPT-2(2019)把 LayerNorm 移到每个子层的输入端,最大版本 48 层、1600 维、15 亿参数,上下文 1024,并展示了不微调、只靠提示完成任务的能力。GPT-3(2020)扩到 1750 亿参数,96 层、12288 维、96 个头、上下文 2048,层间交替使用稠密注意力和局部带状稀疏注意力;它的论文主题是上下文学习:在提示里放几个例子,模型就能照着做新任务,不需要更新任何权重。
| 模型 | 年份 | 结构 | 典型规模 | 位置编码 | 预训练目标 |
|---|---|---|---|---|---|
| Transformer | 2017 | 编码器-解码器 | 6+6 层,约 0.65 亿 | 正弦绝对位置 | 有监督翻译 |
| BERT | 2018 | 编码器 | 12 层 1.1 亿 / 24 层 3.4 亿 | 学习式,512 | 掩码填空 |
| GPT-1 | 2018 | 解码器 | 12 层,约 1.17 亿 | 学习式,512 | 下一个 token |
| GPT-2 | 2019 | 解码器 | 最大 48 层,15 亿 | 学习式,1024 | 下一个 token |
| T5 | 2019 | 编码器-解码器 | base 2.2 亿,最大 110 亿 | 相对位置偏置 | 片段填空 |
| GPT-3 | 2020 | 解码器 | 96 层,1750 亿 | 学习式,2048 | 下一个 token |
为什么大模型几乎都走 decoder-only
到 2023 年以后,Llama、Qwen、DeepSeek、Mistral、Gemma、gpt-oss 这些主流开源大语言模型全部是 decoder-only。闭源模型的结构大多没有公开,不能直接算进来。这个结果背后有实验证据,也有工程和生态上的原因,两者要分开看。
对照实验:结论取决于有没有多任务微调。 2022 年 Wang 等人做了一组控制变量的实验:50 亿参数级别、1700 亿 token,比较因果 decoder-only、非因果(前缀)decoder 和编码器-解码器三种结构,配上自回归和掩码两种预训练目标,再看做不做多任务提示微调。结论有两条:只做无监督预训练时,因果 decoder-only 加自回归目标的零样本能力最好;如果之后再做多任务提示微调,用掩码目标训练的非因果结构反而最好。论文还发现预训练好的模型可以从一种结构改造成另一种。所以“decoder-only 一定最强”并不是这项研究的结论,它支持的是“只靠预训练、直接拿来用提示做新任务”这条路线下 decoder-only 更合适,而这恰好是大模型走的路。
训练信号更密。 预测下一个 token 时,序列里每个位置都有一个答案、都贡献一份损失;BERT 只对被遮住的 15% 位置算损失,T5 的片段填空也只挖掉一小部分 token 让解码器写出来。同样的文本,decoder-only 每个 token 都被当作一次练习。这是从训练目标的定义直接推出来的,但它是否就是质量差距的主因,没有单独的对照实验能回答。
一条序列装下所有任务。 decoder-only 把指令、示例、检索资料、对话历史和回答都放在同一条序列里,任务靠提示描述,GPT-3 的上下文学习就建立在这一点上。编码器-解码器也能这样用(T5 就是文本到文本),但要事先决定哪部分进编码器、哪部分让解码器写。
推理时缓存可以直接复用。 这一点在多轮对话里最明显。资料助手第一轮回答完“年假怎么申请?”,小林追问“要提前几天?”:
decoder-only 的每个位置只看左边,第一轮已有 token 的 K、V 不会因为后面来了新问题而改变,第二轮只需算新增的 token,前缀缓存也能跨请求复用同一段系统提示(见推理系列第 03 篇)。编码器是双向的,旧 token 也要“看”新问题,新问题一进来,整段历史的编码表示全部改变,只能重新编码,解码器用的交叉注意力 K、V 也要跟着重算。这是由注意力的可见范围直接推出的机制差异,不依赖任何实验。
规模化研究与生态的路径依赖。 2020 年前后的规模定律研究、GPT-3 的成功、之后开源社区的训练框架、推理引擎(KV Cache 管理、投机解码)、量化工具几乎都围绕 decoder-only 建设。一旦这些基础设施成熟,新模型沿用同一结构的成本最低。这是行业观察,不是“decoder-only 在原理上更优”的证明。
边界:另外两种结构仍在用。 做检索向量、重排序、分类时,编码器模型仍然常见,2024 年的 ModernBERT 就是一个新的纯编码器模型;语音识别的 Whisper 是编码器-解码器,音频由编码器一次读完,文字由解码器写出。Google 2025 年的 T5Gemma 把预训练好的 decoder-only Gemma 2 改造成编码器-解码器,报告它在质量与推理效率的取舍上优于原来的 decoder-only 版本,之后又基于 Gemma 3 推出了 T5Gemma 2。选型时可以按任务形态判断:输入完整已知、只需理解或输出很短,编码器更省;输入长、输出短且边界清楚,编码器-解码器值得考虑;要对话、续写、用提示做各种任务,decoder-only 最顺手。反过来的失败情况也很典型:拿 BERT 去生成长回答做不到;用一个 8B decoder-only 模型给每天几百万条工单做分类,按参数量估算每条的计算量约是 BERT-base 的 70 倍,而在固定类别的任务上,微调过的小编码器准确率未必更低。
从 GPT-2 到 Llama:同一个骨架,换了一批零件
Llama-3.1-8B 和 GPT-2 都是“词嵌入 → N 层(注意力 + FFN,各带残差)→ 输出层”,骨架没有变。变的是每一层里的零件。把 GPT-2 small 的一层和 Llama-3.1-8B 的一层并排放:
Llama 1 的论文在架构一节里直接列出了它借来的三样东西:GPT-3 的预归一化(改用 RMSNorm)、PaLM 的 SwiGLU、GPT-Neo 系列用过的旋转位置编码。后来的 Llama 2 在 34B、70B 上加了 GQA,Llama 3 推广到所有尺寸并把词表扩到 128256。逐项看:
- Pre-Norm。 2017 年的 Post-Norm 把归一化放在残差相加之后,层数一多训练容易不稳,需要小心的学习率预热;GPT-2 已经把归一化移到子层输入端,残差流上不再有归一化,梯度可以沿残差直通。这一项 GPT-2 就有,Llama 沿用。机制见第 06 篇。
- RMSNorm 代替 LayerNorm。 不减均值、没有 bias,只除以均方根再乘缩放,计算更省,实践中效果相当(第 06 篇)。
- SwiGLU 代替 GELU。 FFN 从两个矩阵变成三个(门、上、下),为了保持参数量相近,Llama 1 把中间维度取约 2/3 × 4d;Llama 3 又多乘了 1.3 的系数,Llama-3.1-8B 是 14336,约 3.5 倍(第 05 篇)。
- RoPE 代替学习式位置。 不再有 1024 行的位置表,位置在注意力里通过旋转 q、k 表达,长度可以靠缩放扩展(第 07 篇)。
- 去掉 bias。 PaLM 的论文报告,线性层和归一化都不用 bias 能提升大模型的训练稳定性。Llama 的 q、k、v、o 和 FFN 都没有 bias。也有例外:Qwen2 和 Qwen2.5 保留了 q、k、v 投影的 bias,Qwen3 又去掉了(第 14 篇)。
- GQA 代替多头注意力。 32 个查询头共用 8 组 K、V,KV Cache 缩小到四分之一,质量损失很小(第 04 篇)。
- 去掉 dropout。 GPT-2 在注意力、残差和嵌入处都有 0.1 的 dropout;Llama 的配置里
attention_dropout是 0.0,残差路径上没有 dropout。常见的解释是:dropout 是防过拟合的正则手段,而大模型预训练在十几万亿 token 上基本只过一遍数据,过拟合不是主要矛盾,dropout 反而拖慢学习。这个解释合理,但各家没有公开系统的对照实验。 - 其他随规模变化的选择。 词表从 50257 扩到 128256(第 02 篇);8B 以上的模型通常不共享词嵌入和输出层,1B 级小模型仍然常共享;上下文从 1024 拉到 131072。GPT-3 那种交替稀疏注意力没有被 Llama 沿用,但“部分层只看局部”的思路后来以滑动窗口、分块注意力的形式回来了(第 11 篇)。
把这些零件放回代码里,Transformers 的 LlamaDecoderLayer.forward 简化后只有几行:
python
# 简化自 LlamaDecoderLayer.forward;x: [batch, seq, 4096]
residual = x
x = self.input_layernorm(x) # RMSNorm,Pre-Norm
x = self.self_attn(x, position_embeddings=rope) # GQA,q、k 在内部做 RoPE
x = residual + x # 残差
residual = x
x = self.post_attention_layernorm(x) # RMSNorm
x = self.mlp(x) # SwiGLU:down(silu(gate(x)) * up(x))
x = residual + x # 残差对照 GPT-2 的 GPT2Block,结构是一样的两段“归一化 → 子层 → 残差相加”,差别全在 input_layernorm、self_attn、mlp 这几个零件内部。所以拿到一个新模型时,先确认骨架是不是这两段式,再逐个检查零件换成了什么,就能很快说出它和 Llama 的区别,第 18 篇会把这个方法展开成读配置和源码的步骤。
从 2017 年的翻译模型出发,编码器、编码器-解码器、解码器三条路线的区别在于注意力的可见范围:双向适合读懂已知文本,因果适合逐字生成。decoder-only 成为大模型主流,有“只靠预训练做零样本任务时最好”的对照实验支持,也有训练信号密、一条序列装下所有任务、KV Cache 可以复用这些机制上的理由,还有生态的路径依赖;编码器和编码器-解码器在检索、分类和语音识别里仍有位置。从 GPT-2 到 Llama,骨架不变,Pre-Norm 之外的位置、归一化、FFN、bias、KV 头和 dropout 都换了一遍。下一篇看在这个骨架上最大的一次改动:混合专家 MoE,它让总参数 6710 亿的模型每个 token 只用 370 亿。
