【模型架构篇04】Transformer架构精讲:Encoder-Decoder全拆解
·
【模型架构篇04】Transformer架构精讲:Encoder-Decoder全拆解
前言:2017年Google那篇"Attention Is All You Need"不仅是一篇论文,它彻底改写了AI的历史。此后诞生的GPT、BERT、T5、LLaMA、DeepSeek,都是同一个架构的不同"子孙"。为什么有的模型只用Decoder,有的只用Encoder?Encoder-Decoder原版架构在今天还有用吗?Decoder-only为什么成了2026年的统治地位?本文从原始Transformer逐层拆解,对比三大架构流派的设计哲学与选择逻辑。
📋 目录
- 一、Transformer的家族树
- 二、原始Transformer:Encoder-Decoder
- 三、Encoder-only:BERT与双向理解
- 四、Decoder-only:GPT与自回归生成
- 五、Encoder-Decoder的复活:T5与多模态
- 六、架构对比与选型指南
- 七、RMSNorm与层归一化演进
- 八、残差连接与Pre-Norm/Post-Norm
- 九、GPT与BERT代码级对比
一、Transformer的家族树
1.1 架构图谱
所有主流大模型,都是Transformer的变体:
2017: Transformer(原始)
├──────────────────┬───────────────────┐
│ │ │
Encoder-Decoder Encoder-only Decoder-only
│ │ │
├─ T5 (2019) ├─ BERT (2018) ├─ GPT-1 (2018)
├─ BART (2020) ├─ RoBERTa (2019) ├─ GPT-2 (2019)
└─ M2M-100 ├─ ALBERT (2019) ├─ GPT-3 (2020)
├─ ELECTRA (2020) ├─ GPT-4 (2023)
└─ DeBERTa ├─ LLaMA (2023)
│ (前缀解码器变体)
├─ Mistral (2023)
├─ Qwen (2024-2026)
├─ DeepSeek (2024-2026)
└─ Claude Opus系列(2025-2026)
2026年格局:
Decoder-only → 统治地位(几乎所有新模型都走这条路)
Encoder-only → 小众但不可替代(理解/分类任务)
Encoder-Decoder → 几乎消亡(T5的后继者很少)
1.2 为什么Decoder-only成了2026年的统治架构?
Decoder-only能统治的原因可以归纳为以下四点:
1️⃣ 缩放效率(Scaling Efficiency)
实验发现:同样的算力预算下
Decoder-only的性能增长曲线最陡
Encoder-Decoder浪费了大量参数在"编码器"上
2️⃣ 生成即理解
Decoder-only生成时,中间层也在"理解"
不需要专门的编码器
"理解"是"生成"的副产品
3️⃣ 统一接口
所有任务 = 同一个生成范式
总结? → "总结:..."
翻译? → "翻译成英文:..."
问答? → "问题:...答案:"
不需要为不同任务设计不同的架构
4️⃣ 工程友好
推理时只需要运行一个模型(Decoder)
不需要先编码再解码
部署更简单,显存更省
2026年的状态:
几乎所有新发布的模型都是Decoder-only
Encoder-only(BERT)只有做向量Embedding时还常用
Encoder-Decoder几乎被淘汰
二、原始Transformer:Encoder-Decoder
2.1 整体架构
原始Transformer由两部分组成:
Encoder(编码器)← 左侧堆叠N个Block
Decoder(解码器)← 右侧堆叠N个Block
┌─────────────────────┐
│ Output │
│ (shifted right) │
└─────────┬───────────┘
│
┌─────────▼───────────┐
│ Decoder × N │
│ ┌───────────────┐ │
│ │ Masked MHA │ │ ← Causal Attention
│ │ Cross-Attn │ │ ← 看Encoder输出
│ │ FFN │ │
│ └───────────────┘ │
└─────────┬───────────┘
│
┌────────────┼────────────┐
│ │ │
┌──────▼─────┐ │ ┌──────▼─────┐
│ Encoder×N │ │ │ Encoder×N │
│ Self-Attn │ │ │ Self-Attn │
│ FFN │ │ │ FFN │
└──────┬─────┘ │ └──────┬─────┘
│ │ │
┌──────▼─────┐ │ ┌──────▼─────┐
│ Input │ │ │ Input │
│ "I love AI"│ │ │ "我爱AI" │
└────────────┘ │ └────────────┘
│
(机器翻译场景)
Encoder:双向理解输入文本(可以看到上下文两侧)
Decoder:自回归生成输出文本(只能看到左侧)
这是为"序列到序列"任务设计的
输入:一种语言的句子
输出:另一种语言的翻译
其他Encoder-Decoder做不了的?其实也能做
但后来大家发现纯Decoder更简单
2.2 Encoder Block详解
一个Encoder Block包含两个子层:
输入 → Multi-Head Self-Attention → Add&Norm → FFN → Add&Norm → 输出
子层1:Self-Attention(自我注意力)
每个位置关注所有位置(包括自己)
Q、K、V都来自同一个序列
可以看到"左右两边"的信息
例子:在"我今天很开心"中
"开"可以看到"我"、"今天"、"很"、"开心"
→ 双向信息流
子层2:FFN(前馈网络)
每个位置独立经过一个全连接层
捕捉位置内部的非线性关系
Add&Norm = 残差连接 + 层归一化
残差:output = LayerNorm(x + Sublayer(x))
解决深层网络退化问题
2.3 Decoder Block详解
Decoder Block包含三个子层(比Encoder多一个):
输入 → Masked Self-Attention → Add&Norm →
Cross-Attention → Add&Norm → FFN → Add&Norm → 输出
子层1:Masked Self-Attention(掩码自注意力)
和Encoder的Self-Attention一样
但加了Causal Mask
每个位置只能看到自己和左边的位置
→ 自回归:预测下一个词时不能"偷看"答案
子层2:Cross-Attention(交叉注意力)← 这是Decoder独有的
Q来自Decoder的当前层
K、V来自Encoder的最后一层输出
相当于"在生成时不断回头看输入"
直译任务中:
Decoder当前已生成"我 爱"
回头看Encoder的"I love AI"
确定下一步该输出什么
子层3:FFN(前馈网络)
和Encoder的FFN一样
2.4 为什么原始Transformer要用Encoder-Decoder?
历史原因:Transformer是为机器翻译设计的
机器翻译 = 序列到序列任务
原文:"I love AI" → 需要完整理解后再翻译
译文:"我爱AI" → 逐词自回归生成
Encoder负责"理解"
读完整个句子
建立全局的语义表示
→ 输出一个"语义向量"
Decoder负责"生成"
基于Encoder的语义表示
逐词生成翻译
→ 每一步都可以"回头看"原文
这种设计在翻译任务上效果极佳
但后来发现:
对于大多数"单语言任务"
Encoder不是必须的
只用Decoder也能完成任务
而且更简单、更易扩展
三、Encoder-only:BERT与双向理解
3.1 BERT的核心创新
BERT(Bidirectional Encoder Representations from Transformers,2018)
只使用Transformer的Encoder部分
关键创新:双向理解 + 掩码语言模型
双向理解(和Transformer Encoder一样):
每个位置可以看到所有其他位置
不管是左边还是右边
"我今天去[MASK]了"
"去"和"了"都在可见范围内
→ BERT能准确预测被掩码的词
掩码语言模型(MLM,Masked Language Model):
随机遮盖15%的token
让模型预测被遮盖的词
→ 相当于"完形填空"
"I [MASK] AI" → 预测 [MASK] = "love"
下一个句子预测(NSP):
判断两个句子是否连续
BERT: [CLS] 第一句 [SEP] 第二句 [SEP]
预测:这两句是否相邻?
→ 学习句子间的关系
3.2 BERT的训练方式
BERT训练两阶段:
阶段1:预训练
任务A:掩码语言模型(MLM)
数据:无标注文本(维基百科)
目标:预测被mask的词
方法:
15%的token被选中做mask
其中:80%替换为[MASK],10%替换为随机词,10%保持不变
为什么不全用[MASK]?
因为推理时没有[MASK]标记
模型需要适应"正常输入"
任务B:下一个句子预测(NSP)
数据:成对句子
50%是连续句子,50%是随机拼接
目标:判断是否连续
预训练数据量:
BERT-base:约3.3B tokens(英文维基百科+BooksCorpus)
BERT-large:同数据量
阶段2:微调
根据下游任务,在BERT上接不同的输出头:
分类任务(情感分析):
[CLS] 这部电影太棒了 [SEP]
→ BERT输出[CLS]向量 → 线性分类器 → 正面/负面
命名实体识别(NER):
每个位置输出标签
张/三 → B-PER / I-PER
问答任务(SQuAD):
输出答案的起始位置和结束位置
3.3 BERT vs GPT的架构差异
BERT (Encoder-only) vs GPT (Decoder-only):
┌──────────────┬──────────────────┬──────────────────┐
│ 维度 │ BERT │ GPT │
├──────────────┼──────────────────┼──────────────────┤
│ 架构 │ Encoder-only │ Decoder-only │
│ 注意力 │ 双向(能看到两侧)│ 单向(Causal Mask)│
│ 训练目标 │ MLM(完形填空) │ 自回归(预测下一个)│
│ 擅长任务 │ 理解 / 分类 │ 生成 / 对话 │
│ 编码方式 │ 所有位置同时 │ 从左到右逐token │
│ 微调 │ 必须微调 │ 零样本/Few-shot │
│ 典型大小 │ 110M-340M │ 125M-1.8T │
│ 规模扩展 │ 较差 │ 优秀(Scaling Law)│
└──────────────┴──────────────────┴──────────────────┘
为什么BERT不能"变大"?
2018-2019年,BERT是SOTA
但后面发现:BERT的预训练方式(MLM)很难扩展
因为它每次只预测15%的token
只有15%的训练信号 → 需要更多数据和计算量
而GPT预测100%的token → 训练效率更高
这也是为什么Scaling Law在GPT上成立,在BERT上不太成立
3.4 BERT的2026年现状
2026年了,BERT还活着吗?
✅ BERT仍然在用的场景:
文本分类(情感分析、垃圾检测)
命名实体识别(NER)
关系抽取
句子对匹配
作为Embedding模型(Sentence-BERT)
→ 凡是"需要双向理解" + "不需要生成"的任务
→ BERT或其变体(如RoBERTa)仍然是首选
❌ BERT不擅长的场景:
文本生成
对话
代码生成
翻译
→ 生成任务的天下已经是GPT了
BERT的当代替代者:
✅ 理解任务:BERT/RoBERTa依然足够好
✅ 分类任务:可以换更小的蒸馏模型(DistilBERT)
✅ Embedding:BGE/MTEB等专用Embedding模型更强
❌ 生成任务:必须用Decoder-only模型
一句话总结:
如果你只需要"理解"文本,BERT仍然是好选择
如果你需要任何形式的"生成",请用Decoder-only
四、Decoder-only:GPT与自回归生成
4.1 Decoder-only的核心思想
Decoder-only = 只保留Transformer的Decoder部分
去掉Encoder
去掉Cross-Attention
只保留Masked Self-Attention + FFN
为什么去掉Encoder和Cross-Attention也能工作?
Decoder已经学会了"理解"
理解不需要专门的Encoder
生成的过程中自然包含了理解
GPT的架构(超简洁):
输入序列 → Token Embedding + Position Embedding
→ Decoder Block × N(Masked Self-Attention + FFN)
→ 输出下一个token的概率分布
没有Encoder
没有Cross-Attention
就是单纯的"从左到右预测下一个词"
4.2 自回归生成过程
自回归生成 = 用自己生成的结果作为下一次的输入
Step 1:输入"中国的首都是"
Step 2:模型预测下一个词 → "北京"
Step 3:输入变成"中国的首都是北京"
Step 4:预测下一个词 → "。"(句号)
Step 5:输入变成"中国的首都是北京。"
Step 6:预测下一个词 → [EOS](结束)
以上每一步中,模型都使用了Causal Mask
"北京"看不到"。" → 保证了自回归特性
计算量分析:
生成N个token,需要N次前向传播
每次前向传播的计算量递增(因为上下文越来越长)
但KV Cache让每次增量计算 ≈ 常数时间
→ 实际生成速度 ≈ 匀速
4.3 GPT系列架构演进
GPT系列的架构演进 = 大模型发展的缩影:
┌──────────┬────────┬────────┬───────────┬────────────────┐
│ 模型 │ 年份 │ 参数量 │ 架构特点 │ 训练数据 │
├──────────┼────────┼────────┼───────────┼────────────────┤
│ GPT-1 │ 2018 │ 117M │ 12层Decoder│ BookCorpus │
│ │ │ │ 768d │ │
│ GPT-2 │ 2019 │ 1.5B │ 48层Decoder│ WebText(8M页) │
│ │ │ │ 1600d │ │
│ GPT-3 │ 2020 │ 175B │ 96层 │ CommonCrawl │
│ │ │ │ 12288d │ 过滤后570GB │
│ GPT-4 │ 2023 │ 1.8T(估)│ MoE架构 │ 未公开 │
│ │ │ │ 8个专家 │ │
│ GPT-4o │ 2024 │ 未公开 │ 多模态原生 │ 未公开 │
│ GPT-4.1 │ 2025 │ 未公开 │ 1M上下文 │ 未公开 │
│ GPT-5.2 │ 2026 │ 未公开 │ 最强对话 │ 未公开 │
└──────────┴────────┴────────┴───────────┴────────────────┘
架构演进的关键变化:
GPT-1→GPT-2:层数从12→48,首次展示"规模提升带来质变"
GPT-2→GPT-3:从1.5B→175B,Scaling Law的震撼发现
GPT-3→GPT-4:从稠密→MoE,用更少成本扩展参数
GPT-4→GPT-4.1:上下文窗口从32K→1M
GPT-4.1→GPT-5系列:推理能力显著增强
不变的核心:
所有GPT模型都是Decoder-only
所有GPT模型都用Causal Mask
所有GPT模型都做自回归预测
→ 架构没有根本性变化,变大+变好数据是关键
4.4 LLaMA架构:Decoder-only的现代标杆
Meta的LLaMA系列是目前开源社区最重要的Decoder-only架构:
LLaMA原始架构(2023):
Decoder-only + Pre-Norm(RMSNorm)+ SwiGLU激活函数 + RoPE
相对于原始GPT的改进:
1️⃣ RMSNorm(替代LayerNorm)
LayerNorm:计算均值和方差 → 需要两次统计
RMSNorm:只计算RMS(均方根) → 快5-10%
效果几乎一样,速度更快
2️⃣ SwiGLU(替代ReLU/GELU)
原始Transformer FFN:ReLU(xW)W₂
SwiGLU FFN:Swish(xW) ⊙ (xV)W₂
⊙ = 元素级相乘 → 增加非线性表达能力
代价:多了一个权重矩阵V,参数增大约50%
但效果更好,几乎所有2023年后的模型都用SwiGLU
3️⃣ RoPE(替代绝对位置编码)
位置编码在【AI基础篇06】中讲过
旋转编码 → 相对位置信息
已取代Sinusoidal成为主流
4️⃣ Grouped Query Attention(GQA)
LLaMA 2-70B使用GQA
推理时KV Cache减少到1/8
长上下文部署更可行
LLaMA 3(2024)的进一步改进:
更大的词汇表(128K tokens)
更多的训练数据(15T tokens)
更强的数据过滤(质量 >> 数量)
DPO对齐替代RLHF
Qwen2.5(2025-2026)的改进:
在LLaMA架构基础上优化
更好的中文分词器
Sparse Attention支持长上下文
4.5 2026年Decoder-only模型全景
2026年主流Decoder-only模型:
闭源模型:
Claude Opus 4.6/4.7:Anthropic,SWE-bench 80.9%
GPT-5.2/5.3 Codex:OpenAI,推理和代码最强
Gemini 3.1/3 Pro:Google,多模态原生
Kimi K2.6:月之暗面,长文本场景
开源模型:
LLaMA 4:Meta,最新开源旗舰
Qwen3-Max:阿里,中文最强(~91% C-Eval)
DeepSeek V4:深度求索,1.6T MoE,推理速度35倍↑
Mistral Large:Mistral AI,小模型性价比高
全部是Decoder-only!
没有例外
Decoder-only已统一大模型世界
五、Encoder-Decoder的复活:T5与多模态
5.1 T5:Encoder-Decoder的最后一搏
T5(Text-to-Text Transfer Transformer,2019):
Google提出,把"所有NLP任务都统一成文本到文本"
T5的核心思想:
分类任务:"这个电影是正面的还是负面的?"
→ 输入:"情感分类:这部电影太棒了"
→ 输出:"正面"
翻译任务:
→ 输入:"翻译成中文:I love AI"
→ 输出:"我爱AI"
摘要任务:
→ 输入:"总结:文章内容..."
→ 输出:"文章主要讨论了..."
T5的表现:
在所有NLP任务上达到了当时的SOTA
证明了"统一框架"的可行性
T5的架构选择:
坚持使用Encoder-Decoder
认为"理解+生成"需要两个不同的网络
但后来Decoder-only证明不需要
T5的影响:
启发了后来"所有任务统一到一个格式"的思想
给Decoder-only提供了重要参考
"前缀微调"(Prefix Tuning)等技术都受T5启发
2026年的T5:
几乎不再使用
被Decoder-only全面取代
它的"text-to-text"思想被继承
但架构被淘汰
5.2 多模态场景下的Encoder-Decoder
多模态模型是Encoder-Decoder的唯一"幸存领域":
多模态模型的典型架构:
Image Encoder(ViT)→ Cross-Attention → Text Decoder(LLaMA)
Encoder(图像) → 提取视觉特征
Cross-Attention → 让文本"看"到图像
Decoder(文本) → 生成描述
典型的多模态Encoder-Decoder模型:
LLaVA:CLIP视觉编码器 + LLaMA文本解码器
Qwen-VL:Qwen视觉编码器 + Qwen文本解码器
Gemini:原生多模态,非传统Encoder-Decoder
GPT-4o:原生多模态,架构未公开
注意:
这里的"Encoder"是视觉编码器,不是Transformer原文的Text Encoder
文本部分仍然是Decoder-only
只是额外加了一个"视觉Encoder"
5.3 架构选择的最终结论
三种架构的终极总结:
┌──────────────┬─────────┬─────────┬──────────────┐
│ 架构 │ 训练效率 │ 推理效率 │ 适用场景 │
├──────────────┼─────────┼─────────┼──────────────┤
│ Encoder-only │ 中 │ 高 │ 分类/理解 │
│ (BERT) │ (只学15%)│ │ NER/Embedding │
│ │ │ │ │
│ Decoder-only │ 高 │ 高 │ 生成/对话 │
│ (GPT/LLaMA) │ (学100%)│ │ 代码/翻译 │
│ │ │ │ ← 2026年主流 │
│ │ │ │ │
│ Encoder-Decoder│ 低 │ 低 │ 翻译/多模态 │
│ (T5/BART) │ (两倍参)│ (两步) │ ← 边缘化 │
└──────────────┴─────────┴─────────┴──────────────┘
2026年选型建议:
文本生成 → Decoder-only(唯一选择)
文本理解 → Encoder-only or Decoder-only
多模态 → 视觉Encoder + 文本Decoder
翻译 → Decoder-only(已被验证足够好)
对话 → Decoder-only(唯一选择)
六、架构对比与选型指南
6.1 整体对比
┌──────────────┬────────────┬───────────┬──────────────┐
│ 维度 │ Encoder- │ Decoder- │ Encoder- │
│ │ only(BERT) │ only(GPT) │ Decoder(T5) │
├──────────────┼────────────┼───────────┼──────────────┤
│ 参数量效率 │ 中 │ 高 │ 低 │
│ 训练效率 │ 低(MLM) │ 高(AR) │ 中 │
│ 推理效率 │ 高(并行) │ 中(自回归) │ 低(两步) │
│ 零样本能力 │ 弱 │ 强 │ 中 │
│ 微调适配 │ 必须 │ 可选 │ 可选 │
│ 长文本处理 │ 好(双向) │ 中(单向) │ 好(全局) │
│ 生成质量 │ 差 │ 好 │ 好 │
│ 上下文窗口 │ 小(通常512)│ 大(1M+) │ 中 │
│ 端侧部署 │ 适合 │ 不适合 │ 不适合 │
│ 2026年采用率 │ <5% │ >90% │ <5% │
└──────────────┴────────────┴───────────┴──────────────┘
6.2 参数量效率的重要性
这是Decoder-only胜出的最关键原因:
同样的训练预算,Decoder-only能获得更好的性能。
假设我们有1000张GPU×30天的训练预算:
Decoder-only方案:
全部参数用于Decoder
100%的计算量用于"自回归学习"
每次训练step,100%的token都产生梯度
→ 效率最大化
Encoder-Decoder方案:
50%参数在Encoder,50%在Decoder(T5-large)
Encoder的计算量大但只用做"理解"
Decoder的计算量用于"生成"
→ 效率打对折
效果对比如下:
同等预算下
Decoder-only的MMLU得分比Encoder-Decoder高5-10%
→ 这就是"效率差距"
如果你不是为了做机器翻译
Encoder-Decoder的"双向理解"优势并不值那50%的参数
七、RMSNorm与层归一化演进
7.1 LayerNorm到RMSNorm
LayerNorm(原始Transformer用):
对每一层的输入做归一化
LayerNorm(x) = (x - μ) / σ × γ + β
其中:
μ = mean(x) ← 计算均值
σ = std(x) ← 计算标准差
γ = 可学习缩放参数
β = 可学习偏移参数
需要计算均值和方差
两个统计量 → 额外计算量
RMSNorm(LLaMA用,2023):
RMSNorm(x) = x / RMS(x) × γ
其中:
RMS(x) = sqrt(mean(x²)) ← 只计算均方根
γ = 可学习缩放参数(不需要β)
RMSNorm(x)比LayerNorm约快5-10%
效果几乎一样(因为均值偏移对Transformer影响有限)
LLaMA使用RMSNorm的关键原因:
70B模型 × 80层 × 每层都做Norm
算力差异在千卡训练时被放大
省掉5%的计算量 = 节省数万美元
7.2 Pre-Norm vs Post-Norm
这是训练稳定性最重要的设计选择之一:
Post-Norm(原始Transformer用):
output = LayerNorm(x + Sublayer(x))
先加残差,再归一化
残差连接在"归一化之前"
问题:深层网络(>12层)训练不稳定
梯度容易爆炸/消失
不适合大模型
Pre-Norm(LLaMA/所有现代大模型用):
output = x + Sublayer(LayerNorm(x))
先归一化,再加残差
残差连接在"归一化之后"
优势:深层网络(>100层)训练稳定
梯度可以直接通过残差回传
适合大模型
Pre-Norm vs Post-Norm 在大模型中的效果:
Post-Norm:12层可以,24层勉强,96层不可能
Pre-Norm:100层轻松,1000层理论上也行
所有现代大模型都用Pre-Norm!
这是"大模型能堆到百层"的关键
八、残差连接与Pre-Norm/Post-Norm
8.1 残差连接为什么重要
没有残差连接:梯度消失的噩梦
Layer 1 → Layer 2 → ... → Layer 96
梯度从第96层传到第1层
每经过一层,梯度乘以该层的导数
导数通常 < 1(特别是Sigmoid/ReLU的区域)
96次乘法后 → 梯度≈0 → 前面的层学不到东西
有残差连接:梯度的高速通道
output = x + Sublayer(x)
Layer 1 → + → Layer 2 → + → ... → Layer 96 → +
↑ ↑ ↑
直接通路 直接通路 直接通路
梯度可以通过残差直连通道从第96层直达第1层
不需要经过96次导数乘法
→ 深层网络训练可行
8.2 Pre-Norm vs Post-Norm可视化
Post-Norm架构(原始Transformer):
x
│
▼
Sublayer(Attention/FFN)
│
▼
x + Sublayer(x) ← 残差连接
│
▼
LayerNorm(...) ← 后归一化
│
▼
输出(传给下一层)
Pre-Norm架构(LLaMA/现代模型):
x
│
├──────────→ (残差直连)
│
▼
LayerNorm(x) ← 先归一化
│
▼
Sublayer(...) ← 子层
│
▼
x + Sublayer(LN(x)) ← 残差连接在最后
│
▼
输出(传给下一层)
为什么Pre-Norm更好:
残差路径上的数据没有经过任何变换
梯度可以畅通无阻地回传
而Post-Norm中,残差数据需要经过LayerNorm
LayerNorm的求导引入了额外的数值问题
8.3 2026年的标准Transformer Block
2026年现代大模型的一个标准Decoder Block:
def standard_decoder_block(x, attention, ffn):
# 1. 注意力层(Pre-Norm)
residual = x
x = RMSNorm(x)
x = attention(x) # Self-Attention, GQA
x = residual + x
# 2. FFN层(Pre-Norm)
residual = x
x = RMSNorm(x)
x = ffn(x) # SwiGLU FFN
x = residual + x
return x
这就是LLaMA、Qwen、DeepSeek等所有现代模型的"基本单元"
结构极其简洁
每一层都是"归一化→子层→残差连接"
堆叠N次即可
九、GPT与BERT代码级对比
9.1 简化版GPT实现
import torch
import torch.nn as nn
import torch.nn.functional as F
class CausalSelfAttention(nn.Module):
"""因果自注意力(GPT用)"""
def __init__(self, d_model, n_heads, dropout=0.1):
super().__init__()
assert d_model % n_heads == 0
self.d_model = d_model
self.n_heads = n_heads
self.d_k = d_model // n_heads
self.W_Q = nn.Linear(d_model, d_model)
self.W_K = nn.Linear(d_model, d_model)
self.W_V = nn.Linear(d_model, d_model)
self.W_O = nn.Linear(d_model, d_model)
self.dropout = nn.Dropout(dropout)
def forward(self, x, mask=None):
B, L, D = x.shape
# 投影 + 拆多头
Q = self.W_Q(x).view(B, L, self.n_heads, self.d_k).transpose(1, 2)
K = self.W_K(x).view(B, L, self.n_heads, self.d_k).transpose(1, 2)
V = self.W_V(x).view(B, L, self.n_heads, self.d_k).transpose(1, 2)
# 注意力分数
scores = torch.matmul(Q, K.transpose(-2, -1)) / (self.d_k ** 0.5)
# Causal Mask(GPT的关键!)
if mask is None:
mask = torch.triu(
torch.ones(L, L, device=x.device) * float('-inf'),
diagonal=1
)
scores = scores + mask
# Softmax + 加权
attn = F.softmax(scores, dim=-1)
attn = self.dropout(attn)
output = torch.matmul(attn, V)
# 合并头
output = output.transpose(1, 2).contiguous().view(B, L, D)
output = self.W_O(output)
return output
class GPTBlock(nn.Module):
"""GPT的一个Decoder Block"""
def __init__(self, d_model, n_heads, d_ff, dropout=0.1):
super().__init__()
# Pre-Norm
self.norm1 = nn.LayerNorm(d_model)
self.attn = CausalSelfAttention(d_model, n_heads, dropout)
self.norm2 = nn.LayerNorm(d_model)
self.ffn = nn.Sequential(
nn.Linear(d_model, d_ff),
nn.GELU(),
nn.Linear(d_ff, d_model),
nn.Dropout(dropout)
)
def forward(self, x, mask=None):
# Pre-Norm架构
x = x + self.attn(self.norm1(x), mask) # 先norm再attention,再加残差
x = x + self.ffn(self.norm2(x)) # 先norm再ffn,再加残差
return x
class SimplifiedGPT(nn.Module):
"""完整GPT模型"""
def __init__(self, vocab_size, d_model=768, n_heads=12,
n_layers=12, d_ff=3072, max_seq_len=512):
super().__init__()
self.token_embedding = nn.Embedding(vocab_size, d_model)
self.position_embedding = nn.Embedding(max_seq_len, d_model)
self.blocks = nn.ModuleList([
GPTBlock(d_model, n_heads, d_ff)
for _ in range(n_layers)
])
self.norm = nn.LayerNorm(d_model)
self.lm_head = nn.Linear(d_model, vocab_size, bias=False)
# 预计算Causal Mask
self.register_buffer(
"causal_mask",
torch.triu(
torch.ones(max_seq_len, max_seq_len) * float('-inf'),
diagonal=1
)
)
def forward(self, input_ids):
B, L = input_ids.shape
# Embedding
token_emb = self.token_embedding(input_ids)
pos_emb = self.position_embedding(
torch.arange(L, device=input_ids.device)
)
x = token_emb + pos_emb
# Decoder Blocks
mask = self.causal_mask[:L, :L]
for block in self.blocks:
x = block(x, mask)
x = self.norm(x)
logits = self.lm_head(x)
return logits
def generate(self, input_ids, max_new_tokens=50, temperature=1.0):
"""自回归生成"""
for _ in range(max_new_tokens):
logits = self(input_ids)
next_logits = logits[:, -1, :] / temperature
next_token = torch.multinomial(
F.softmax(next_logits, dim=-1), num_samples=1
)
input_ids = torch.cat([input_ids, next_token], dim=-1)
return input_ids
9.2 简化版BERT实现
class BERTBlock(nn.Module):
"""BERT的一个Encoder Block"""
def __init__(self, d_model, n_heads, d_ff, dropout=0.1):
super().__init__()
# Pre-Norm(现代BERT也用Pre-Norm)
self.norm1 = nn.LayerNorm(d_model)
# BERT用标准Self-Attention(不加Causal Mask)
self.attn = nn.MultiheadAttention(d_model, n_heads, dropout, batch_first=True)
self.norm2 = nn.LayerNorm(d_model)
self.ffn = nn.Sequential(
nn.Linear(d_model, d_ff),
nn.GELU(),
nn.Linear(d_ff, d_model),
nn.Dropout(dropout)
)
def forward(self, x, attention_mask=None):
# 标准双向注意力(没有Causal Mask!)
attn_output, _ = self.attn(
self.norm1(x), self.norm1(x), self.norm1(x),
key_padding_mask=attention_mask
)
x = x + attn_output
x = x + self.ffn(self.norm2(x))
return x
class SimplifiedBERT(nn.Module):
"""完整BERT模型(MLM预训练)"""
def __init__(self, vocab_size, d_model=768, n_heads=12,
n_layers=12, d_ff=3072, max_seq_len=512):
super().__init__()
self.token_embedding = nn.Embedding(vocab_size, d_model)
self.position_embedding = nn.Embedding(max_seq_len, d_model)
self.segment_embedding = nn.Embedding(2, d_model) # BERT的句子段
self.blocks = nn.ModuleList([
BERTBlock(d_model, n_heads, d_ff)
for _ in range(n_layers)
])
self.norm = nn.LayerNorm(d_model)
self.mlm_head = nn.Sequential(
nn.Linear(d_model, d_model),
nn.GELU(),
nn.LayerNorm(d_model),
nn.Linear(d_model, vocab_size)
)
# [CLS]输出用于分类
self.classifier = nn.Linear(d_model, 2) # 二分类(NSP)
def forward(self, input_ids, token_type_ids=None, attention_mask=None):
B, L = input_ids.shape
if token_type_ids is None:
token_type_ids = torch.zeros(B, L, dtype=torch.long)
# Embedding
token_emb = self.token_embedding(input_ids)
pos_emb = self.position_embedding(
torch.arange(L, device=input_ids.device)
)
seg_emb = self.segment_embedding(token_type_ids)
x = token_emb + pos_emb + seg_emb
# Encoder Blocks(双向注意力)
for block in self.blocks:
x = block(x, attention_mask)
x = self.norm(x)
mlm_logits = self.mlm_head(x) # [B, L, V] → MLM预测
cls_logits = self.classifier(x[:, 0]) # [B, 2] → NSP预测
return mlm_logits, cls_logits
# GPT vs BERT 代码差异对比:
print("GPT和BERT的架构差异(代码视角):")
print("1️⃣ Attention Mask")
print(" GPT: Casual Mask(上三角-∞)→ 只能看左边")
print(" BERT: 无Mask(或Padding Mask)→ 可以看两边")
print()
print("2️⃣ 输出头")
print(" GPT: 语言模型头(预测下一个token)")
print(" BERT: MLM头(预测[MASK]) + NSP头(分类)")
print()
print("3️⃣ 生成")
print(" GPT: 自回归(逐token生成)")
print(" BERT: 不能直接生成(需要额外Decoder或MLM)")
print()
print("4️⃣ 位置编码")
print(" GPT: 可学习位置编码")
print(" BERT: 可学习位置编码")
9.3 模型大小与配置速查
# 各模型的架构配置:
model_configs = {
# ===== Encoder-only (BERT家族) =====
"BERT-base": {
"architecture": "Encoder-only",
"params": "110M",
"layers": 12,
"d_model": 768,
"n_heads": 12,
"d_ff": 3072,
"max_seq_len": 512,
"activation": "GELU",
"norm": "Post-Norm (原始)/Pre-Norm (后续版本)",
"attention": "双向全连接"
},
"BERT-large": {
"architecture": "Encoder-only",
"params": "340M",
"layers": 24,
"d_model": 1024,
"n_heads": 16,
"d_ff": 4096,
"max_seq_len": 512,
"activation": "GELU",
"norm": "Post-Norm → Pre-Norm",
"attention": "双向全连接"
},
# ===== Decoder-only (GPT/LLaMA家族) =====
"GPT-3": {
"architecture": "Decoder-only",
"params": "175B",
"layers": 96,
"d_model": 12288,
"n_heads": 96,
"d_ff": 49152,
"max_seq_len": 2048,
"activation": "GELU",
"norm": "Pre-Norm (LayerNorm)",
"attention": "Causal, MHA"
},
"LLaMA-3-70B": {
"architecture": "Decoder-only",
"params": "70B",
"layers": 80,
"d_model": 8192,
"n_heads": 64,
"d_ff": 28672,
"max_seq_len": 8192,
"activation": "SwiGLU",
"norm": "Pre-Norm (RMSNorm)",
"attention": "Causal, GQA (8组)"
},
"DeepSeek-V3": {
"architecture": "Decoder-only + MoE",
"params": "671B (37B激活)",
"layers": 67,
"d_model": 7168,
"n_heads": 128,
"d_ff": "MoE: 256专家×2048",
"max_seq_len": 128000,
"activation": "SwiGLU",
"norm": "Pre-Norm (RMSNorm)",
"attention": "Causal, MLA"
},
"Qwen3-Max": {
"architecture": "Decoder-only",
"params": "未公开 (大几千亿)",
"layers": "未公开",
"d_model": "未公开",
"n_heads": "未公开",
"d_ff": "未公开",
"max_seq_len": 262144,
"activation": "SwiGLU",
"norm": "Pre-Norm (RMSNorm)",
"attention": "Causal, GQA"
},
# ===== Encoder-Decoder (T5) =====
"T5-11B": {
"architecture": "Encoder-Decoder",
"params": "11B",
"layers": "Enc:24, Dec:24",
"d_model": 1024,
"n_heads": 128, # 注意:T5的head定义不同
"d_ff": 65536,
"max_seq_len": 512,
"activation": "ReLU (门控)",
"norm": "Pre-Norm (RMSNorm)",
"attention": "Enc:双向, Dec:Causal+Cross"
}
}
print("各模型架构配置:")
for name, config in model_configs.items():
print(f"\n{name}:")
for k, v in config.items():
print(f" {k}: {v}")
📌 总结
Transformer架构核心要点:
1️⃣ 三种架构流派
Encoder-only(BERT):双向理解,适合分类/Embedding
Decoder-only(GPT):自回归生成,2026年统治地位
Encoder-Decoder(T5):两阶段处理,几乎被淘汰
2️⃣ Decoder-only为什么赢了?
更高的训练效率(100% token贡献梯度)
更简单的工程实现(一个模型)
更强的缩放能力(Scaling Law已验证)
3️⃣ 原始Transformer的设计(2017)
Encoder:N个Block,双向Self-Attention
Decoder:N个Block,Masked Self-Attention + Cross-Attention
为机器翻译设计
4️⃣ 现代架构的进化
LayerNorm → RMSNorm(快5-10%)
Post-Norm → Pre-Norm(深层训练稳定)
ReLU/GELU → SwiGLU(表达更强)
MHA → GQA/MoE/MLA(效率更高)
绝对位置编码 → RoPE(相对位置)
5️⃣ 2026年的标准配置
Decoder-only + Pre-Norm(RMSNorm) + SwiGLU + RoPE + GQA
LLaMA、Qwen、DeepSeek、Mistral皆如此
没有例外
🔗 延伸阅读
- 【AI基础篇05】注意力机制:Self-Attention详解
- 【AI基础篇06】位置编码
- 【模型架构篇01】大模型部署:从vLLM到ollama
- 【模型架构篇03】MoE混合专家模型详解
- 【AI基础篇02】从Transformer到GPT:生成式AI的演进史
觉得有帮助?点赞收藏!下一篇我们讲Tokenizer实现与词汇表设计——为什么GPT-4词汇表10万而LLaMA只有3.2万?不同Tokenizer对模型能力的影响有多大? 🚀
标签:人工智能、Transformer、BERT、GPT、架构设计、Encoder-Decoder、Decoder-only、大模型基础
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)