跳到主要内容

FFN、Normalization 与位置编码

Transformer Block 的三大核心组件:FFN 承担主要计算,Norm 稳定训练,Position 注入序列信息

前置知识

核心概念

Transformer Block 内部结构

现代 LLM(Llama 3/4、Qwen2/3、Claude 4)采用 Pre-Norm + SwiGLU + RoPE 的组合。

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 更好?

  • Gating 机制让 FFN 可以动态选择"哪些特征通过",类似轻量级的 MoE
  • SiLU 比 ReLU 平滑,梯度不会在负半区截断
  • 实验表明 SwiGLU 比传统 FFN 在同等参数量下质量高 ~1-2%
  • 代价:参数量增加 50%(3 个投影矩阵 vs 2 个),计算量也增加

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-NormPost-Norm
Norm 位置Sub-layer 之前Sub-layer 之后(残差之后)
训练稳定性:梯度有直接路径差:深层网络梯度消失
表示能力略弱
是否需要 Warmup不需要(或很短)需要(学习率 warmup 防止早期震荡)
现代 LLM 选择✅ 主流(Llama, Qwen, GPT-3)❌ 少用

为什么 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 的优势:
- 少一次减均值操作,计算量减少 ~7%
- 少一个可学习参数 β
- 实验表明质量与 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旋转矩阵,角度与位置相关(NTK/YaRN scaling)Llama, Qwen, DeepSeek
ALiBiAttention 分数加线性偏置好(天然外推)极低MPT, GLM
Absolute可学习的位置向量差(超过训练长度失效)极低BERT, GPT-2
Sinusoidal正弦函数编码中等极低原始 Transformer

为什么 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 × num_layers)
≈ 4096 × 4096 × 11008 × 32 ≈ 5,897 GFLOPs

FFN 占 Prefill 计算量的 ~99%!

Decode 阶段(每步 seq_len 增长 1):
Attention: O(seq_len × num_heads × head_dim) — 随 seq_len 线性
FFN: O(d_model × d_ff × num_layers) — 常数

结论:
- Prefill 优化重点: FFN(占 ~99% FLOPs)
- Decode 优化重点: 权重加载速度(memory-bound)
- FFN 量化对 Prefill 加速效果最明显

Pre-Norm 对推理的影响

  • Pre-Norm 在推理时不需要保存 Norm 的中间结果,显存友好
  • Post-Norm 需要在 Norm 之前保存残差,增加中间激活
  • 推理框架对 Pre-Norm 的支持更成熟

常见问题排查

症状原因解决
长上下文质量下降RoPE 外推不足启用 YaRN / NTK-aware scaling
训练 loss 不下降可能是 Post-Norm 不稳定检查 warmup 设置,或切换 Pre-Norm
FFN 显存占用异常d_ff 扩展比过大检查模型配置,有些模型 d_ff 是 d_model 的 8 倍
RoPE 导致 NaN位置超出旋转表范围检查 seq_len 配置是否超过 RoPE 支持的最大值

面试视角

面试官会怎么问

Q1: "为什么 Transformer 中 FFN 层的参数占大多数?大概占多少?"

满分回答:

  • FFN 的中间维度 d_ff 通常是 d_model 的 2-4 倍(SwiGLU 有 3 个矩阵)
  • Attention 的维度是 d_model × d_model
  • FFN 是 d_model × d_ff × 3,d_ff ≈ 3-4 × d_model
  • 所以 FFN 参数 ≈ 3 × d_model × d_ff,Attention 参数 ≈ 4 × d_model × d_model
  • FFN 占比 ≈ (3 × d_ff) / (3 × d_ff + 4 × d_model) ≈ 2/3 到 3/4

Q2: "Pre-Norm 和 Post-Norm 的区别是什么?为什么现代 LLM 用 Pre-Norm?"

满分回答:

  • Pre-Norm:Norm 在 Sub-layer 之前,x + SubLayer(Norm(x))
  • Post-Norm:Norm 在残差之后,Norm(x + SubLayer(x))
  • Pre-Norm 梯度有直接路径,深层训练稳定,不需要 warmup
  • Post-Norm 深层训练容易梯度消失,需要 warmup
  • 现代 LLM 都用 Pre-Norm 因为训练稳定性更重要

Q3: "RoPE 是怎么工作的?为什么它能支持外推?"

满分回答:

  • RoPE 用旋转矩阵将位置信息注入到 Q 和 K 中
  • 旋转角度 θ 与 token 位置 m 相关
  • 点积只依赖相对位置 (m-n),所以是相对位置编码
  • 外推时通过 NTK-aware scaling 调整基频,使高频分量不变
  • 可以从训练 4K 外推到 32K-128K

Q4: "SwiGLU 比传统 FFN 多了什么?代价是什么?"

满分回答:

  • 多了 Gate 分支(W_gate)和 SiLU 激活
  • 从 2 个矩阵变为 3 个矩阵(W_gate, W_up, W_down)
  • 参数量增加 50%,计算量也增加
  • 但 gating 机制让 FFN 可以选择性地传递信息
  • 质量提升 ~1-2%,在 scaling 趋势下值得这个代价

Q5: "RMSNorm 和 LayerNorm 的区别?"

满分回答:

  • LayerNorm 做均值归一化 + 方差归一化,有 γ 和 β 两个参数
  • RMSNorm 只做均方根归一化,只有 γ 一个参数
  • RMSNorm 计算量减少 ~7%,参数少一个
  • 质量几乎无差异,所以 Llama/Qwen 都用 RMSNorm

对比分析

FFN 变体对比

变体激活函数矩阵数参数倍数质量代表模型
FFN (ReLU)ReLU21x基准GPT-2
GeGLUGELU31.5x+1%PaLM
SwiGLUSiLU31.5x+1-2%Llama, Qwen
GLUSigmoid31.5x+0.5%早期实验

Position Encoding 外推能力对比

方案2x 训练长度4x 训练长度8x 训练长度需要额外技巧
Absolute质量崩溃完全不可用-需要 fine-tune
Sinusoidal可用质量下降明显-需要插值
ALiBi可用可用可用不需要
RoPE可用可用可用 + scalingNTK-aware / YaRN

最佳实践

调参建议

  • RoPE scaling:部署时如果 seq_len 超过训练长度,必须启用 scaling(YaRN 或 NTK-aware)
  • FFN 量化:Prefill 阶段 99% 的 FLOPs 在 FFN,FFN 量化对首 token 延迟优化效果最明显
  • block_size 选择:RoPE 的基频(base_frequency)默认 10000,长上下文场景可增大到 1000000(NTK-aware)

避坑指南

  • RoPE 外推超过 8x 训练长度时质量会明显下降,不要盲目拉长上下文
  • SwiGLU 的 3 个矩阵在权重量化时需要分别处理,不能简单合并
  • Post-Norm 模型在推理时中间激活更大,batch size 上限更低
  • ALiBi 虽然外推性好,但现代模型基本都用 RoPE,部署时注意区分

上一节:KV Cache 详解 下一节:MoE 架构