本文是"代码实战三部曲"(第 36-38 篇)的收官之作,也是第 23-38 篇共 16 篇文章的数学全景总结。前 16 篇文章分别拆解了 Transformer 的各组件原理、数值计算和代码实现,本文将这些碎片拼接为一幅完整的信号流全景图------展示一条数据("我爱深度学习")从进入 Transformer 到最终驱动参数更新的完整旅程,并标注每一步的张量形状、数学公式和对应文章。
建议读者先略读本文的"大图"概览,再针对不熟悉的环节回看对应文章。
一、引言:为什么需要一张全景图?
经过 16 篇文章的漫长旅程,我们完成了:
- 第 23-32 篇:Transformer 各组件的数学原理拆解(注意力机制、FFN、LayerNorm、嵌入等)
- 第 33-34 篇:编码器与解码器的前向数值实例手算
- 第 35 篇:完整一轮训练流程(前向→损失→反向→更新→再前向)的数值计算
- 第 36 篇:编码器的代码实现与手动验证
- 第 37 篇:解码器的代码实现与交叉注意力验证
- 第 38 篇:完整 Transformer 训练实战与参数演化分析
但问题是:这些知识是碎片化的 。读者可能知道自注意力的数学公式,但不清楚它在前向传播中处于哪个位置、输入输出是什么形状、梯度如何回传。这正是本文要解决的问题------提供一张"数学全景图",将所有碎片拼合为一个完整的信号流。
本文的组织方式
本文不引入任何新概念,而是用统一的视角展示整个 Transformer 的信号流:
- 一张大图:从数据到参数的完整信号流概览(第二节)
- 维度追踪:每个操作的输入输出张量形状(第三节)
- 前向传播全景:每个组件的数学运算(第四节)
- 反向传播全景:梯度如何回传(第五节)
- 参数更新全景:从梯度到参数变化的桥梁(第六节)
- 系列索引:16 篇文章的交叉对照表(第七节)
- 数学原理速查:核心公式一览(第八节)
二、一张大图:Transformer 完整信号流
下面是本文的核心------Transformer 完整信号流图。它展示了一条数据从原始字符输入,经过编码器、解码器、输出投影,最终以梯度形式回传并更新所有参数的全过程。
╔══════════════════════════════════════════════════════════════════════════════════╗
║ TRANSFORMER 完整信号流(从数据到参数) ║
╚══════════════════════════════════════════════════════════════════════════════════╝
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 一、数据准备 │
│ │
│ Source: "我爱深度学习" ──→ 字符级分词 ──→ [2, 15, 1, 374, 153, 306, 422, 3] │
│ Target: "i love deep learning" ──→ 字符级分词 ──→ [2, 9, 12, ... , 3] │
│ 词表: 中文 1106 字 / 英文 54 字符 │
└─────────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 二、编码器前向(Encoder Forward) 形状变化: (8,) → (8, 32) │
│ │
│ src_ids (8 tokens) │
│ │ │
│ ▼ │
│ ┌─ Embedding ──┐ (8, 32) lookup + scale: x * √d_model │
│ │ 形状: 1106×32 │ │
│ └──────┬───────┘ │
│ ▼ │
│ ┌─ PositionalEncoding ──┐ (8, 32) PE(pos,2i)=sin(pos/10000^{2i/d}) │
│ └──────┬───────────────┘ PE(pos,2i+1)=cos(pos/10000^{2i/d}) │
│ ▼ ← 编码器层 × 3 │
│ ┌──────────────────────────┐ │
│ │ EncoderLayer 0 │ │
│ │ ┌──────────────────┐ │ 自注意力: Q=K=V (8,32) │
│ │ │ Self-Attention │ │ Q/K/V投影 → 分4头 → scores=QK^T/√8 │
│ │ │ (MultiHead, h=4) │ │ → softmax → 加权V → 合并4头 → WO投影 │
│ │ └────────┬─────────┘ │ attn_weights: (4, 8, 8) │
│ │ ▼ │ │
│ │ LayerNorm(x + attn) │ 残差连接 + LN │
│ │ ▼ │ │
│ │ ┌──────────────────┐ │ FFN: Linear(32→128) → ReLU → Linear(128→32) │
│ │ │ FeedForward │ │ │
│ │ └────────┬─────────┘ │ │
│ │ ▼ │ │
│ │ LayerNorm(x + ffn) │ 每层输出范数稳定在 √32 ≈ 5.657 │
│ └──────────┬───────────────┘ │
│ ▼ │
│ ┌──────────────────────────┐ │
│ │ EncoderLayer 1, 2 │ (形状与第0层完全一致) │
│ └──────────┬───────────────┘ │
│ ▼ │
│ enc_output: (8, 32) ← 编码器输出(作为解码器交叉注意力的 K,V) │
└─────────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 三、解码器前向(Decoder Forward) 形状变化: (19,) → (19, 32) → (19, 54)│
│ │
│ tgt_ids (19 tokens: <sos> + "ilovedeeplearning" + <eos>) │
│ │ │
│ ▼ │
│ ┌─ Embedding ──┐ (19, 32) × √d_model │
│ └──────┬───────┘ │
│ ▼ │
│ ┌─ PositionalEncoding ──┐ (19, 32) │
│ └──────┬───────────────┘ │
│ ▼ ← 解码器层 × 3 │
│ ┌──────────────────────────┐ │
│ │ DecoderLayer 0 │ │
│ │ ┌──────────────────┐ │ 因果自注意力: Q=K=V (19,32) │
│ │ │ Masked Self-Attn │ │ 上三角 masked_fill(-inf) │
│ │ │ (causal=True) │ │ attn_weights: (4, 19, 19), 上三角=0 │
│ │ └────────┬─────────┘ │ │
│ │ ▼ │ │
│ │ LayerNorm(x + attn) │ │
│ │ ▼ │ │
│ │ ┌──────────────────┐ │ 交叉注意力: Q(19,32)←解码器, K=V(8,32)←编码器 │
│ │ │ Cross-Attention │ │ scores = Q_dec · K_enc^T / √8 │
│ │ │ (Q←dec, KV←enc) │ │ 形状: (4, 19, 8) ← 矩形而非方阵 │
│ │ └────────┬─────────┘ │ │
│ │ ▼ │ │
│ │ LayerNorm(x + cross) │ │
│ │ ▼ │ │
│ │ ┌──────────────────┐ │ FFN: 32→128→32 │
│ │ │ FeedForward │ │ │
│ │ └────────┬─────────┘ │ │
│ │ ▼ │ │
│ │ LayerNorm(x + ffn) │ │
│ └──────────┬───────────────┘ │
│ ▼ │
│ ┌──────────────────────────┐ │
│ │ DecoderLayer 1, 2 │ (形状一致,但注意力分布随层变化) │
│ └──────────┬───────────────┘ │
│ ▼ │
│ ┌─ OutputProj (54×32) ─┐ Linear(32→54), 输出 logits │
│ └──────────┬────────────┘ (19, 54) │
│ ▼ │
│ logits: (19, 54) ← 每个解码位置对 54 个 token 的得分 │
└─────────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 四、损失计算(Loss) L = cross_entropy(logits, tgt) │
│ │
│ logits (19, 54) ──→ Softmax ──→ P (19, 54) │
│ P[位置p, token j] = exp(logit_j) / Σ_k exp(logit_k) │
│ │
│ L = (1/19) Σ_p -log(P[p, tgt_p]) │
│ │
│ 初始: L ≈ 3.89 (uniform over 54 tokens ≈ ln(54)) │
│ 训练后: L → 0.41 (过拟合), 验证损失 → 7.25 (严重过拟合) │
└─────────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 五、反向传播(Backpropagation) 链式法则: ∂L/∂θ = ∂L/∂y · ∂y/∂θ│
│ │
│ 梯度从 Loss 出发,反向穿过每条路径: │
│ │
│ ∂L/∂logits (19, 54) ← 第一步:dL/d(logit_j) = P_j - 𝟙(j==target) │
│ │ │
│ ▼ │
│ ∂L/∂W_out (54, 32) ∂L/∂D (19, 32) ← 同时计算 │
│ │ │ │
│ │ ┌───────────┼───────────┐ │
│ │ ▼ ▼ ▼ │
│ │ ∂L/∂W_FFN2 ∂L/∂CrossAttn ∂L/∂SelfAttn │
│ │ ∂L/∂W_FFN1 │
│ │ ▼ │
│ │ ∂L/∂X_cross ──→ 梯度回传至编码器 │
│ │ ▼ │
│ │ ∂L/∂W_V_cross ∂L/∂W_K_cross ∂L/∂W_Q_cross │
│ │ ▼ │
│ │ ∂L/∂X_self │
│ │ ▼ │
│ │ ∂L/∂W_V_self ∂L/∂W_K_self ∂L/∂W_Q_self ← 自注意力梯度 │
│ │ ▼ │
│ │ ∂L/∂X_enc (8, 32) ← 梯度回传到编码器 │
│ │ ▼ │
│ │ ∂L/∂enc_FFN2 ∂L/∂enc_FFN1 ∂L/∂enc_Attn ∂L/∂Embedding │
│ ▼ │
│ 每个参数 θ 获得梯度 ∂L/∂θ ← 用于下一节的参数更新 │
└─────────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 六、参数更新(SGD / Adam) θ = θ - η · ∂L/∂θ │
│ │
│ 参数组 | 数量 | 典型梯度范数 | 更新后效果 │
│ ─────────────────────────────────────────────────────────── │
│ Embedding (中) | 41,856 | 中等 | 语义向量空间重构 │
│ W_Q / W_K | 各 1,024 | 小→中 | 注意力对齐模式学习 │
│ W_V / W_O | 各 1,024 | 中 | 值空间和输出空间调整 │
│ FFN W1 (32→128) | 4,096 | 中 | 特征升维变换学习 │
│ FFN W2 (128→32) | 4,096 | 大 | 过拟合中增长最快 (+60%) │
│ OutputProj (32→54)| 1,728 | 大 | 词表映射学习 │
│ LayerNorm γ/β | 各 32 | 小 | 缩放/偏移微调 │
│ │
│ Adam 维护一阶/二阶动量矩,自适应调整每个参数的学习率 │
└─────────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 七、迭代循环 重复 1-6 步直至收敛 / 过拟合 │
│ │
│ 第 38 篇实验: 800 句对, 3000 步训练, 损失 4.09→0.41 │
│ → 训练损失降 90%, 验证损失先降后升, 清晰展示过拟合全过程 │
│ → Embedding 从随机→有序, 注意力从均匀→聚焦, 参数范数差异化增长 │
└─────────────────────────────────────────────────────────────────────────────────┘
三、维度追踪:每一处的张量形状
理解 Transformer 信号流最快捷的方式是追踪张量形状的变化。下面给出从原始数据到最终输出的完整维度链。
3.1 整体维度链
以 d_model=32, h=4, d_k=8, batch=1 为例:
原始文本 → token IDs → Embedding → + PE
"我爱深度学习" → (8,) → (1, 8, 32) → (1, 8, 32)
"i love deep learning" → (19,) → (1, 19, 32) → (1, 19, 32)
编码器层 (×3) → 解码器层 (×3) → Output Projection → Logits
(1, 8, 32) → (1, 19, 32) → (1, 19, 54) → (1, 19, 54)
Softmax → Loss (标量) → 反向梯度 → 参数更新
(1, 19, 54) → ( ) → 每参数与形状一致 → 所有参数形状不变
3.2 编码器逐操作维度表
| 操作 | 输入形状 | 输出形状 | 关键变换 |
|---|---|---|---|
| Embedding lookup | (1, S) | (1, S, 32) | token id → 32维向量 |
| × √d_model | (1, S, 32) | (1, S, 32) | 缩放至标准方差 |
| + PE (S, 32) | (1, S, 32) | (1, S, 32) | 叠加位置编码 |
| W_Q linear | (1, S, 32) | (1, S, 32) | Q 投影 |
| Reshape for heads | (1, S, 32) | (1, 4, S, 8) | 拆分为 4 个头 |
| Q × K^T / √8 | (1, 4, S, 8) × (1, 4, 8, S) | (1, 4, S, S) | 注意力得分 |
| Softmax | (1, 4, S, S) | (1, 4, S, S) | 归一化为权重 |
| Weights × V | (1, 4, S, S) × (1, 4, S, 8) | (1, 4, S, 8) | 加权上下文 |
| Merge heads | (1, 4, S, 8) | (1, S, 32) | 4头拼接 |
| W_O linear | (1, S, 32) | (1, S, 32) | 输出投影 |
| Residual + LN | (1, S, 32) | (1, S, 32) | 残差连接 + 归一化 |
| FFN W1 (32→128) | (1, S, 32) | (1, S, 128) | 升维 |
| ReLU | (1, S, 128) | (1, S, 128) | 非线性(~46% 置零) |
| FFN W2 (128→32) | (1, S, 128) | (1, S, 32) | 降维 |
| Residual + LN | (1, S, 32) | (1, S, 32) | 每层输出范数 ≈ √32 |
S = 源序列长度(本例中为 8)
3.3 解码器逐操作维度表
| 操作 | 输入形状 | 输出形状 | 关键变换 |
|---|---|---|---|
| Embedding + PE | (1, T) | (1, T, 32) | 与编码器相同 |
| 因果自注意力 | |||
| Q/K/V 投影 | (1, T, 32) → 3×(1, T, 32) | (1, 4, T, 8) | 三分量独立投影 |
| Scores (因果掩码) | (1, 4, T, T) | (1, 4, T, T) | 上三角 = -inf |
| Softmax + × V | (1, 4, T, T) → (1, 4, T, 8) | (1, T, 32) | 合并输出 |
| 交叉注意力 | |||
| Q 投影(来自解码器) | (1, T, 32) | (1, 4, T, 8) | --- |
| K 投影(来自编码器) | (1, S, 32) | (1, 4, S, 8) | 形状不同! |
| V 投影(来自编码器) | (1, S, 32) | (1, 4, S, 8) | 形状不同! |
| Scores = Q·K^T/√8 | (1, 4, T, 8) × (1, 4, 8, S) | (1, 4, T, S) | 矩形矩阵 |
| Softmax + × V | (1, 4, T, S) × (1, 4, S, 8) | (1, T, 32) | 融合源语言信息 |
| Residual + LN | (1, T, 32) | (1, T, 32) | --- |
| FFN | (1, T, 32) | (1, T, 32) | 32→128→32 |
| Output Projection | (1, T, 32) | (1, T, 54) | 32→54 (词表大小) |
T = 目标序列长度(本例中为 19),S = 源序列长度(本例中为 8)
3.4 维度对比速查表
以下是一组核心对比,帮助理解不同组件的维度行为:
| 对比项 | 编码器自注意力 | 解码器因果自注意力 | 解码器交叉注意力 |
|---|---|---|---|
| Q 来源 | 编码器自身 | 解码器自身 | 解码器(目标语言) |
| K,V 来源 | 编码器自身 | 解码器自身 | 编码器输出(源语言) |
| 得分矩阵形状 | (4, S, S) 方形 | (4, T, T) 方形 | (4, T, S) 矩形 |
| 掩码类型 | padding mask | 因果掩码(上三角) | 无特殊掩码 |
| 矩阵是否有对角线 | 有(自注意力) | 有(自注意力) | 无对角线 |
一个关键洞察 :交叉注意力的得分矩阵形状为 (T, S) 而非 (S, T) 或 (T, T)。这意味着目标语言的每个 token 都会对源语言的所有 token 计算一个注意力分布,这是"序列到序列"对齐的核心机制。
四、前向传播全景
本节将 Transformer 前向传播的所有数学运算汇总到一个统一的框架中。
4.1 总览:前向传播的数学链
输入 x ← 离散 token 序列
│
嵌入 + 缩放 ← x = Embedding(src) · √d_model
│
+ 位置编码 ← x = x + PE(pos)
│
编码器 × N ← x = EncoderLayer(x)
│ ├── Self-Attention: x = LN(x + Attn(x, x, x))
│ └── FFN: x = LN(x + FFN(x))
│
enc_output ← 编码器的最终输出
│
解码器 × N ← x = DecoderLayer(x, enc_output)
│ ├── Masked Self-Attn: x = LN(x + Attn(x, x, x, causal))
│ ├── Cross-Attn: x = LN(x + Attn(x, enc, enc))
│ └── FFN: x = LN(x + FFN(x))
│
输出投影 ← logits = x · W_out^T + b_out
│
Softmax ← P = softmax(logits)
│
损失 ← L = -log(P[tgt])
4.2 各组件数学公式全集
位置编码
PE(pos,2i)=sin(pos100002i/dmodel),PE(pos,2i+1)=cos(pos100002i/dmodel)\text{PE}(pos, 2i) = \sin\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right), \quad \text{PE}(pos, 2i+1) = \cos\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right)PE(pos,2i)=sin(100002i/dmodelpos),PE(pos,2i+1)=cos(100002i/dmodelpos)
- 低维高频(编码相对位置),高维低频(编码绝对位置)
- 与 embedding 相加而非拼接,语义+位置在同一空间
- 对应文章:第 36 篇第三节
多头自注意力
MultiHead(Q,K,V)=WO⋅Concat(head1,...,headh)\text{MultiHead}(Q, K, V) = W_O \cdot \text{Concat}(\text{head}_1, ..., \text{head}_h)MultiHead(Q,K,V)=WO⋅Concat(head1,...,headh)
headi=softmax(QiKi⊤dk+M)Vi\text{head}_i = \text{softmax}\left(\frac{Q_i K_i^\top}{\sqrt{d_k}} + M\right) V_iheadi=softmax(dk QiKi⊤+M)Vi
其中 Qi=WQ(i)QQ_i = W_Q^{(i)} QQi=WQ(i)Q, Ki=WK(i)KK_i = W_K^{(i)} KKi=WK(i)K, Vi=WV(i)VV_i = W_V^{(i)} VVi=WV(i)V
- dk=dmodel/hd_k = d_{\text{model}} / hdk=dmodel/h
- MMM 是掩码矩阵(padding mask 或因果掩码)
- 对应文章:第 36 篇第四节
前馈网络(FFN)
FFN(x)=W2⋅max(0,W1x+b1)+b2\text{FFN}(x) = W_2 \cdot \max(0, W_1 x + b_1) + b_2FFN(x)=W2⋅max(0,W1x+b1)+b2
- 升维:dmodel→dffd_{\text{model}} \to d_{ff}dmodel→dff(通常 dff=4×dmodeld_{ff} = 4 \times d_{\text{model}}dff=4×dmodel)
- ReLU 非线性:max(0,⋅)\max(0, \cdot)max(0,⋅)
- 降维:dff→dmodeld_{ff} \to d_{\text{model}}dff→dmodel
- 对应文章:第 36 篇第五节
残差连接 + LayerNorm
output=LN(x+Sublayer(x))\text{output} = \text{LN}(x + \text{Sublayer}(x))output=LN(x+Sublayer(x))
LN(x)=γ⋅x−μσ+ϵ+β,μ=1d∑i=1dxi,σ2=1d∑i=1d(xi−μ)2\text{LN}(x) = \gamma \cdot \frac{x - \mu}{\sigma + \epsilon} + \beta, \quad \mu = \frac{1}{d}\sum_{i=1}^d x_i, \quad \sigma^2 = \frac{1}{d}\sum_{i=1}^d (x_i - \mu)^2LN(x)=γ⋅σ+ϵx−μ+β,μ=d1i=1∑dxi,σ2=d1i=1∑d(xi−μ)2
- 残差连接确保梯度可直接回传绕过子层
- LN 确保输出范数稳定(≈ √d_model),防止深层网络数值爆炸
- 对应文章:第 36 篇第六节
交叉注意力
与自注意力公式完全相同,区别仅在于 Q、K、V 的来源:
CrossAttn(Qdec,Kenc,Venc)=softmax(QdecKenc⊤dk)Venc\text{CrossAttn}(Q_{\text{dec}}, K_{\text{enc}}, V_{\text{enc}}) = \text{softmax}\left(\frac{Q_{\text{dec}} K_{\text{enc}}^\top}{\sqrt{d_k}}\right) V_{\text{enc}}CrossAttn(Qdec,Kenc,Venc)=softmax(dk QdecKenc⊤)Venc
- Q 形状:(T, d_k),K/V 形状:(S, d_k),得分矩阵形状:(T, S)
- 不使用因果掩码------解码器每个位置可关注编码器所有位置
- 对应文章:第 37 篇第四节
输出投影层
Logits=D⋅Wproj⊤+bproj\text{Logits} = D \cdot W_{\text{proj}}^\top + b_{\text{proj}}Logits=D⋅Wproj⊤+bproj
- D∈RT×dmodelD \in \mathbb{R}^{T \times d_{\text{model}}}D∈RT×dmodel:解码器输出
- Wproj∈Rvocab×dmodelW_{\text{proj}} \in \mathbb{R}^{\text{vocab} \times d_{\text{model}}}Wproj∈Rvocab×dmodel:投影矩阵
- 将 d_model 维向量映射到词表大小,每个 token 获得一个 logit 分数
Softmax 与交叉熵
Pp[j]=exp(Logits[p,j])∑k=1vocabexp(Logits[p,k])P_p[j] = \frac{\exp(\text{Logits}[p, j])}{\sum_{k=1}^{\text{vocab}} \exp(\text{Logits}[p, k])}Pp[j]=∑k=1vocabexp(Logits[p,k])exp(Logits[p,j])
L=1T∑p=1T−log(Pp[tp])\mathcal{L} = \frac{1}{T} \sum_{p=1}^T -\log(P_p[t_p])L=T1p=1∑T−log(Pp[tp])
其中 tpt_ptp 是位置 ppp 的目标 token ID。
- 对应文章:第 35 篇第二节
五、反向传播全景
反向传播是 Transformer 训练的"隐藏一半"------前向传播告诉我们模型输出了什么,反向传播告诉我们应该如何调整参数。
5.1 核心公式:损失对 Logits 的梯度
反向传播的第一步是计算损失对 logits 的梯度。这是整个梯度流的源头:
∂L∂Logits[p,j]=1T(Pp[j]−1[tp=j])\frac{\partial \mathcal{L}}{\partial \text{Logits}[p, j]} = \frac{1}{T} \left( P_p[j] - \mathbb{1}[t_p = j] \right)∂Logits[p,j]∂L=T1(Pp[j]−1[tp=j])
这个公式的意义非常直观:
- 当目标 token 为 j 时:∂L∂Logits[p,j]=1T(Pp[j]−1)<0\frac{\partial \mathcal{L}}{\partial \text{Logits}[p, j]} = \frac{1}{T}(P_p[j] - 1) < 0∂Logits[p,j]∂L=T1(Pp[j]−1)<0 → 需要提高这个 logit
- 当目标 token 不为 j 时:∂L∂Logits[p,j]=1TPp[j]>0\frac{\partial \mathcal{L}}{\partial \text{Logits}[p, j]} = \frac{1}{T} P_p[j] > 0∂Logits[p,j]∂L=T1Pp[j]>0 → 需要降低这个 logit
5.2 输出投影层梯度
∂L∂Wproj[j,k]=1T∑p=1T(Pp[j]−1[tp=j])⋅D[p,k]\frac{\partial \mathcal{L}}{\partial W_{\text{proj}}[j, k]} = \frac{1}{T} \sum_{p=1}^T \left( P_p[j] - \mathbb{1}[t_p = j] \right) \cdot D[p, k]∂Wproj[j,k]∂L=T1p=1∑T(Pp[j]−1[tp=j])⋅D[p,k]
这是整个反向传播中唯一需要"从头计算"的梯度------其余所有梯度都通过链式法则从这层向后传递。
对应文章:第 35 篇第三节(完整手算过程)
5.3 梯度回传路径全景
Loss (标量) ← 交叉熵
│
│ 公式: ∂L/∂Logit[j] = P[j] - 𝟙(j==target)
▼
Logits (T, vocab)
│
├──→ ∂L/∂W_proj (vocab, d_model) ← 直接计算(见 5.2 节)
│ ↓
└──→ ∂L/∂D (T, d_model) ← 沿输出投影层回传
│
│ 公式: ∂L/∂D[p,k] = Σ_j (∂L/∂Logit[p,j]) · W_proj[j,k]
│
▼
Decoder Layer 2 ← 3层解码器 → Decoder Layer 0
│
├──→ FFN梯度
│ ∂L/∂W_2 = (relu(W_1 x))^T · ∂L/∂y ← 降维层
│ ∂L/∂W_1 = x^T · (relu'(W_1 x) ⊙ (∂L/∂y · W_2^T)) ← 升维层
│
├──→ 交叉注意力梯度
│ ∂L/∂Q = (∂L/∂S) · K / √d_k ← Q 梯度
│ ∂L/∂K = (∂L/∂S)^T · Q / √d_k ← K 梯度
│ ∂L/∂V = Attn^T · ∂L/∂Context ← V 梯度
│ 其中 ∂L/∂S = ∂L/∂Attn · V^T ⊙ softmax'(S)
│ (S = QK^T / √d_k 为得分矩阵)
│
├──→ 因果自注意力梯度(公式同上,但 Q=K=V)
│
▼
∂L/∂X_enc (T, d_model) ← 梯度跨过编码器-解码器边界
│
▼
Encoder Layer 2 → ... → Encoder Layer 0
│
├──→ 自注意力梯度(公式同上)
│
├──→ FFN梯度(公式同上)
│
└──→ ∂L/∂Embedding (vocab, d_model) ← 词嵌入梯度
│
▼
∂L/∂Embedding[token_id, :] = Σ_p ∂L/∂X_enc[p, :] ← 只有被用到的 token 有梯度
5.4 交叉熵→Softmax梯度:以第35篇数值为例
第 35 篇的手算展示了梯度的一个关键特性------梯度竞争:
以 token "i"(WprojW_{\text{proj}}Wproj 的第 3 行)为例,其梯度为三个位置上梯度的加权和:
| 位置 p | δp[i]\delta_p[i]δp[i] | D[p,0] | D[p,1] | D[p,2] | D[p,3] | 含义 |
|---|---|---|---|---|---|---|
| 0 | -0.804 (目标,需提高) | 1.0 | 0.0 | 1.0 | 0.0 | 位置 0 需要更多 "i" |
| 1 | +0.134 (非目标,需降低) | 0.0 | 1.0 | 0.0 | 1.0 | 位置 1 需要更少 "i" |
| 2 | +0.151 (非目标,需降低) | 1.0 | 1.0 | 0.0 | 0.0 | 位置 2 需要更少 "i" |
最终梯度:
- ∂L/∂W[3,0] = (-0.804×1.0 + 0.134×0.0 + 0.151×1.0) / 3 = -0.218 → 增加
- ∂L/∂W[3,1] = (-0.804×0.0 + 0.134×1.0 + 0.151×1.0) / 3 = +0.095 → 减小
梯度竞争:同一个 token 的权重在不同位置受到方向相反的拉力------位置 0 让"i"变高,位置 1、2 让"i"变低。这是多任务学习的微观体现。
六、参数更新全景
有了梯度,参数就沿负梯度方向更新。本文汇总所有参数组的更新特征。
6.1 更新公式
SGD :
θt+1=θt−η⋅∂L∂θt\theta_{t+1} = \theta_t - \eta \cdot \frac{\partial \mathcal{L}}{\partial \theta_t}θt+1=θt−η⋅∂θt∂L
Adam (实际使用的优化器):
mt=β1mt−1+(1−β1)gtm_t = \beta_1 m_{t-1} + (1-\beta_1) g_tmt=β1mt−1+(1−β1)gt
vt=β2vt−1+(1−β2)gt2v_t = \beta_2 v_{t-1} + (1-\beta_2) g_t^2vt=β2vt−1+(1−β2)gt2
m^t=mt/(1−β1t),v^t=vt/(1−β2t)\hat{m}_t = m_t / (1-\beta_1^t), \quad \hat{v}t = v_t / (1-\beta_2^t)m^t=mt/(1−β1t),v^t=vt/(1−β2t)
θt+1=θt−η⋅m^tv^t+ϵ\theta{t+1} = \theta_t - \eta \cdot \frac{\hat{m}_t}{\sqrt{\hat{v}_t} + \epsilon}θt+1=θt−η⋅v^t +ϵm^t
Adam 为每个参数自适应调整学习率------梯度变化剧烈的参数获得较小步长,梯度平缓的参数获得较大步长。
6.2 参数组更新特征(第 38 篇 3000 步训练数据)
| 参数组 | 数量 | 初始范数 | 最终范数 | 增长 | 训练中的作用 |
|---|---|---|---|---|---|
| Embedding (中) | 41,856 | --- | --- | --- | 构建语义向量空间 |
| W_Q (单层) | 1,024 | 3.22 | 4.10 | +27% | 学习如何"提问" |
| W_K (单层) | 1,024 | 3.26 | 4.12 | +27% | 学习如何"应答" |
| W_V (单层) | 1,024 | 3.32 | 4.25 | +28% | 学习提取"值"信息 |
| W_O (单层) | 1,024 | 3.34 | 4.30 | +29% | 学习合并多头输出 |
| FFN W1 | 4,096 | 6.60 | 8.24 | +25% | 高维特征提取 |
| FFN W2 | 4,096 | 3.25 | 5.20 | +60% | 增长最快------过拟合中记忆训练模式 |
| OutputProj | 1,728 | --- | --- | --- | 词表映射 |
| LN γ/β | 各 32 | 1.0/0.0 | --- | --- | 缩放/偏移微调 |
6.3 学习阶段转移:从注意力到 FFN
第 38 篇最重要的发现之一是学习重点的阶段转移:
| 训练阶段 | 步数 | 增长最快的参数 | 含义 |
|---|---|---|---|
| 早期(欠拟合) | 0-125 | W_Q, W_K | 注意力机制先被优化 |
| 后期(过拟合) | 500-3000 | W_2(FFN输出) | FFN开始记忆训练模式 |
这个转移的数学解释:
- 早期梯度通过注意力路径(W_Q, W_K)回传效率最高------调整注意力就能快速降低损失
- 后期损失主要由 token 级别的预测错误驱动------调整 FFN 输出投影(W2)能更精确地"记住"每个输入对应的输出
七、系列文章对照索引
下面是本专栏第 23-38 篇共 16 篇文章的对照索引,按功能维度组织。读者可根据需要快速跳转。
7.1 按组件索引
| 组件 | 数学原理 | 数值实例 | 代码实战 |
|---|---|---|---|
| 位置编码 | 第 24 篇 | 第 33 篇 2.1 节 | 第 36 篇第三节 |
| 自注意力 | 第 25 篇 | 第 33 篇 2.2 节 | 第 36 篇第四节 |
| 多头注意力 | 第 26 篇 | --- | 第 36 篇第四节 |
| 前馈网络 FFN | 第 31 篇 | 第 33 篇 2.3 节 | 第 36 篇第五节 |
| 层归一化 LN | 第 30 篇 | --- | 第 36 篇第六节 |
| 残差连接 | 第 30 篇 | --- | 第 36 篇第六节 |
| 交叉注意力 | --- | 第 28 篇 | 第 37 篇第四节 |
| 因果掩码 | 第 32 篇 | --- | 第 37 篇第三节 |
| 输出投影层 | --- | 第 35 篇第二节 | 第 37 篇第五节 |
7.2 按文章索引
| 文章 | 标题 | 核心内容 | 前置依赖 |
|---|---|---|---|
| 24 | 位置编码 | 正弦-余弦编码原理 | --- |
| 25 | 缩放点积注意力 | Attention(Q,K,V) 公式 | --- |
| 26 | 多头注意力 | 并行注意力头机制 | 第 26 篇 |
| 20 | 层归一化 | LN 公式与性质 | --- |
| 30 | 残差连接 | 梯度高速公路 | --- |
| 31 | 前馈网络 | 升维→ReLU→降维 | --- |
| 32 | 因果掩码 | 自回归生成中的掩码 | 第 26 篇 |
| 33 | 编码器前向数值 | 手算"我爱深"过编码器 | 25-30 |
| 34 | 解码器前向数值 | 手算解码器 + 交叉注意力 | 25-31 |
| 35 | 训练流程数值 | 前向→损失→反向→更新→再前向(一篇顶五篇) | 23-34 |
| 36 | 编码器代码实战 | PyTorch 实现 + 逐步骤手算验证 | 33 |
| 37 | 解码器代码实战 | 因果掩码 + 交叉注意力 + 自回归推理 | 34, 36 |
| 38 | 训练代码实战 | 完整训练 + 3000 步参数演化分析 | 35, 36, 37 |
加粗的 33-38 篇构成了"从手算数值到代码实现"的完整实践闭环。
7.3 "三部曲"内部对照
第 36-38 篇构成"代码实战三部曲",其内部关系可以概括为:
| 第 36 篇(编码器) | 第 37 篇(解码器) | 第 38 篇(训练) | |
|---|---|---|---|
| 核心问题 | 编码器如何将字符序列编码为上下文向量? | 解码器如何利用编码器输出逐步生成目标序列? | 参数如何从随机走向有序? |
| 模型状态 | 随机初始化 | 随机初始化 | 训练中(3000 步) |
| 关键验证 | 手算 vs 代码(误差 10^{-8}) | 因果掩码前后对比 | 8 个检查点纵向对比 |
| 注意力特点 | 4 头自注意力,初现分化 | 因果自注意力 + 交叉注意力 | 注意力聚焦度持续变化 |
| 输出形式 | enc_output (8, 32) | 随机字符序列 | "itisnevero"(有进步但未收敛) |
| 与下一篇的关系 | 输出作为第 37 篇 K,V | 引出训练需求 | 三部曲终章 |
八、数学原理速查索引
本节以"一句话公式 + 一句话直觉"的方式,汇总核心数学概念,作为快速参考。
注意力机制
| 概念 | 公式 | 一句话直觉 |
|---|---|---|
| 缩放点积注意力 | Attn(Q,K,V)=softmax(QK⊤dk)V\text{Attn}(Q,K,V) = \text{softmax}(\frac{QK^\top}{\sqrt{d_k}})VAttn(Q,K,V)=softmax(dk QK⊤)V | 用 Q 和 K 的相似度做权重,加权求和 V |
| 多头注意力 | MultiHead=WO⋅Concat(head1,...,headh)\text{MultiHead} = W_O \cdot \text{Concat}(\text{head}_1, ..., \text{head}_h)MultiHead=WO⋅Concat(head1,...,headh) | 多组 QKV 并行,获取不同视角 |
| 因果掩码 | Mij=0 (i≥j), −∞ (i<j)M_{ij} = 0\ (i \ge j),\ -\infty\ (i < j)Mij=0 (i≥j), −∞ (i<j) | 预测第 t 步时不能看到第 t+1 步及以后 |
| 交叉注意力 | QdecKenc⊤Q_{\text{dec}} K_{\text{enc}}^\topQdecKenc⊤ | 目标语言向源语言"提问" |
前馈与归一化
| 概念 | 公式 | 一句话直觉 |
|---|---|---|
| FFN | FFN(x)=W2⋅max(0,W1x+b1)+b2\text{FFN}(x) = W_2 \cdot \max(0, W_1 x + b_1) + b_2FFN(x)=W2⋅max(0,W1x+b1)+b2 | 每个位置独立地升维提取特征再降维 |
| LayerNorm | LN(x)=γ⋅x−μσ+β\text{LN}(x) = \gamma \cdot \frac{x-\mu}{\sigma} + \betaLN(x)=γ⋅σx−μ+β | 在每个样本内部做标准化 |
| 残差连接 | x+Sublayer(x)x + \text{Sublayer}(x)x+Sublayer(x) | 梯度可以从旁路直接通过,防止深层退化 |
位置编码
| 概念 | 公式 | 一句话直觉 |
|---|---|---|
| 正余弦 PE | PE(pos,2i)=sin(pos100002i/d)\text{PE}(pos,2i)=\sin(\frac{pos}{10000^{2i/d}})PE(pos,2i)=sin(100002i/dpos), PE(pos,2i+1)=cos(pos100002i/d)\text{PE}(pos,2i+1)=\cos(\frac{pos}{10000^{2i/d}})PE(pos,2i+1)=cos(100002i/dpos) | 低维高频编码相对位置,高维低频编码绝对位置 |
训练相关
| 概念 | 公式 | 一句话直觉 |
|---|---|---|
| 交叉熵损失 | L=−1T∑plogPp[tp]\mathcal{L} = -\frac{1}{T}\sum_p \log P_p[t_p]L=−T1∑plogPp[tp] | 让正确 token 的概率最大化 |
| Softmax | Pj=ezj∑kezkP_j = \frac{e^{z_j}}{\sum_k e^{z_k}}Pj=∑kezkezj | 将 logits 转换为概率分布(和为 1) |
| 链式法则 | ∂L∂θ=∂L∂y⋅∂y∂θ\frac{\partial \mathcal{L}}{\partial \theta} = \frac{\partial \mathcal{L}}{\partial y} \cdot \frac{\partial y}{\partial \theta}∂θ∂L=∂y∂L⋅∂θ∂y | 梯度沿计算图逐层回传 |
| SGD | θt+1=θt−ηgt\theta_{t+1} = \theta_t - \eta g_tθt+1=θt−ηgt | 沿最陡的下坡方向走一小步 |
| Adam | θt+1=θt−ηmtvt+ϵ\theta_{t+1} = \theta_t - \eta \frac{m_t}{\sqrt{v_t}+\epsilon}θt+1=θt−ηvt +ϵmt | 自适应步长------陡峭处慢走,平坦处快走 |
九、总结:三部曲的闭环
9.1 从松散组件到完整系统的认知路径
回顾本专栏从第 23 篇到第 39 篇的旅程,我们经历了四层认知跃迁:
第 1 层:概念 第 23-32 篇
└── 逐个学习 Transformer 组件的数学原理
(注意力、FFN、LN、Adam 等 10 个独立概念)
第 2 层:数值 第 33-35 篇
└── 用极小型模型(d_model=4)手算每个组件的输入输出
(从 10 个概念到 3 个完整流程:编码器→解码器→训练)
第 3 层:代码 第 36-38 篇
└── 用 PyTorch 实现完整 Transformer(d_model=32)
(从手算到代码,逐步骤验证,13 万参数训练实战)
第 4 层:全景 ← 本文 第 39 篇
└── 将全部碎片拼接为一张大图
(展示一条数据从进入系统到驱动参数更新的完整信号流)
9.2 "随机到有序"的数学本质
第 38 篇用 3000 步训练直观展示了参数从随机走向有序。现在,站在全景视角,我们可以给出更本质的数学描述:
训练前 :参数 θ\thetaθ 服从随机分布(均匀或正态),模型输出基本是噪声,损失接近 ln(vocab)\ln(\text{vocab})ln(vocab)。
训练后 :参数 θ\thetaθ 收敛到损失曲面的某个局部极小点附近,各参数组呈现出差异化的增长模式:
- Embedding 矩阵:从各向同性 → 语义聚类(t-SNE 可视化验证)
- 注意力投影:WQ/WK 增长 27%,注意力分布从均匀 → 聚焦(KL 散度增加)
- FFN 投影:W2 增长 60%,成为最大变动参数组(过拟合阶段主导)
- 输出投影:正确 token 的 logit 从淹没在噪声中 → 显著高于其他
用数学语言说:训练将参数从高熵的随机状态驱动到了低熵的有序状态 ,使得模型的条件概率分布 P(y∣x;θ)P(y|x;\theta)P(y∣x;θ) 逼近训练数据 Pdata(y∣x)P_{\text{data}}(y|x)Pdata(y∣x)。
9.3 信号流全景:一个统一的视角
如果只从本文带走一个概念,应该是这个统一的信号流视角:
数据 ──→ 嵌入 ──→ 编码器(S→S) ──→ 解码器(S→T) ──→ 投影 ──→ 损失 ──→ 梯度 ──→ 更新
↑ │
└──────────────────────────── 循环迭代 ──────────────────────────────────────────┘
每个箭头都是一组数学运算,每个方块都是一个可微模块,整个系统由链式法则无缝连接。无论 Transformer 的规模如何增大(d_model=32 或 512 或 1024),这个信号流框架完全不变:
| 操作 | 小模型(d_model=32) | 大模型(d_model=512) | 是否变化 |
|---|---|---|---|
| Embedding | (V, 32) | (V, 512) | 维度放大 |
| 注意力 | Q·K^T / √8 | Q·K^T / √64 | 缩放因子变化 |
| FFN | 32→128→32 | 512→2048→512 | 维度放大 |
| 残差LN | 范数 = √32 ≈ 5.66 | 范数 = √512 ≈ 22.63 | 数值变大 |
| 梯度公式 | 1T(P−1)\frac{1}{T}(P - \mathbb{1})T1(P−1) | 完全相同 | 不变 |
| SGD/Adam | θ←θ−η∇\theta \leftarrow \theta - \eta \nablaθ←θ−η∇ | 完全相同 | 不变 |
这就是数学抽象的威力:理解小模型的完整信号流,就理解了大模型的一切本质。