Transformer 架构详解(未完善)

🏷️ 关键词

如果你只能读一篇关于 Transformer 的文章,希望是这一篇。本文从最基础的问题出发,一步步推导出 Transformer 的每一个核心设计,保证看完能理解为什么它如此强大。


一、Transformer 要解决什么问题?

Transformer 出现之前(2017 年以前),主流的序列模型是 RNN(循环神经网络)LSTM。它们有什么问题?

RNN 的两大痛点

痛点 1:长距离依赖丢失

RNN 处理句子 "我在北京出生,后来去了上海读书,现在……喜欢这里"

  "我" → "在" → "北京" → "出生" → …… → "上海" → "读书" → …… → "这里"
   ↑信息  一步步  传递   逐层   衰减   到   这里   已经   模糊  不清

  问题:"这里" 指的是 "北京" 还是 "上海"?
  → 信息经过太多步,早就模糊了

这部分可以理解为并没有记忆,或者说想金鱼的一样很短 痛点 2:不能并行计算

RNN 必须按顺序计算:
  第 1 步: "我" → 隐藏状态 h₁
  第 2 步: "在" → 隐藏状态 h₂(依赖 h₁)
  第 3 步: "北京" → 隐藏状态 h₃(依赖 h₂)
  ...
  
  第 N 步必须等前 N-1 步算完 → 不能并行 → 训练慢

Transformer 的核心洞察

不再逐个传递信息,而是让每个词直接”看”到所有其他词:

传统 RNN:     我 → 在 → 北京 → …… → 喜欢 → 这里
                                 信息逐层衰减↗

Transformer:  我 ──────┐
              在 ──────┤
              北京 ────┤──→ 直接关注所有位置
              ……  ────┤
              喜欢 ────┤
              这里 ────┘
              
              每个词一步就连接到其他所有词!⚡

二、Attention:注意力机制

直观理解

想象你在读这句话:”那个穿红色衣服的女孩跑过去捡起了她的书包“。

当看到”“时,你会自然地关注到”女孩“——因为”她”指的就是”女孩”。这就是注意力:在处理某个位置时,知道应该关注输入的哪些其他位置

经典例子:”it” 指的是什么?

来看 Jay Alammar 的经典例子:

“The animal didn’t cross the street because it was too tired” (那只动物没有过马路,因为它太累了)

问题:这里的 “it” 指的是 “animal” 还是 “street”?

人类一眼就知道是 “animal”(动物太累了),但机器需要学会这种指代关系。这正是 Attention 要解决的问题:

当模型处理 "it" 这个位置时:

  注意力权重分布(越高 = 越关注):
  
  The    animal    didn't    cross    the    street    because    it    was    tired
  0.01    0.72     0.01      0.01    0.01    0.08      0.01      —    0.01   0.14
  
  ↑ "it" 把 72% 的注意力放在 "animal" 上
  ↑ 8% 关注 "street"(也有可能是街太累了?)
  ↑ 14% 关注 "tired"("累了"的主体是 animal)
  
  → 模型正确理解了 "it" → "animal" 的指代关系 ✅

这种注意力可视化是 Transformer 可解释性的重要工具——我们能看到模型在关注什么。

数学形式:QKV 三要素

Attention 的核心是三个矩阵:Q(Query)K(Key)V(Value)

# 用生活类比理解 QKV
Q = Query   = "你在搜索引擎里输入的问题"
K = Key     = "网页的标题标签"  
V = Value   = "网页的实际内容"

完整的计算分三步:

第 1 步:匹配(Q × K)

每个 Query 和所有 Key 计算相似度得分:

  Q₁ → 和 K₁ 的相似度: 0.9  ← "女孩" 和 "女孩" 高度匹配
  Q₁ → 和 K₂ 的相似度: 0.1  ← "女孩" 和 "跑" 不太相关
  Q₁ → 和 K₃ 的相似度: 0.3  ← "女孩" 和 "书包" 有点相关

第 2 步:缩放 + 归一化(Softmax)

得分除以 √d_k 防止数值过大,然后通过 Softmax 变成概率分布(和为 1):

  原始得分: [0.9, 0.1, 0.3]
  缩放后:  [0.6, 0.07, 0.2]   ← 除以 √d_k,让梯度更稳定
  Softmax: [0.5, 0.2, 0.3]    ← 变成概率,和为 1

第 3 步:加权求和(× V)

用这些概率加权聚合所有 Value:

  输出 = 0.5 × V₁ + 0.2 × V₂ + 0.3 × V₃
  
  最终结果 ≈ 主要包含 V₁(女孩)的信息 + 少量 V₂(跑)+ 一些 V₃(书包)

完整公式

这就是著名的 Scaled Dot-Product Attention

