Normalization 自测题
完成以下题目检验你的理解程度
🎮 交互式自测(推荐)
Q1
梯度消失问题的根本原因是什么?
Q2
RMSNorm 和 LayerNorm 的主要区别是什么?
Q3
Pre-LN 相比 Post-LN 的优势是什么?
Q4
在 MiniMind 的一个 TransformerBlock 中,有几个 RMSNorm?
Q5
为什么 RMSNorm 的 forward 方法中要用 .float() 和 .type_as(x)?
🎯 综合问答题
Q6: 实战问题
假设你在训练一个 16 层的 Transformer,但 loss 在前 100 步就变成了 NaN。可能的原因和解决方案是什么?
点击查看参考答案
可能原因:
- 没有使用归一化 → 梯度爆炸
- 使用了 Post-LN → 深层网络不稳定
- 学习率太大 → 数值溢出
- 权重初始化不当 → 初始激活值过大
- FP16 精度问题 → 数值下溢或溢出
解决方案(按优先级):
确保使用 Pre-LN + RMSNorm:
pythonclass TransformerBlock(nn.Module): def forward(self, x): x = x + self.attn(self.norm1(x)) # Pre-Norm x = x + self.ffn(self.norm2(x)) # Pre-Norm return x降低学习率:
- 从 1e-3 降到 1e-4 或更小
- 使用 warmup(前 N 步线性增加学习率)
检查权重初始化:
- 使用 Kaiming 或 Xavier 初始化
- 确保初始激活值在合理范围(std ≈ 1.0)
使用梯度裁剪:
pythontorch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)使用 BF16 而不是 FP16:
- BF16 数值范围更大,更稳定
pythonwith torch.autocast(device_type='cuda', dtype=torch.bfloat16): output = model(input)
调试步骤:
- 在每一层后打印激活值的统计量(均值、标准差、max/min)
- 检查哪一层首先出现 NaN
- 针对性地调整该层的配置
Q7: 概念理解
用你自己的话解释:"归一化就像给水龙头装水压稳定器"这个类比。
点击查看参考答案
类比解释:
场景:你家的水龙头
没有稳定器:
- 上游水压高 → 水龙头喷射(梯度爆炸)
- 上游水压低 → 水龙头滴水(梯度消失)
- 用水体验很差,无法控制
有稳定器:
- 无论上游水压如何变化
- 稳定器调整后,输出水压始终稳定
- 用水体验舒适,易于控制
对应到神经网络:
没有归一化:
- 前面层的激活值变化 → 后面层的输入不稳定
- 数值可能爆炸(过大)或消失(过小)
- 训练困难,模型难以收敛
有归一化(RMSNorm):
- 每一层的输出都被"归一化"到标准范围
- 无论输入如何变化,输出分布稳定(std ≈ 1.0)
- 训练稳定,模型容易收敛
核心思想: 归一化不是改变信息内容,而是控制信息的尺度,让后续层更容易处理。
✅ 完成检查
完成所有题目后,检查你是否达到:
- [ ] Q1-Q5 全对:基础知识扎实
- [ ] Q6 能提出 3+ 解决方案:具备实战能力
- [ ] Q7 能清晰解释类比:深刻理解概念
如果还有不清楚的地方,回到 teaching.md 复习,或重新运行实验代码。
🎓 进阶挑战
想要更深入理解?尝试:
修改实验代码:
- 将 RMSNorm 替换为 LayerNorm,对比速度
- 测试不同的 eps 值(1e-5 vs 1e-8)
- 增加层数到 20 层,观察效果
阅读论文:
实现变体:
- 实现 BatchNorm(在 batch 维度归一化)
- 对比 BatchNorm vs LayerNorm vs RMSNorm
下一步:前往 02. Position Encoding 学习位置编码!