Transformer Block 的三大核心组件:FFN 承担主要计算,Norm 稳定训练,Position 注入序列信息
前置知识
核心概念
Transformer Block 内部结构
FFN 层详细计算
传统 FFN vs Gated FFN
传统 FFN (GPT-2, BERT): output = (x @ W_up + b_up) @ W_down = ReLU(x @ W_up) @ W_down 参数: 2 × d_model × d_ff
SwiGLU FFN (Llama, Qwen): gate = SiLU(x @ W_gate) # SiLU = x * sigmoid(x) up = x @ W_up output = (gate ⊙ up) @ W_down 参数: 3 × d_model × d_ff (W_gate + W_up + W_down)
GeGLU FFN (PaLM): gate = GELU(x @ W_gate) up = x @ W_up output = (gate ⊙ up) @ W_down 参数: 3 × d_model × d_ff
为什么 SwiGLU 更好?
FFN 参数占比推导
假设: d_model = 4096 d_ff = 11008 (Llama 3 8B 的 FFN 扩展比 ~2.7x) num_heads = 32 head_dim = 128 num_layers = 32
每层参数: Attention: W_q: d_model × (num_heads × head_dim) = 4096 × 4096 = 16.8M W_k: d_model × (num_kv_heads × head_dim) = 4096 × 1024 = 4.2M (GQA-4) W_v: d_model × (num_kv_heads × head_dim) = 4096 × 1024 = 4.2M W_o: (num_heads × head_dim) × d_model = 4096 × 4096 = 16.8M 小计: ~42M
FFN (SwiGLU): W_gate: d_model × d_ff = 4096 × 11008 = 45.1M W_up: d_model × d_ff = 4096 × 11008 = 45.1M W_down: d_ff × d_model = 11008 × 4096 = 45.1M 小计: ~135M
每层总计: ~177M FFN 占比: 135 / 177 ≈ 76%
结论: FFN 参数通常占每层参数的 2/3 到 3/4。 这是因为 d_ff 通常是 d_model 的 2-4 倍(扩展比), 而且 SwiGLU 有 3 个投影矩阵。
Normalization 位置差异
Pre-Norm vs Post-Norm
为什么 Pre-Norm 成为主流?
Pre-Norm 的残差连接中, x 有一条从输入直接到输出的"高速公路"。梯度回传时,即使 Sub-layer 的梯度很小, x 的梯度也能直接传回去(梯度 ≈ 1)。这使得深层网络(50+ 层)可以稳定训练。
Post-Norm 中,梯度必须穿过 Norm 层和 Sub-layer 才能到达前面的层,层数多了以后梯度呈指数衰减。
RMSNorm vs LayerNorm
LayerNorm (原始 Transformer): μ = mean(x) σ² = variance(x) output = (x - μ) / sqrt(σ² + ε) × γ + β 计算: 需要均值和方差,参数: γ + β
RMSNorm (Llama, Qwen): rms = sqrt(mean(x²) + ε) output = (x / rms) × γ 计算: 只需均方根,参数: γ(无 β)
RMSNorm 的优势: - 少一次减均值操作和一个可学习参数 β - 实验表明质量与 LayerNorm 几乎无差异 - 在大规模推理中累积节省可观的计算量
Position Encoding 方案对比
为什么需要位置编码?
Self-Attention 的计算 Q @ K^T 是 token 之间的两两点积,不包含位置信息。 Attention(token_i, token_j) 和 Attention(token_j, token_i) 的分数计算方式完全一样。如果不注入位置信息,模型无法区分 "A B C" 和 "C B A"。
主流方案对比
为什么 RoPE 成为主流?
RoPE (Rotary Position Embedding): 将每个位置映射为一个旋转角度 θ_i Q 和 K 在计算 Attention 前被旋转到对应位置
旋转矩阵: [cos(mθ) -sin(mθ)] [sin(mθ) cos(mθ)]
其中 m 是 token 位置,θ 是基频
关键性质: RoPE(Q_m) · RoPE(K_n) = f(Q, K, m-n) 点积只依赖于相对位置 m-n,而非绝对位置
RoPE 的优势: 1. 相对位置编码:Attention 分数只与 token 间相对距离有关 2. 外推性好:通过 NTK-aware / YaRN scaling 可以外推到训练长度的 8-16 倍 3. 计算开销低:只需要对 Q 和 K 做旋转变换,不增加额外参数 4. 兼容 FlashAttention:旋转在 QK^T 之前做,不影响分块计算
RoPE Scaling 技术: NTK-aware: 通过缩放旋转基频 θ,使外推时高频分量保持不变 - 核心思想:位置编码的频率决定了模型"感知"的相对距离 - 缩小 θ → 降低旋转频率 → 使模型能"看到"更远的距离 YaRN: 将位置分为"内插区"和"外推区"分别处理 - 短距离位置:保持原始编码(内插,模型已学会) - 长距离位置:压缩编码空间(外推,模型没见过) 效果: 训练 4K → 推理 128K(32 倍外推)质量损失 < 5%
部署视角
FFN 计算对部署的影响
Prefill 阶段 FLOPs 分布(seq_len=4096, Llama 3 8B): Attention(单层): O(seq_len^2 × num_heads × head_dim) ≈ 4096^2 × 32 × 128 ≈ 68.7 GFLOPs FFN(单层): O(seq_len × d_model × d_ff) ≈ 4096 × 4096 × 11008 ≈ 184.3 GFLOPs
全模型(×32 层): Attention: 68.7 × 32 ≈ 2,198 GFLOPs FFN: 184.3 × 32 ≈ 5,897 GFLOPs 总计: ~8,095 GFLOPs
FFN 占 Prefill 计算量的 ~73%!
Decode 阶段(每步 seq_len 增长 1): Attention: O(seq_len × num_heads × head_dim) — 随 seq_len 线性 FFN: O(d_model × d_ff × num_layers) — 常数
结论: - Prefill 优化重点: FFN(占 ~73% FLOPs),但长 prompt 时 Attention 占比会上升 - Decode 优化重点: 权重加载速度(memory-bound) - FFN 量化对 Prefill 加速效果最明显
Pre-Norm 对推理的影响
常见问题排查
面试视角
面试官会怎么问
Q1: "为什么 Transformer 中 FFN 层的参数占大多数?大概占多少?"
满分回答:
Q2: "Pre-Norm 和 Post-Norm 的区别是什么?为什么现代 LLM 用 Pre-Norm?"
Q3: "RoPE 是怎么工作的?为什么它能支持外推?"
Q4: "SwiGLU 比传统 FFN 多了什么?代价是什么?"
Q5: "RMSNorm 和 LayerNorm 的区别?"
对比分析
FFN 变体对比
Position Encoding 外推能力对比
最佳实践
调参建议
避坑指南