\[\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right) V\]

其中 $d_k$ 是 Key 的维度。除以 $\sqrt{d_k}$ 的原因是:当维度很高时,点积的结果会变得很大,Softmax 的梯度会变得非常小(梯度消失),缩放后解决了这个问题。

步骤分解图

输入序列: [x₁, x₂, x₃, x₄]

Step 1: 计算 Q、K、V
  x₁ → Q₁, K₁, V₁
  x₂ → Q₂, K₂, V₂
  x₃ → Q₃, K₃, V₃
  x₄ → Q₄, K₄, V₄

Step 2: 计算注意力分数矩阵
       K₁   K₂   K₃   K₄
  Q₁  [0.9, 0.1, 0.3, 0.2] ← x₁ 对各个位置的关注度
  Q₂  [0.2, 0.8, 0.1, 0.4]
  Q₃  [0.3, 0.2, 0.7, 0.1]
  Q₄  [0.1, 0.3, 0.2, 0.9]

Step 3: Softmax 归一化(每行和为 1)

Step 4: 每行 × V 得到输出
  输出₁ = 0.9V₁ × 0.1V₂ × 0.3V₃ × 0.2V₄(加权求和)

代码实现

import torch
import torch.nn.functional as F

def scaled_dot_product_attention(Q, K, V):
    """
    Q: (batch, heads, seq_len, d_k) — 查询
    K: (batch, heads, seq_len, d_k) — 键
    V: (batch, heads, seq_len, d_v) — 值
    """
    d_k = Q.size(-1)
    
    # Step 1: Q × K^T 计算相似度
    scores = torch.matmul(Q, K.transpose(-2, -1))
    
    # Step 2: 缩放防止梯度消失
    scores = scores / (d_k ** 0.5)
    
    # Step 3: Softmax 归一化
    weights = F.softmax(scores, dim=-1)
    
    # Step 4: 加权求和
    output = torch.matmul(weights, V)
    
    return output, weights  # weights 可以用于可视化

三、Multi-Head Attention:多头注意力

为什么需要”多”头?

单个注意力只能学到一种关注模式。但语言很复杂,需要从多个角度同时理解:

句子: "那只猫追到了老鼠,它跑得很快"

头 1 — 语法关系:    "猫" → "追" → "老鼠"     关注主谓宾
头 2 — 指代关系:    "猫" ← "它"              关注代词指代
头 3 — 语义关联:    "猫" ··· "老鼠"          关注语义关系
头 4 — 位置关系:    "追" → "到" → "了"       关注局部上下文

每个头学到的模式不同 → 拼接起来 → 信息更丰富

计算流程

输入 X
  │
  ├──→ 线形投影到 头 1: Q₁ = XW₁_Q, K₁ = XW₁_K, V₁ = XW₁_V → Attention₁
  ├──→ 线形投影到 头 2: Q₂ = XW₂_Q, K₂ = XW₂_K, V₂ = XW₂_V → Attention₂
  ├──→ 线形投影到 头 3: Q₃ = XW₃_Q, K₃ = XW₃_K, V₃ = XW₃_V → Attention₃
  └──→ 线形投影到 头 4: Q₄ = XW₄_Q, K₄ = XW₄_K, V₄ = XW₄_V → Attention₄
                                              │
                                              ▼
                                 拼接所有头的输出
                                        │
                                        ▼
                                  线形投影 → 最终输出

完整公式

\[\text{MultiHead}(Q, K, V) = \text{Concat}(\text{head}_1, \dots, \text{head}_h) W_O\]

其中每个头为:

\[\text{head}_i = \text{Attention}(XW_i^Q, XW_i^K, XW_i^V)\]

多头 vs 单头的效果

特性 单头注意力 多头注意力(8 头)
关注角度 1 种 8 种不同角度
参数量 较少 总参数量相同(每个头维度降低)
表达力 有限 强得多
可解释性 不同头可对应不同语言模式

关键设计细节:虽然头数增加了,但每个头的维度降低了(如果原来 512 维,8 头就是每个头 64 维),总计算量基本不变。


四、Positional Encoding:位置编码

为什么需要位置信息?

Attention 有一个固有问题:它是置换不变的(permutation invariant)。

不加位置编码时,以下两句话对 Attention 来说完全一样:

  "猫追老鼠"  → 每个词关注其他词的模式
  "老鼠追猫"  → 模式完全相同!因为词袋相同

这显然不对——"猫追老鼠" 和 "老鼠追猫" 意思完全相反!

Positional Encoding 给每个位置一个唯一的位置信号,让模型知道词的先后顺序。

正弦波位置编码

原论文使用一组正弦/余弦函数,每个维度有不同频率:

