欢迎光临
我们一直在努力

Transformer架构深度详解 —— 从零基础入门到精通

#

目录

  • 第一章:序列建模的历史演进
  • 第二章:注意力机制的数学原理(超详细推导)
  • 第三章:多头注意力的深层设计哲学
  • 第四章:位置编码的完整数学推导
  • 第五章:Encoder的逐层深度拆解
  • 第六章:Decoder的逐层深度拆解
  • 第七章:三种架构范式的本质区别
  • 第八章:经典模型深度剖析
  • 第九章:Transformer的训练与优化
  • 第十章:面试高频问题深度解析

第一章:序列建模的历史演进

1.1 为什么需要序列建模?

自然语言、语音、时间序列、DNA序列——现实世界中充满了序列数据。序列数据的核心特点是:元素之间存在顺序依赖关系。

"我 吃 苹果" vs "苹果 吃 我"
→ 同样的词,顺序不同,含义完全不同
→ 这就是序列建模的核心挑战:如何捕捉顺序和上下文信息

1.2 RNN的工作原理(详细版)

RNN通过隐藏状态的递推来处理序列:

时间步t的计算:
h_t = tanh(W_hh × h_{t-1} + W_xh × x_t + b)

其中:
x_t : 当前时刻的输入向量(如词嵌入)
h_{t-1} : 上一时刻的隐藏状态("记忆")
W_hh : 隐藏层到隐藏层的权重矩阵(捕捉时序依赖)
W_xh : 输入到隐藏层的权重矩阵(处理当前输入)
b : 偏置项
tanh : 激活函数(将值压缩到[-1,1])

展开来看:
h_1 = tanh(W_hh × h_0 + W_xh × x_1 + b) ← 只看x1
h_2 = tanh(W_hh × h_1 + W_xh × x_2 + b) ← 间接看了x1, 直接看x2
h_3 = tanh(W_hh × h_2 + W_xh × x_3 + b) ← 间接看了x1,x2, 直接看x3

1.3 RNN的梯度消失问题(数学推导)

这是RNN最致命的问题。展开递推关系:

h_t = tanh(W_hh × h_{t-1} + …)

