混合精度与 FP8
降精度是唯一同时改善"算力"与"带宽"两侧的手段:位宽减半,Tensor Core 峰值算力翻倍、显存与带宽需求减半(见Roofline)。从 FP32 到 FP16/BF16 再到 FP8,每一次精度下探都是一次性能跃迁——也都是一次数值风险的重新定价。本章讲清楚各格子的特性、训练怎么稳住、以及哪些风险是真实的。
格子们:FP32 → BF16 → FP8
Section titled “格子们:FP32 → BF16 → FP8”| 格式 | 位宽 | 指数/尾数 | 动态范围 | 峰值算力(H100) | 主要用途 |
|---|---|---|---|---|---|
| FP32 | 32 | 8/23 | ~10³⁸ | 67 TFLOPS | 累加器、优化器状态 |
| TF32 | 19(存储 32) | 8/10 | ~10³⁸ | 495 TFLOPS | FP32 的加速替身 |
| FP16 | 16 | 5/10 | ~6×10⁴ | 989 TFLOPS | 训练(配 loss scale) |
| BF16 | 16 | 8/7 | ~10³⁸ | 989 TFLOPS | 训练的默认选择 |
| FP8 E4M3 | 8 | 4/3 | ~448 | 1979 TFLOPS | 前向/大模型训练 |
| FP8 E5M2 | 8 | 5/2 | ~5.7×10⁴ | 1979 TFLOPS | 梯度(范围大、精度低) |
| FP4 | 4 | — | 极小 | 9000+(B200) | 推理/极限场景 |
关键理解:位宽买两样东西——范围(指数)与精度(尾数),两者互相挤占:
- FP16 的尾数多(精度高)但指数少(范围窄,容易上/下溢)——所以需要 loss scaling
- BF16 把 FP16 的尾数位拿去给了指数位:范围与 FP32 一样大(不会溢出),精度低一点(尾数 7 位)——范围换精度,训练从此不用管 loss scale,这是 BF16 成为默认的原因
- FP8 的 E4M3(前向,精度优先)与 E5M2(梯度,范围优先)就是这个权衡在 8 位上的重演
混合精度的标准结构
Section titled “混合精度的标准结构”"混合"的意思是:计算用低精度,敏感处保留 FP32:
前向/反向:BF16(或 FP8)—— 矩阵乘与激活权重主副本:FP32 —— 优化器更新的基准梯度累加:FP32 —— 多微批的求和不能丢精度优化器状态:FP32 —— 动量/方差是敏感累加量这就是"16 B/参数"显存公式的来源(2 B 权重 + 2 B 梯度 + 12 B FP32 优化器状态,见数据并行)。
Loss scaling(FP16 专属):FP16 范围窄,小梯度下溢成 0。解法:loss 乘一个大数(如 2¹⁶)放大梯度,反向后再除回来。BF16 范围与 FP32 相同,不需要 loss scaling——这是一次巨大的工程简化。
FP8 训练:快一倍的代价与护栏
Section titled “FP8 训练:快一倍的代价与护栏”FP8 的算力翻倍是真实的(H100 FP8 1979 TFLOPS),但直接全 FP8 训练会崩。生产级 FP8 训练(DeepSeek-V3、GPT 混合方案)的标配护栏:
一、只对"安全"的矩阵乘用 FP8:前向的大 GEMM(QKV、FFN)用 E4M3;attention 的 softmax、LayerNorm、所有 reduction 保持 BF16/FP32。
二、动态缩放(per-tensor / per-block scaling):FP8 的表示范围太小,每个张量按自己的实际数值范围缩放到 FP8 的格子中心,算完再缩回:
DeepSeek-V3 的细粒度缩放(per-block/per-group)是 FP8 训练稳定的关键改进。
三、累加器与主副本保持高精度:FP8 矩阵乘的累加器仍是 FP32,权重主副本 FP32——低精度只在"搬运与计算"上,敏感处一律高精度。
收益是真实的:FP8 GEMM 快 ~1.5–2 倍、带宽省一半、显存省一半。成本是工程复杂度(缩放、精度监控、kernel 支持),所以"BF16 起步、稳了再 FP8"是标准路径。
精度风险的真实清单
Section titled “精度风险的真实清单”| 风险 | 场景 | 症状 | 防护 |
|---|---|---|---|
| FP16 下溢 | 小梯度 | loss 不动/梯度为 0 | loss scaling 或改 BF16 |
| FP16 上溢 | 大 loss | NaN/inf | loss scaling 的动态调整 |
| BF16 累加误差 | 长序列求和 | 训练慢漂移 | 累加器用 FP32 |
| FP8 量化误差 | 全 FP8 | loss 曲线抬升 | 细粒度缩放 + 混合精度 |
| 优化器状态低精度 | 实验性做法 | 长训练发散 | FP32 优化器状态(别省这 12 B) |
| 推理 KV cache FP8 | 长上下文 | 长程注意力退化 | 抽测长程任务再上 |
监控比防护更重要:训练中持续监控梯度范数、loss 突变、NaN 计数——精度问题在曲线上的特征(跳变、抬升、平台)比事后复现容易得多。
- 训练:BF16 起步(无 loss scale、范围安全),验证训练稳定后再评估 FP8
- 推理:BF16 基线 → INT8/FP8 量化(算力带宽双收益,见量化)→ 极限场景 FP4
- 别动的地方:优化器状态、梯度累加、softmax/LN——省这 12 B/参数的代价是训练发散,不值得
- 迁移对比:换精度(BF16→FP8)时用固定的检查点/数据集跑对比实验,loss 曲线 + 下游评测双确认
- 位宽买"范围+精度":BF16 用精度换范围(免 loss scaling),FP8 的 E4M3/E5M2 重演这个权衡。
- 混合精度 = 低精度计算 + FP32 敏感处(主副本/累加器/优化器状态),16 B/参数显存公式由此而来。
- FP8 训练的护栏:只对大 GEMM、细粒度动态缩放、累加器高精度;收益 1.5–2 倍是真实的。
- 精度问题在 loss 曲线上有特征(跳变/抬升/平台),持续监控优于事后防护。
- 路径:BF16 起步 → FP8 → FP4;优化器状态别省,softmax/LN 别动。
- GEMM、cuBLAS/CUTLASS 与算子库 — 上一章:FP8 的主战场
- 数值精度与格式 — 第一卷:格子的数学基础
- FlashAttention 与算子融合 — FA 的 FP8 路径
- 量化 — 推理侧的精度压缩