\[\begin{aligned} PE(pos, 2i) &= \sin\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right) \\[4pt] PE(pos, 2i+1) &= \cos\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right) \end{aligned}\]

直观理解

  • $pos$ 是位置(0, 1, 2, …)
  • $i$ 是维度(0, 1, 2, …, d/2)
  • 不同维度用不同频率:低维变化快(区分邻近位置),高维变化慢(编码远距离关系)
  • 这种设计让模型既知道绝对位置,也能推断相对距离
维度 0 (高频):  ~ ~ ~ ~ ~ ~ ~ ~   快速变化,区分相邻位置
维度 1:          ~  ~  ~  ~       中等频率
维度 2:           ~   ~   ~       更低频率
...
维度 d (低频):    ~    ~    ~     慢速变化,编码远距离关系

现代演进:RoPE

Transformer 原始的位置编码是绝对位置编码,直接加到输入上。现代模型(如 LlamaDeepSeek-V3 的 MLA)大多使用 RoPE(旋转位置编码)

RoPE 的核心思想:不是把位置加到输入上,而是把位置信息编码到 Q 和 K 的旋转矩阵中。这样 Attention 计算时天然包含相对位置信息。


五、完整架构:Putting It All Together

宏观结构

Transformer 采用 Encoder-Decoder(编码器-解码器)架构:

输入序列: ["我", "爱", "学习"]
                │
                ▼
     ┌──────────────────────┐
     │    Encoder(编码器)   │
     │  6 层,每层包含:       │
     │  • Multi-Head Attn   │
     │  • Feed Forward      │
     │  • Add & Norm        │
     └──────────┬───────────┘
                │ 编码后的表示
                ▼
     ┌──────────────────────┐
     │    Decoder(解码器)   │
     │  6 层,每层包含:       │
     │  • Masked Multi-Head  │
     │  • Cross-Attention   │
     │  • Feed Forward      │
     │  • Add & Norm        │
     └──────────┬───────────┘
                │
                ▼
输出序列: ["I", "love", "learning"]

每一层的内部

输入向量 (512 维)
    │
    ▼
┌──────────────────────┐
│  Multi-Head Attention│  ← 捕捉词与词的关系
│  8 头 × 64 维        │
│  残差连接 + 层归一化   │  ← 稳定训练
└──────────────────────┘
    │
    ▼
┌──────────────────────┐
│  Feed Forward Network │  ← 每个位置独立做非线性变换
│  Linear → ReLU → Linear│
│  残差连接 + 层归一化   │
└──────────────────────┘
    │
    ▼
    输出到下一层

两个关键设计

1. 残差连接(Residual Connection)

每条子层输出都加上原始输入:output = LayerNorm(x + Sublayer(x))

输入 x ──→ [Self-Attention] ──→ ──→ ⊕ ──→ LayerNorm ──→ 输出
                                  ↑
                                  └── ← x(原始输入跳过注意力层)

这个设计借鉴了 ResNetShortcut Connections,让梯度可以直接回传,训练更稳定。

2. 掩码自注意力(Masked Self-Attention)

Decoder 中的自注意力是掩码的——每个位置只能关注它自己及之前的位置,不能”偷看”未来的词。

"我 爱 学 习"
                     可关注的
位置 1 ("我"):    [✅, ❌, ❌, ❌]  → 只能看自己
位置 2 ("爱"):    [✅, ✅, ❌, ❌]  → 能看"我"和"爱"
位置 3 ("学"):    [✅, ✅, ✅, ❌]  
位置 4 ("习"):    [✅, ✅, ✅, ✅]

实现方式:把未来位置的分数设为 -∞,Softmax 后为 0

六、从 Transformer 到现代大模型

Transformer 之后的发展:

Transformer (2017)          ← 基础架构
    │
    ├── BERT (2018)          ← 编码器架构,双向理解
    ├── GPT 系列 (2018-)     ← 解码器架构,文本生成
    ├── T5 (2019)            ← Encoder-Decoder,文本到文本
    │
    ├── Llama 系列 (2023-)   ← 仅解码器,开源优化
    ├── DeepSeek 系列        ← 引入 MLA + MoE
    └── Qwen 系列            ← 多语言优化

几乎所有现代大语言模型都基于 Transformer 解码器架构,核心创新围绕:

  • 注意力优化GQAMLADSA
  • 架构扩展MoEMoDE
  • 训练方法SFTGRPOSelf-Play RL

进一步深化理解

我们从问题出发

为什么必须用QKV三个矩阵

朴素注意力的困境 alt text 零参数的情况下,无可以计算的、可以训练的参数 于此,其实在于X被迫扮演多重角色,既要作为注意力的关注者,又要去提取最相关的特征

参考文献

2025 年 12 月 15 日