∂h_t / ∂h_1 = ∏(k=2 to t) ∂h_k / ∂h_{k-1}
= ∏(k=2 to t) W_hh^T × diag(tanh'(z_k))

其中 tanh'(z) ∈ (0, 1],最大值为1(当z=0时)

当t很大时,这个连乘会:
– 如果W_hh的特征值 < 1:连乘趋近于0 → 梯度消失
– 如果W_hh的特征值 > 1:连乘趋近于∞ → 梯度爆炸

直觉理解:信息像接力赛一样传递,每一棒都有损耗。跑10棒后,第一棒的信息几乎完全丢失。

1.4 LSTM和GRU的改进(仍不够)

LSTM引入了门控机制和细胞状态:

遗忘门: f_t = σ(W_f × [h_{t-1}, x_t] + b_f) ← 决定忘记什么
输入门: i_t = σ(W_i × [h_{t-1}, x_t] + b_i) ← 决定记住什么
输出门: o_t = σ(W_o × [h_{t-1}, x_t] + b_o) ← 决定输出什么

细胞状态: C_t = f_t ⊙ C_{t-1} + i_t ⊙ tanh(W_C × [h_{t-1}, x_t] + b_C)
隐藏状态: h_t = o_t ⊙ tanh(C_t)

关键改进:细胞状态C_t的更新是"加法"而非"乘法"
→ 梯度可以沿着C_t几乎无损地传播很远
→ 但仍需按顺序计算,无法并行

1.5 Transformer的革命性突破

2017年Google的论文《Attention Is All You Need》提出了三个革命性思想:

创新点解决的问题效果
自注意力(Self-Attention) 长距离依赖 任意两个位置直接交互,O(1)路径长度
并行计算 RNN的串行瓶颈 所有位置同时计算,GPU利用率100%
位置编码 没有循环就没有位置信息 显式注入位置信息,可学习或固定

第二章:注意力机制的数学原理(超详细推导)

2.1 从信息检索的角度理解注意力

想象你在图书馆查资料:

  • 你有一个查询(Query):“什么是量子计算?”
  • 每本书有一个键(Key):标题/摘要/关键词
  • 每本书有一个值(Value):实际内容

检索过程:

  • 用你的查询和每本书的键计算相似度
  • 相似度高的书,你花更多时间阅读
  • 最终你综合各本书的内容形成你的理解
  • 数学表达:
    attention_output = Σ(similarity(query, key_i) × value_i)
    其中 similarity 需要归一化(权重之和=1)

    2.2 Q、K、V的线性变换

    输入序列X(n×d矩阵,n个词,每个词d维),通过三个可学习的权重矩阵变换:

    Q = X × W_Q (n × d_k) W_Q: d × d_k
    K = X × W_K (n × d_k) W_K: d × d_k
    V = X × W_V (n × d_v) W_V: d × d_v

    为什么需要三个不同的矩阵?
    → 因为"查询"、"被查询"、"信息内容"是三个不同的语义空间
    → 类比:搜索引擎中,搜索词(Query)、网页标题(Key)、网页内容(Value)
    是三种不同的东西,需要不同的表示方式

    如果不区分Q/K/V会怎样?
    → 那就是最简单的"自相关"计算,表达能力有限
    → 通过独立的投影矩阵,模型可以学习到更丰富的交互模式

    2.3 相似度计算:为什么用点积?

    有多种计算相似度的方式:

    方式1: 加性注意力 (Bahdanau, 2015)
    score(q, k) = v^T × tanh(W_1 × q + W_2 × k)
    → 需要额外参数v, W1, W2
    → 计算较慢

    方式2: 点积注意力 (Luong, 2015)
    score(q, k) = q^T × k
    → 不需要额外参数
    → 可以用矩阵乘法高效计算

    方式3: 缩放点积注意力 (Vaswani, 2017) ← Transformer使用的方式
    score(q, k) = q^T × k / √d_k
    → 点积 + 缩放因子

    为什么Transformer选择点积?
    → 点积可以用高度优化的矩阵乘法(GEMM)实现
    → GPU上的矩阵乘法是高度优化的,速度远快于加性注意力
    → 实际测试中,点积注意力比加性注意力快2-4倍

    2.4 为什么要除以√d_k?(面试高频问题!)

    这是很多人忽略的关键细节。

    假设Q和K的每个分量都是独立的均值为0、方差为1的随机变量

    那么 q·k = Σ(q_i × k_i),i=1到d_k

    每个q_i × k_i的期望:E[q_i × k_i] = E[q_i] × E[k_i] = 0
    每个q_i × k_i的方差:Var[q_i × k_i] = 1

    q·k的期望:E[q·k] = 0
    q·k的方差:Var[q·k] = d_k ← 方差随维度线性增长!

    当d_k很大时(如512或1024):
    → 点积值的方差很大
    → 点积值可能非常大(如±20甚至±50)
    → softmax的输入值很大时,输出会趋近于one-hot(梯度几乎为0)

    数值示例:
    d_k = 512, 假设点积值为 [20, -15, 10, -8, 5, …]
    softmax([20, -15, 10, -8, 5]) ≈ [1.0, 0.0, 0.0, 0.0, 0.0]
    → 梯度消失!注意力变成了"硬选择",无法学习

    除以√d_k后:
    点积值变为 [20/√512, -15/√512, …] ≈ [0.88, -0.66, …]
    → 方差被归一化为1
    → softmax输出更均匀,梯度可以正常流动

    一句话总结:除以√d_k是为了稳定softmax的梯度,防止高维空间中点积值过大导致梯度消失。

    2.5 softmax的作用与性质

    softmax(z_i) = e^(z_i) / Σ_j e^(z_j)

    性质:

  • 输出在(0,1)之间,且所有输出之和=1 → 概率分布
  • 保持相对大小关系:若z_i > z_j,则softmax(z_i) > softmax(z_j)
  • 温度控制:实际中有时用softmax(z/τ),τ越大分布越均匀
  • 为什么用softmax而不是sigmoid?

    • sigmoid独立地将每个值映射到(0,1),但输出之和不等于1
    • 注意力权重需要表示一个概率分布(分配给每个位置的权重之和应为1)
    • softmax天然满足这个约束

    为什么不用ReLU/线性?

    • 注意力权重必须是非负的(权重表示"关注度",不能是负数)
    • 还需要归一化(所有权重之和为1,表示分配了100%的注意力)
    • softmax同时满足这两个要求

    2.6 完整的注意力计算流程(逐步推导)

    输入: X ∈ R^(n×d) (n=序列长度, d=模型维度)
    参数: W_Q ∈ R^(d×d_k), W_K ∈ R^(d×d_k), W_V ∈ R^(d×d_v)

    Step 1: 线性投影
    Q = X × W_Q ∈ R^(n×d_k) 每个词变成一个"查询向量"
    K = X × W_K ∈ R^(n×d_k) 每个词变成一个"键向量"
    V = X × W_V ∈ R^(n×d_v) 每个词变成一个"值向量"

    Step 2: 计算注意力分数矩阵
    S = Q × K^T ∈ R^(n×n)
    S_ij = q_i^T × k_j / √d_k
    → S_ij表示第i个词对第j个词的"关注程度"

    Step 3: 缩放
    S = S / √d_k

    Step 4: softmax归一化(按行)
    A = softmax(S) ∈ R^(n×n)
    A_ij = e^(S_ij) / Σ_k e^(S_ik)
    → 每一行是一个概率分布,表示该词对所有词的注意力分配

    Step 5: 加权求和
    O = A × V ∈ R^(n×d_v)
    O_i = Σ_j A_ij × v_j
    → 第i个词的输出 = 所有词的值向量的加权平均
    → 权重由注意力矩阵A的第i行决定


    第三章:多头注意力的深层设计哲学

    3.1 为什么需要多头?

    单头注意力的局限:

    单头注意力只能学习一种注意力模式
    但在自然语言中,一个词需要同时关注多种不同类型的信息:

    "那只 猫 坐在 垫子 上,因为 它 很 舒服"

    "它"需要同时关注:
    – 语法关系:谁是"它"的指代对象?→ 关注"猫"(主语)
    – 语义关系:为什么舒服?→ 关注"垫子"(地点)
    – 位置关系:最近的名词是什么?→ 关注"垫子"(相邻)

    一个注意力头很难同时捕捉所有这些关系!

    3.2 多头注意力的计算

    MultiHead(Q, K, V) = Concat(head_1, …, head_h) × W_O

    其中:
    head_i = Attention(X×W_Q_i, X×W_K_i, X×W_V_i)

    参数:
    W_Q_i ∈ R^(d×d_k), W_K_i ∈ R^(d×d_k), W_V_i ∈ R^(d×d_v) (每个头一组)
    W_O ∈ R^(h×d_v × d) (输出投影矩阵)

    通常设置:
    d_k = d_v = d_model / h
    例如: d_model=512, h=8 → d_k = d_v = 64

    每个头看到的是输入的不同"视角":
    head_1: 512维 → 投影到64维的子空间1
    head_2: 512维 → 投影到64维的子空间2

    head_8: 512维 → 投影到64维的子空间8

    → 8个头在8个不同的子空间中独立计算注意力
    → 最终拼接起来,信息更丰富

    3.3 多头注意力的计算复杂度分析(面试常问!)

    单头注意力的时间复杂度:
    Q×K^T: O(n × n × d_k) = O(n² × d_k)
    softmax: O(n²)
    A×V: O(n × n × d_v) = O(n² × d_v)
    总计: O(n² × d)

    多头注意力的时间复杂度:
    h个头,每个头维度d/h
    每个头: O(n² × d/h)
    h个头: O(h × n² × d/h) = O(n² × d)
    + 输出投影: O(n × d × d) = O(n × d²)
    总计: O(n² × d + n × d²) ≈ O(n² × d) (当n > d时)

    空间复杂度:
    注意力矩阵: O(n²) ← 这是Transformer的主要瓶颈!
    当序列长度n很大时(如n=10000),注意力矩阵需要100M个元素

    3.4 不同头学到了什么?

    通过可视化分析,不同头确实学到了不同的语言知识:

    头1: 主语-谓语关系
    "猫" ←→ "坐" 高注意力

    头2: 修饰关系
    "漂亮的" ←→ "花" 高注意力

    头3: 指代关系
    "它" ←→ "猫" 高注意力

    头4: 位置偏好
    每个词倾向于关注相邻的词

    头5: 句法结构
    关注标点符号和句子边界


    第四章:位置编码的完整数学推导

    4.1 为什么需要位置编码?

    自注意力的计算是"排列不变(permutation invariant)"的:
    Attention(π(Q), π(K), π(V)) = π(Attention(Q, K, V))
    其中π是任意排列

    这意味着:
    "我 爱 中国" 和 "中国 爱 我" 在自注意力看来是一样的!
    因为只是矩阵行列的重排,不影响结果

    但自然语言中,词序至关重要:
    "我 爱 中国" ≠ "中国 爱 我"

    所以必须显式注入位置信息!

    4.2 正弦位置编码的设计

    Transformer使用固定的正弦/余弦位置编码:

    PE(pos, 2i) = sin(pos / 10000^(2i/d_model))
    PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))

    其中:
    pos : 词在序列中的位置(0, 1, 2, …)
    i : 维度索引(0, 1, 2, …, d_model/2-1)
    d_model : 模型维度

    4.3 为什么选择sin/cos?(面试高频!)

    原因1:可以表示相对位置

    对于任意固定偏移k,存在线性变换矩阵M_k使得:
    PE(pos+k) = M_k × PE(pos)

    证明:
    考虑一对维度(2i, 2i+1):
    [PE(pos+k, 2i) ] [cos(kω_i) sin(kω_i)] [PE(pos, 2i) ]
    [PE(pos+k, 2i+1)] = [-sin(kω_i) cos(kω_i)] [PE(pos, 2i+1)]

    其中 ω_i = 1/10000^(2i/d_model)

    这是一个旋转矩阵!→ 旋转角度 = k × ω_i
    → 模型可以通过学习来捕捉相对位置关系

    原因2:可以泛化到更长的序列

    训练时见过的最长序列是512个词
    但sin/cos函数是周期性的,可以计算任意位置的编码
    → 理论上可以泛化到任意长度(虽然效果会下降)

    原因3:值域有界

    PE(pos) ∈ [-1, 1](因为sin和cos的值域)
    → 不会因为位置太远而导致编码值过大
    → 与词嵌入(通常也在[-1,1]范围内)可以合理相加

    4.4 位置编码的可视化

    假设d_model = 128,绘制PE矩阵:

    位置 0: [sin(0), cos(0), sin(0), cos(0), …] = [0, 1, 0, 1, …]
    位置 1: [sin(ω₁), cos(ω₁), sin(ω₂), cos(ω₂), …]
    位置 2: [sin(2ω₁), cos(2ω₁), sin(2ω₂), cos(2ω₂), …]

    低维度(i小)→ 频率高(ω大)→ 变化快 → 捕捉细粒度位置
    高维度(i大)→ 频率低(ω小)→ 变化慢 → 捕捉粗粒度位置

    → 不同维度关注不同粒度的位置信息

    4.5 可学习位置编码 vs 固定位置编码

    固定编码(原始Transformer):
    PE由sin/cos公式计算,不可学习
    优点:实现简单,可泛化
    缺点:可能不是最优的

    可学习编码(BERT, GPT):
    PE作为可学习参数,通过训练优化
    优点:可以学到更适合任务的位置表示
    缺点:不能泛化到训练时没见过的长度

    实践发现:
    在大规模预训练中,两者性能差异很小
    BERT使用可学习编码
    原始Transformer和部分模型使用固定编码


    第五章:Encoder的逐层深度拆解

    5.1 Encoder的完整数据流

    输入序列: "我 爱 中国"

    Step 1: 词嵌入(Embedding)
    "我" → [0.2, -0.5, 0.8, …] (d_model维)
    "爱" → [0.1, 0.3, -0.7, …]
    "中国" → [-0.4, 0.6, 0.2, …]

    矩阵: X_emb ∈ R^(3×512)

    Step 2: 加入位置编码
    X = X_emb + PE
    → 现在每个词既有语义信息又有位置信息

    Step 3: N层Encoder Block(逐层处理)
    for layer = 1 to N:
    X = EncoderLayer(X)

    Step 4: 输出
    H = X ∈ R^(3×512) (上下文化的词表示)
    → "我"的表示已经融合了"爱"和"中国"的信息

    5.2 单层Encoder Block的内部计算(每一步都详细)

    输入: X ∈ R^(n×d)

    ━━━━━━━━ 子层1: 多头自注意力 ━━━━━━━━

    1. 线性投影生成Q, K, V:
    Q = X × W_Q ∈ R^(n×d_k)
    K = X × W_K ∈ R^(n×d_k)
    V = X × W_V ∈ R^(n×d_v)

    2. 计算注意力分数:
    S = Q × K^T / √d_k ∈ R^(n×n)

    对于输入"我 爱 中国":
    S = [[s11, s12, s13], ← "我"对"我","爱","中国"的注意力分数
    [s21, s22, s23], ← "爱"对"我","爱","中国"的注意力分数
    [s31, s32, s33]] ← "中国"对"我","爱","中国"的注意力分数

    3. softmax归一化:
    A = softmax(S) ∈ R^(n×n)

    注意:Encoder中是全连接注意力(没有掩码)
    → 每个词可以看到所有其他词

    4. 加权求和:
    Attn_output = A × V ∈ R^(n×d_v)

    5. 输出投影:
    MultiHead_output = Attn_output × W_O ∈ R^(n×d)

    ━━━━━━━━ 残差连接 + 层归一化 ━━━━━━━━

    6. 残差连接:
    X' = X + MultiHead_output
    → 保留原始输入信息,防止梯度消失

    7. 层归一化(LayerNorm):
    X'' = LayerNorm(X')

    LayerNorm的计算(比BatchNorm更适合NLP):
    μ = mean(X') (对每个样本的所有维度求均值)
    σ² = var(X') (对每个样本的所有维度求方差)
    X'' = γ × (X' – μ) / √(σ² + ε) + β

    其中γ和β是可学习的缩放和偏移参数
    ε是防止除零的小常数(通常1e-5)

    ━━━━━━━━ 子层2: 前馈网络(FFN) ━━━━━━━━

    8. 两层全连接 + 激活函数:
    FFN(X'') = max(0, X'' × W_1 + b_1) × W_2 + b_2

    W_1 ∈ R^(d×d_ff), W_2 ∈ R^(d_ff×d)
    d_ff通常 = 4 × d(如d=512, d_ff=2048)

    为什么d_ff = 4d?
    → 先扩展到4倍维度(增加表达能力)
    → 再压缩回原始维度
    → 类似于"先展开思考,再压缩总结"

    ━━━━━━━━ 残差连接 + 层归一化 ━━━━━━━━

    9. 残差连接 + LayerNorm:
    output = LayerNorm(X'' + FFN(X''))

    → output送入下一层Encoder(如果是最后一层,送入Decoder)

    5.3 层归一化 vs 批归一化(面试常问!)

    BatchNorm: 对同一个特征维度,跨batch归一化
    μ, σ²是在batch维度上计算的
    问题:NLP中序列长度不等,batch统计不稳定

    LayerNorm: 对同一个样本,跨所有特征维度归一化
    μ, σ²是在特征维度上计算的
    优点:不依赖batch大小,每个样本独立归一化

    为什么Transformer选择LayerNorm?
    1. NLP序列长度可变,BatchNorm统计不稳定
    2. LayerNorm在推理时不需要维护running mean/var
    3. 小batch时BatchNorm统计噪声大,LayerNorm不受影响

    5.4 Pre-Norm vs Post-Norm

    Post-Norm(原始Transformer):
    output = LayerNorm(x + SubLayer(x))
    → 先计算子层,再加残差,最后归一化

    Pre-Norm(后来的改进):
    output = x + SubLayer(LayerNorm(x))
    → 先归一化,再计算子层,最后加残差

    Pre-Norm的优势:
    – 训练更稳定(梯度流更平滑)
    – 可以训练更深的模型
    – 不需要warmup学习率

    为什么Pre-Norm更稳定?
    Post-Norm: 梯度需要经过LayerNorm,可能导致梯度消失
    Pre-Norm: 残差连接直接传递梯度,不经过LayerNorm
    → 梯度可以无损地从最后一层传到第一层


    第六章:Decoder的逐层深度拆解

    6.1 Decoder与Encoder的核心区别

    Encoder: "理解输入"
    – 双向注意力:每个词可以看到所有其他词
    – 一次性处理整个输入序列

    Decoder: "生成输出"
    – 单向注意力(掩码):只能看到已生成的词
    – 交叉注意力:可以看Encoder的全部输出
    – 自回归:一个词一个词地生成

    6.2 掩码自注意力的详细实现

    为什么需要掩码?

    训练时(Teacher Forcing):
    输入: [<BOS>, 我, 爱, 中国]
    目标: [我, 爱, 中国, <EOS>]

    一次性输入所有已知token,但必须防止"偷看"未来

    推理时:
    自然就是逐词生成,不存在偷看问题

    掩码矩阵M(-∞表示遮挡,0表示可见):
    <BOS> 我 爱 中国
    <BOS> [ 0, -∞, -∞, -∞ ]
    我 [ 0, 0, -∞, -∞ ]
    爱 [ 0, 0, 0, -∞ ]
    中国 [ 0, 0, 0, 0 ]

    应用掩码:
    S_masked = Q × K^T / √d_k + M

    被遮挡位置加上-∞后:
    softmax(-∞) = 0 → 注意力权重为0,完全忽略

    数学表达:
    A_ij = softmax(S_ij + M_i)
    当j > i时,M_i = -∞ → A_ij = 0
    当j ≤ i时,M_i = 0 → A_ij = softmax(S_ij)

    6.3 交叉注意力的详细机制

    交叉注意力是Encoder和Decoder之间的桥梁:

    Q = Decoder当前层的输出 × W_Q ← "我在找什么?"
    K = Encoder最终输出 × W_K ← "输入中有什么?"
    V = Encoder最终输出 × W_V ← "输入的实际内容"

    CrossAttention(Q_dec, K_enc, V_enc) = softmax(Q_dec × K_enc^T / √d_k) × V_enc

    直觉理解:
    在翻译"I love China"→"我爱中国"时:

    生成"我"时:
    Q_dec = [当前decoder状态的查询]
    与K_enc中的"I"、"love"、"China"分别计算相似度
    → 可能"I"的注意力最高 → 获取"I"的V信息 → 输出"我"

    生成"爱"时:
    → 可能"love"的注意力最高 → 输出"爱"

    生成"中国"时:
    → 可能"China"的注意力最高 → 输出"中国"

    6.4 Decoder的自回归生成过程

    推理时,Decoder逐词生成:

    Step 1:
    输入: <BOS>
    掩码自注意力: <BOS> → <BOS>
    交叉注意力: <BOS> → Encoder输出
    FFN
    输出层: P(我) = 0.6, P(你) = 0.2, …
    → 选择"我"

    Step 2:
    输入: <BOS>, 我
    掩码自注意力: <BOS>→我, 我→我
    交叉注意力: <BOS>→Encoder, 我→Encoder
    FFN
    输出层: P(爱) = 0.7, P(喜欢) = 0.15, …
    → 选择"爱"

    Step 3:
    输入: <BOS>, 我, 爱

    → 选择"中国"

    Step 4:
    输入: <BOS>, 我, 爱, 中国

    → 选择<EOS>,生成结束


    第七章:三种架构范式的本质区别

    7.1 Encoder-Only(以BERT为代表)

    架构: 只有Encoder,没有Decoder
    注意力: 双向自注意力(每个词看到所有词)

    ┌──────────────────────────────────┐
    │ 输入: [CLS] 我 爱 中国 [SEP] │
    │ ↓ ↓ ↓ ↓ ↓ │
    │ ┌────────────────────────────┐ │
    │ │ 双向自注意力 × 12层 │ │
    │ │ 每个词都能看到所有其他词 │ │
    │ └────────────────────────────┘ │
    │ ↓ ↓ ↓ ↓ ↓ │
    │ 输出: h_CLS h_我 h_爱 h_中国 h_SEP │
    │ ↓ │
    │ [CLS]的表示 → 分类头 → 类别 │
    └──────────────────────────────────┘

    预训练: 掩码语言模型(MLM)
    随机遮挡15%的词,让模型预测被遮挡的词
    "我 爱 [MASK] 国" → 预测"中"

    为什么用MLM而不是NTP?
    → MLM迫使模型利用双向上下文来预测
    → 这就是BERT"理解能力强"的原因

    7.2 Decoder-Only(以GPT为代表)

    架构: 只有Decoder(带掩码),没有Encoder
    注意力: 单向掩码自注意力(只看前面的词)

    ┌──────────────────────────────────┐
    │ 输入: 今天 天气 真 好 │
    │ ↓ ↓ ↓ ↓ │
    │ ┌────────────────────────────┐ │
    │ │ 掩码自注意力 × 96层 │ │
    │ │ 每个词只能看到前面的词 │ │
    │ └────────────────────────────┘ │
    │ ↓ ↓ ↓ ↓ │
    │ 输出: → → → → │
    │ ↓ │
    │ 预测下一个词: "我们" │
    └──────────────────────────────────┘

    预训练: 下一个词预测(NTP/Causal LM)
    给定前文,预测下一个词

    为什么GPT只用Decoder也能做理解任务?
    → 通过prompt将理解任务转化为生成任务
    → "这部电影好看吗?正面还是负面?" → "正面"
    → 足够大的模型可以通过生成来"理解"

    7.3 Encoder-Decoder(以T5为代表)

    架构: Encoder + Decoder,通过交叉注意力连接
    注意力: Encoder双向 + Decoder单向 + 交叉

    ┌─────────────────┐ ┌─────────────────────┐
    │ Encoder │ │ Decoder │
    │ │ │ │
    │ 输入: I love China│ │ 输出: <BOS> │
    │ ↓ ↓ ↓ │ │ ↓ │
    │ 双向自注意力×12 │ │ 掩码自注意力 │
    │ ↓ ↓ ↓ │ │ ↓ │
    │ 编码: h1 h2 h3 │────│→ 交叉注意力(Q来自Dec) │
    │ │ │ ↓ │
    │ │ │ FFN │
    │ │ │ ↓ │
    │ │ │ 输出概率: P("我") │
    └─────────────────┘ └─────────────────────┘

    推理过程:
    Encoder一次性编码"I love China" → [h1, h2, h3]
    Decoder自回归生成:
    <BOS> + 交叉注意力(h1,h2,h3) → "我"
    <BOS> 我 + 交叉注意力(h1,h2,h3) → "爱"
    <BOS> 我 爱 + 交叉注意力(h1,h2,h3) → "中国"
    <BOS> 我 爱 中国 + 交叉注意力(h1,h2,h3) → <EOS>

    7.4 三种架构的本质区别总结

    核心区别在于"注意力的方向和范围":

    Encoder-Only (BERT):
    双向全连接注意力
    "我"看→ [我, 爱, 中国]
    "爱"看→ [我, 爱, 中国]
    "中国"看→ [我, 爱, 中国]
    → 每个位置都能看到完整上下文
    → 适合"理解"任务

    Decoder-Only (GPT):
    单向因果注意力(下三角掩码)
    "今天"看→ [今天]
    "天气"看→ [今天, 天气]
    "真"看→ [今天, 天气, 真]
    → 每个位置只能看到之前的
    → 适合"生成"任务

    Encoder-Decoder (T5):
    Encoder: 双向全连接(同BERT)
    Decoder: 单向因果 + 交叉注意力
    → 最灵活,但参数最多
    → 适合"输入→输出"的转换任务


    第八章:经典模型深度剖析

    8.1 BERT的训练细节

    预训练任务1: 掩码语言模型(MLM)
    策略(很重要!):
    – 随机选择15%的token
    – 80%替换为[MASK]
    – 10%替换为随机词
    – 10%保持不变

    为什么这样分配?
    → 如果100%用[MASK],模型可能只学会了处理[MASK]
    → 10%随机词迫使模型对每个位置都做判断
    → 10%不变让模型知道"正确答案有时候就在这里"

    预训练任务2: 下一句预测(NSP)
    输入: [CLS] 今天天气好 [SEP] 我们出去玩 [SEP]
    标签: IsNext / NotNext

    后续研究(RoBERTa)发现NSP没啥用,去掉了
    → 说明这个任务设计不够好

    8.2 GPT的规模定律(Scaling Law)

    GPT-3的175B参数分布在96层Transformer Decoder中:
    d_model = 12288
    n_heads = 96
    d_ff = 4 × 12288 = 49152
    n_layers = 96

    Kaplan et al. (2020)发现的Scaling Law:
    L(N) ∝ N^(-0.076) 模型参数量N
    L(D) ∝ D^(-0.095) 数据量D
    L(C) ∝ C^(-0.050) 计算量C

    含义:
    → 模型越大、数据越多、计算越多,损失就越低
    → 且这种关系是幂律(对数坐标下是直线)
    → 这就是为什么大模型一直在变大

    8.3 T5的统一框架

    T5将所有NLP任务统一为"文本到文本":
    分类: "sentiment: I love it" → "positive"
    翻译: "translate English to German: That is good" → "Das ist gut"
    摘要: "summarize: <长文本>" → "<摘要>"
    问答: "question: Who is CEO? context: Tim Cook is CEO…" → "Tim Cook"

    这种统一使得一个模型可以处理所有任务
    → 这是后来prompt engineering的思想基础


    第九章:Transformer的训练与优化

    9.1 学习率调度(Warmup + Decay)

    原始Transformer使用特殊的学习率调度:

    lr = d_model^(-0.5) × min(step^(-0.5), step × warmup_steps^(-1.5))

    前warmup_steps步:学习率线性增长
    之后:学习率按平方根衰减

    为什么需要warmup?
    → Transformer的初始参数是随机的
    → 如果一开始就用大学习率,梯度可能很大且不稳定
    → warmup让模型先用小学习率"热身",等参数稳定后再加大

    典型设置:warmup_steps = 4000

    9.2 标签平滑(Label Smoothing)

    标准交叉熵:目标是one-hot向量 [0, 0, 1, 0, …]
    标签平滑:将一小部分概率分配给其他词 [0.01, 0.01, 0.97, 0.01, …]

    L = -(1-ε)×log(p_target) – ε/V × Σ log(p_i)

    其中ε通常=0.1,V是词表大小

    为什么用标签平滑?
    → 防止模型过度自信(输出接近1.0的概率)
    → 提高泛化能力
    → 原始Transformer论文中使用了ε=0.1

    9.3 Dropout策略

    Transformer在三个地方使用Dropout:
    1. 注意力权重上:A = dropout(softmax(S))
    2. 每个子层的输出上
    3. 嵌入层上

    Dropout rate通常为0.1

    作用:
    → 防止过拟合
    → 训练时随机丢弃一些连接,迫使模型学习更鲁棒的表示
    → 推理时关闭Dropout,所有连接都使用


    第十章:面试高频问题深度解析

    Q1: 为什么Transformer用缩放点积而不是加性注意力?

    答:主要出于计算效率考虑。
    点积注意力可以用矩阵乘法(GEMM)高效实现,GPU上有专门的硬件加速。
    加性注意力需要逐元素计算tanh,无法充分利用GPU并行。
    实验表明,点积注意力比加性注意力快2-4倍,且效果相当。

    Q2: 为什么除以√d_k?

    答:防止高维空间中点积值过大导致softmax梯度消失。
    当d_k很大时,Q和K的点积方差为d_k,导致softmax输出趋近one-hot。
    除以√d_k将方差归一化为1,使softmax输出更均匀,梯度可以正常流动。

    Q3: 多头注意力中不同头学到了什么?

    答:不同头学习不同类型的语言关系。
    研究表明:有的头关注语法结构(主谓关系),有的关注语义(指代消解),
    有的关注位置(相邻词),有的关注特殊符号(标点)。
    多头机制让模型能同时捕捉多种关系模式。

    Q4: 位置编码为什么用sin/cos?

    答:三个原因:
    1. 可以表示相对位置(通过线性变换,即旋转矩阵)
    2. 值域有界([-1,1]),不会因为位置远而值过大
    3. 可以泛化到训练时没见过的序列长度

    Q5: LayerNorm和BatchNorm的区别?为什么用LayerNorm?

    答:BatchNorm在batch维度归一化,LayerNorm在特征维度归一化。
    NLP中序列长度可变,batch统计不稳定;小batch时统计噪声大。
    LayerNorm不依赖batch大小,每个样本独立归一化,更适合NLP。

    Q6: 为什么GPT只用Decoder就能做理解任务?

    答:GPT通过prompt将理解任务转化为生成任务。
    例如情感分析:"这部电影好看吗?正面还是负面?" → 生成"正面"。
    足够大的语言模型通过海量文本预训练,已经隐式地学会了"理解"。
    这种方式的灵活性和通用性远超专门设计的理解模型。

    Q7: Transformer的时间和空间复杂度?

    答:
    时间复杂度: O(n² × d),n是序列长度,d是模型维度
    空间复杂度: O(n² + n × d),n²来自注意力矩阵

    这是Transformer处理长序列的主要瓶颈。
    当n=10000时,注意力矩阵需要100M个元素。
    这也是后来各种高效Transformer(Linformer, Performer等)的研究动机。

    Q8: Pre-Norm和Post-Norm的区别?

    答:
    Post-Norm: output = LN(x + SubLayer(x)) ← 原始Transformer
    Pre-Norm: output = x + SubLayer(LN(x)) ← 后来改进

    Pre-Norm训练更稳定,因为残差连接直接传递梯度,不经过LayerNorm。
    但有研究发现Post-Norm在充分训练后效果可能更好(梯度更集中)。
    大部分现代模型(GPT、LLaMA)使用Pre-Norm。


    附录:核心公式速查表

    公式含义关键点
    Attention(Q,K,V) = softmax(QK^T/√d_k)V 缩放点积注意力 √d_k防止梯度消失
    MultiHead = Concat(heads)W_O 多头注意力 多视角理解
    FFN(x) = max(0, xW₁+b₁)W₂+b₂ 前馈网络 d_ff=4d扩展再压缩
    LN(x) = γ(x-μ)/√(σ²+ε) + β 层归一化 特征维度归一化
    PE(pos,2i) = sin(pos/10000^(2i/d)) 位置编码 相对位置可线性表示
    output = x + SubLayer(LN(x)) Pre-Norm 训练更稳定

    参考文献

  • Vaswani et al. (2017). “Attention Is All You Need”. NeurIPS. — Transformer原始论文
  • Devlin et al. (2019). “BERT: Pre-training of Deep Bidirectional Transformers”. NAACL.
  • Radford et al. (2019). “Language Models are Unsupervised Multitask Learners”. GPT-2.
  • Brown et al. (2020). “Language Models are Few-Shot Learners”. GPT-3. NeurIPS.
  • Raffel et al. (2020). “Exploring the Limits of Transfer Learning with a Unified Text-to-Text Transformer”. T5. JMLR.
  • Kaplan et al. (2020). “Scaling Laws for Neural Language Models”. arXiv.
  • Ba et al. (2016). “Layer Normalization”. arXiv.
  • Xiong et al. (2020). “On Layer Normalization in the Transformer Architecture”. ICML.
  • 赞(0)
    未经允许不得转载:171主机测评 » Transformer架构深度详解 —— 从零基础入门到精通
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址