【模型架构篇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的家族树

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、大模型基础

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