详细解释
**Mixed Precision Training(混合精度训练 / Automatic Mixed Precision,简称 AMP)**一句话:深度学习训练 99% 的计算量都在矩阵乘和 Attention 这些 Tensor Core 友好的大矩阵运算上,这些运算用半精度 FP16/BF16 算出来和全精度 FP32 数值误差在 0.1% 以内完全不影响收敛,但显存占用减半、计算速度直接翻倍(因为 Tensor Core 对 16 位浮点的吞吐量是 32 位的 ×2 到 ×4);但 Loss / Softmax / Batch Sum 这些运算对半精度下溢/上溢敏感,会出现梯度变 0(Underflow)或者 NaN(Overflow)。混合精度就是「大部分 Tensor 运算用 16 位提速省显存、少数敏感运算保留 32 位 + 给 Loss 乘一个缩放系数 Scale 防下溢」的一套组合拳,让你速度翻倍、显存减半、精度不掉。 它是 2018 年之后所有大模型训练的默认「基础设施级技巧」,任何不开启 AMP 的训练基本等同于故意浪费一半钱和一半时间。
AMP 的三大核心技巧(NVIDIA 2018 年《Mixed Precision Training》论文里定下的三大铁律,至今 7 年没变过):(1)Weight Master Copy 主权重保留 FP32 精度——模型权重的「本」永远存在 FP32 的 Master Copy 里,Forward / Backward 时实时拷贝并 Cast 成 FP16/BF16 去算,Update(优化器 step)时把 16 位梯度 Cast 回 FP32 去更新 FP32 的权重——保证权重的累积更新永远是高精度的,不会因为 16 位精度不够越训越偏;(2)Loss Scaling(损失缩放)防梯度 Underflow——FP16 能表示的最小正数大约是 6e-8,很多梯度假正的值就是 1e-9 量级,直接转 FP16 会变成 0(下溢);做法是 Forward 算出来的 Loss 先乘以一个大 Scale(比如 1024 / 4096),再 Backward,这样梯度也跟着被放大 Scale 倍,就不会 Underflow 成 0;在 update FP32 权重之前再把梯度除以 Scale 还原回去;(3)自动精度白名单 / 黑名单——矩阵乘、Convolution、Attention 这些放在白名单用 FP16/BF16 最快;Softmax、LayerNorm 的 Reduction Sum、Loss 计算这些放在黑名单强制用 FP32(或者算的时候动态 Cast 到 FP32);不用你手动一个个 Cast,PyTorch torch.cuda.amp.autocast / TransformerEngine 自动帮你分。
唯元智创 的所有训练任务(SFT / DPO / KTO / 预训练)全部默认打开 AMP(2025 年默认用 BF16 + FP32 Master Weight,旧显卡不支持 BF16 的自动回退到 FP16 + 动态 Loss Scaling),平台还会自动监控每 100 step 的 Inf/Nan 比例:如果 Inf/Nan 比例超过 0.1%,自动把 Loss Scale 调小 + 报警通知用户,不用人工盯着 Loss 曲线看。
FP16 AMP vs BF16 AMP 对比(2025 年选型——新手最常问的问题)
| 维度 | FP16 AMP + 动态 Loss Scaling | BF16 AMP(不需要 Loss Scale,FP32 Master Weight 仍保留) |
|---|---|---|
| 动态范围(能表示的数范围) | ±65504 上限,±6e-8 下限 → 范围很窄,梯度特别容易下溢 | ±3e38 上限(和 FP32 一样大),±1e-38 下限 → 动态范围和 FP32 一样宽,下溢概率几乎为 0 |
| 尾数精度(值的精细程度) | 10 位尾数(约 3 位十进制小数精度)→ 精度比 BF16 高 2× | 7 位尾数(约 2 位十进制小数精度) |
| 是否需要 Loss Scaling | ✅ 必须用(否则 Underflow 梯度为 0) | ❌ 不需要(动态范围够大,几乎不会下溢) |
| Inf / NaN 出现概率 | 高——Loss Scale 设得不好、梯度突变时容易出现,训练可能偶尔不稳定 | 极低(< 百万分之一 step)——动态范围和 FP32 同级,溢出概率几乎为 0 |
| 收敛曲线和最终精度 | 两者对主流 LLM 训练几乎没差别(差异 < 0.2%) | 相同 |
| 代码复杂度 | 需要 scaler = GradScaler() 包裹整个 step 流程 | 只需要 autocast 开 bf16,不用 GradScaler,简单 50% |
| 硬件要求 | 所有 NVIDIA Volta(V100)及之后的显卡均支持(V100/T4/A10/3090/4090/A100 全支持) | 仅限 Ampere+ 架构(A10/A100/3090/4090/H100/H200/GB200),T4/V100/2080Ti 这些老卡不支持 |
| 2025 年推荐度 | ⭐⭐⭐⭐ 老卡(T4/V100)只能用它;新卡不推荐 | ⭐⭐⭐⭐⭐ 所有新显卡一律 BF16,省掉 Loss Scale 调参的坑,写代码简单一倍 |
落地 AMP 的 5 条实战纪律
- 一定要用 Master Weight(FP32 的权重副本),不要省这份显存——很多人觉得「FP32 Master Copy 又占一半模型权重显存,太浪费了」,想直接存 16 位权重做 update;结果就是训练到后期,16 位权重的 Update 累加误差慢慢积累(每次 × 学习率的小增量,加久了就偏差),下游任务分数会掉 1-3%,而且你完全不知道是因为 AMP 丢精度了。Master Weight 只占模型权重一份 FP32 的空间(7B FP32 才 28GB,相比训练时的优化器状态 3 份 FP32 参数+梯度完全是小头),别省这点钱;2025 年的所有主流框架(HF Trainer、DeepSpeed、Megatron)默认都是开 Master Weight 的,你只要不要手动去关就好。
- FP16 AMP 如果 Loss 出现 NaN,第一步要做的不是调学习率,是把 Loss Scale 的初始值调小 + 打开动态缩放——默认动态 Loss Scaling(PyTorch GradScaler 的默认参数)在 95% 的情况下会自动调对,出现 NaN 时它会自动把 Scale 砍半然后继续;如果你的训练一上来 Loss 就是 NaN,动态 Scale 砍到 1 还是 NaN,那肯定不是 AMP 的问题,是你的学习率太大了 / 数据有 bug / 梯度裁剪没开,不要甩锅给 AMP。
- 绝对不要在 Forward 里手动把所有张量 Cast 到 FP16!——正确做法是用
torch.cuda.amp.autocast(dtype=torch.bfloat16)上下文管理器把模型 Forward 的代码包起来,autocast 会根据操作类型自动把白名单操作 Cast 到 16 位,黑名单强制 32 位;你手动全 Cast 会把 Softmax、LayerNorm 这些黑名单操作也强制放到 16 位,结果 LayerNorm Sum 下溢 / Softmax 归一化错,直接不收敛。 - 评估 / 推理时要不要开 AMP?建议永远开 BF16 推理,别用 FP32——推理没有 Backward 和 Gradient Scaling 这些问题,直接模型 Cast 到 BF16(老卡 FP16)跑就行,显存减半、速度翻倍,精度损失 < 0.1%(大部分人盲测分不出来);2025 年所有的推理服务默认都是 BF16 / FP8,没有人用 FP32 做推理了,浪费钱。
- 多机多卡 + ZeRO / FSDP 场景下的 AMP 精度要统一——所有 GPU 必须用同一种精度(要么全 BF16,要么全 FP16),不能一张卡 BF16 一张卡 FP16;否则跨卡 All-Reduce 同步梯度时 Cast 会出莫名其妙的问题,All-reduce 结果对不上导致训练发散。DeepSpeed 配置里在 bf16 节将 enabled 开关设为 true 全局配置一次就好,不要每张卡单独设置。