FP8 不是把整个训练步改成 8 bit。它先把权重、激活和梯度搬进 E4M3/E5M2 的可表示窗口, 让 GEMM 用 8 bit 输入、FP32 累加;softmax、LayerNorm、loss 等非 GEMM 算子仍留在高精度,optimizer 状态按 recipe 分层(FP8-LM 一阶矩 FP8、二阶矩 FP16)。 真正难的是 scale:同一个张量在不同 rank、不同 step、不同 checkpoint 上必须对得上。
BF16 把计算压到 16 bit,却没有把训练状态压下去:FP32 master、FP32 梯度、FP32 Adam m/v 仍是 16 B/参数(FP8-LM Eq.7);BF16 训练的 all-reduce 也常用 32 bit(§3.2.2)。 所以显存和带宽先撞墙,算力反而不是唯一瓶颈。
FP8 把 GEMM 输入压到 1 B/值。E4M3 范围窄但尾数多,适合权重/激活;E5M2 范围宽但尾数少,适合梯度。 scale 把张量搬进 448 或 57,344 的窗口;GEMM 内部仍用 FP32 累加,非 GEMM 算子留在高精度。
delayed scaling 用 amax 历史预测下一步 scale;per-tensor 最省,block-wise 更能隔离离群值。
Megatron 默认 history_len=1, most_recent,TE 默认 1024, max——两套默认值不同,调参前先确认。
FP8-LM 把梯度、通信和 optimizer 也压到 8 bit。GPT-175B 在 H100 上显存 66.1→40.3 GB, 同 micro BS 吞吐 +21.0%;论文 headline 的 +75.4% 对应 micro BS 1→4,不是同 batch 对照。
训练状态按参数计账;先算清 16 B/参数,才知道 FP8 该省哪一块。
BF16 训练的常见配置是:计算用 BF16,但 master weights、梯度和 Adam 的一阶/二阶矩仍用 FP32。 按 FP8-LM 的记账,每个参数在训练状态上要占:
把 175B 粗算成 175×109 个参数,这四项就是 2,800 GB(175×16),
还没算激活、通信缓冲和临时 workspace。模型越大,optimizer 状态越先成为容量瓶颈。
带宽是第二条线。FP8-LM §3.2.2 指出:BF16 混合精度虽然用 16 bit 算梯度, all-reduce 仍常走 32 bit;也就是说,计算位宽减半并没有自动让通信位宽减半。 激活在 TP/SP 之间的 all-gather / reduce-scatter 同样按高精度搬。
算力是第三条线,但收益面最窄。FP8 Tensor Core 的乘加吞吐高于 BF16,可这份收益只落在 GEMM 上; softmax、LayerNorm、loss、residual add 和 optimizer update 仍按 BF16/FP32 走,而且大多是 memory-bound。 把这三条线混在一起,就会得到“FP8 一定省一半”的错误预期。
GEMM 适合 FP8,因为它有大量乘加,scale 的额外算术可以被摊销;softmax/LN 没有这个结构, 把它们压到 FP8 往往只增加量化误差,不增加吞吐。显存则要看哪些张量真的以 FP8 存储: 只在 GEMM 内部 cast、算完就回高精度,省的是临时带宽,不是 optimizer 的 16 B/参数。
三套方案各自把问题推给了另一处:范围、精度或校准;FP8 想把两者拆开。
Table 9(FP8-LM Appendix A.1):各格式的 normal 范围;INT8 是定点格式,没有指数,范围由 scale 决定。
| 方案 | 每值字节 | 最大 normal | 最小 normal | 训练中的主要问题 |
|---|---|---|---|---|
| FP16 | 2 | 65,504 | 6.10e-5 | 范围窄,需要 loss scaling;大模型易溢出/下溢 |
| BF16 | 2 | 3.39e38 | 1.18e-38 | 范围同 FP32,但尾数只有 7 bit;存储不比 FP16 小 |
| INT8 | 1 | 定点 | 定点 | 需要校准;per-channel 权重 + per-tensor 激活;PTQ 精度损失 |
| FP8 E4M3 | 1 | 448 | 1.56e-2 | 范围窄;需要 per-tensor/block scale |
| FP8 E5M2 | 1 | 57,344 | 6.10e-5 | 范围宽;尾数只有 2 bit,精度低 |
FP16-FP32 在大模型上出过稳定性问题:范围窄,梯度容易溢出或下溢,需要 loss scaling 和跳过更新。 BF16 把指数位做到 8 bit,范围与 FP32 相同,成为 LLM 训练的默认选择;但它的尾数只有 7 bit, 而且每个值仍占 2 B。FP8-LM §2 对这段历史的总结是:社区从 FP16-FP32 转向 BF16-FP32, 是因为 BF16 的范围足够宽,同时比 FP32 省一半字节。
INT8 的麻烦在校准。FP8 Formats 的 Table 5 给了一组推理 PTQ 的对照:BERT Base 的 SQuAD v1.1 F1 从 16-bit 的 88.19 掉到 int8 的 76.89;GPT3 126M 在 wikitext103 上的 perplexity 从 19.01 升到 28.37。 同一张表里 E4M3 的 PTQ 是 88.09 和 19.43,接近 16-bit。 这组数字是推理后量化,不是训练;它说明定点 INT8 对离群值敏感,不能直接拿来当“训练也能用”的证据。
还有一层现实限制:FP8-LM §2 指出,当时的 NVIDIA Transformer Engine 只对 Transformer 的 linear 层做 FP8 compute, weight update 和 gradient synchronization 仍走高精度。也就是说,“FP8 矩阵乘”与“FP8 训练”是两件事: 前者省 GEMM 的输入带宽,后者还要处理梯度、通信和 optimizer 状态。本页后面把这两层分开写。
权重/激活进 FP8,GEMM 后回高精度;optimizer、梯度 all-reduce、checkpoint 仍是高精度。实现简单,但显存和通信的收益有限。
梯度、通信、optimizer 状态也进低精度;需要额外的 scale 对齐、梯度精度取舍和 checkpoint 支持。FP8-LM 走的是这条。
格式决定范围和分辨率,scale 决定这一步的张量落在窗口的哪里;GEMM 内部仍是 FP32 累加。
Table 1(FP8 Formats for Deep Learning):E4M3/E5M2 的位宽、bias、特殊值和 normal/subnormal 边界。
| 属性 | E4M3 | E5M2 |
|---|---|---|
| 位宽(sign / exponent / mantissa) | 1 / 4 / 3 | 1 / 5 / 2 |
| exponent bias | 7 | 15 |
| Infinities | 无 | S.11111.002 |
| NaN | S.1111.1112 | S.11111.{01,10,11}2 |
| 最大 normal | 448 | 57,344 |
| 最小 normal | 2−6 | 2−14 |
| 最小 subnormal | 2−9 | 2−16 |
| binade 数 | 18 | 32 |
E4M3 把 4 bit 给 exponent、3 bit 给 mantissa;E5M2 是 5+2。E4M3 的 bias 是 7,最大 normal 是 448; E5M2 的 bias 是 15,最大 normal 是 57,344。E5M2 的上限是 E4M3 的 128 倍, 但每个 binade 只有 4 个格点,E4M3 有 8 个——这就是范围换分辨率。
E4M3 没有 Inf,只留一个 NaN 编码;它靠回收特殊值把范围从 17 个 binade 扩到 18 个,
最大 normal 从 240 提到 448。E5M2 保持 IEEE 的 Inf/NaN 约定。
论文 §3 推荐的映射是:权重和激活用 E4M3,梯度用 E5M2。
Megatron 的 'hybrid' 就是这个映射;'e4m3' 则把所有权重、激活和输出梯度都放在 E4M3
(transformer_config.py:749-754)。
GEMM 的反缩放可以合并成一个式子。设激活和权重的 scale 分别是 sx、sw:
FP8 Formats §2 的表述是:matrix-multiply 或 dot-product 指令产生 single-precision 输出; TE 文档也写明,Hopper Tensor Core 可以直接把矩阵乘积累加到 FP32。换句话说,FP8 改的是输入存储和乘法, 不是累加精度。没有这一步,长 K 的 GEMM 会在低精度累加里迅速丢有效位。
TE 的 DelayedScaling / CurrentScaling 都是这个粒度。开销最小;一个离群值会把 amax 顶高,导致其余值分辨率下降。
FP8 Formats §4.2 的 PTQ 约定是权重 per-channel、激活 per-tensor。它不在 Megatron 的四个内置 recipe 里;需要时走 custom。
TE 的 MXFP8BlockScaling 用 1 个 E8M0 scale / 32 值(Blackwell);Float8BlockScaling 按 DeepSeek-V3 的块(arXiv 2412.19437 §3.3)。块越细,离群值越难污染邻居,scale 开销也越大。
amax 是张量的最大绝对值。当前 scaling 在每次量化时现算 amax,需要先读一遍张量求最大值、
再读一遍做缩放和 cast;TE 文档把这两次读称为“significant overhead”。
delayed scaling 则用历史窗口里的 amax 预测这一步的 scale:量化时只读一次张量,
cast 完再把本次观测到的 amax 写回历史,供下一步使用。
amax_eff 有两种取法:窗口内最大(max)或最近一次(most_recent)。
然后 s = MAX / amax_eff。TE 的 DelayedScaling 默认窗口 1024、取 max;
Megatron-Core 的默认值是窗口 1、取 most_recent(transformer_config.py:783-790),
并在构造 TEDelayedScaling 时把自己的配置传进去(transformer_engine.py:3809-3816)。
两套默认值不同:前者更平滑,后者对最近一步更敏感。
历史窗口的更新顺序是:先用量化前的 scale 做 cast,再把本次 amax 写入 slot 0;
退出 autocast 时先按含本次 amax 的 history 计算下一步 scale,再旋转窗口。TE 文档给出的顺序是
[amax_N, amax_1, …, amax_N−1] → [0, amax_2, …, amax_N]。
本页的交互是简化模型:用上一步结束时的 history 算本步 scale,本步观测只写回 history 供下一步用。
组 1(默认):history=[120,96,160],algo=max,本步观测 amax=640。
amax_eff = max(120,96,160) = 160;scale = 448/160 = 2.80。scaled amax = 640×2.80 = 1792.0;1792.0 > 448,cast 后饱和到 448,状态为“饱和”。history=[96,160,640];下一步 amax_eff = 640,scale = 448/640 = 0.70。组 2:history=[120,96,160],algo=most_recent,本步观测 amax=80。
amax_eff = 160(最近一次);scale = 2.80;scaled amax = 80×2.80 = 224.0,正常。history=[96,160,80];amax_eff = 80,scale = 448/80 = 5.60。80×5.60 = 448.0,正好在边界;若本步 amax 改为 96,96×5.60 = 537.6 > 448,饱和。这个模型简化了什么:它只模拟 per-tensor、E4M3、无 margin 的 delayed scaling;history 里存的是历史观测 amax,不模拟 TE 的 slot 0 写入与窗口旋转的精确时序,也不模拟 RTNE 的逐值舍入。溢出判断用 > 448.0001 避开浮点尾差;真实实现按 cast 后的结果饱和。
同样一个 4096×4096 权重,FP8 payload 是 BF16 的一半;scale 粒度越细,省得越少。
Table 1a(本页推导,数据取自 FP8-LM Eq.7/8):每参数训练状态的字节数。FP8-LM 的默认配置是 master FP16+tensor scaling、梯度 FP8、Adam 一阶矩 FP8、二阶矩 FP16。
| 项目 | FP32 / FP32 基线(Eq.7) | FP8-LM(Eq.8) |
|---|---|---|
| master weights | 4 B | 2 B |
| gradients | 4 B | 1 B |
| Adam 一阶矩 m | 4 B | 1 B |
| Adam 二阶矩 v | 4 B | 2 B |
| 合计 | 16 B | 6 B |
这张表是 FP8-LM 自己的 recipe,不是 Megatron 默认。Megatron 的 fp8_param=False 默认只把 GEMM 的输入 cast 到 FP8,
参数、梯度、optimizer 状态仍按高精度存;它的 fp8_param_gather 还要求 distributed optimizer、torch FSDP2 / Megatron FSDP 或推理模式
(arguments.py:978-980)。所以“6 B/参数”不能直接套到所有 FP8 训练配置上。
下面只算权重 payload + scale 存储,不含梯度、optimizer、激活和临时 buffer。 per-channel 按“一个输出通道一个 FP32 scale”计;1×32 block 按 TE MXFP8 的 1 个 E8M0 scale / 32 值计。
Table 1b(本页推导,输入 4096×4096):BF16 每值 2 B;FP8 每值 1 B;per-tensor 1 个 FP32;per-channel 4096 个 FP32;1×32 block 524,288 个 E8M0(1 B)。
| 精度 / 粒度 | payload B | scale B | 总计 B | 相对 BF16 |
|---|---|---|---|---|
| BF16 | 33,554,432 | 0 | 33,554,432 | — |
| FP8 per-tensor | 16,777,216 | 4 | 16,777,220 | 50.000% |
| FP8 per-channel | 16,777,216 | 16,384 | 16,793,600 | 49.951% |
| FP8 1×32 block | 16,777,216 | 524,288 | 17,301,504 | 48.438% |
payload 正好是一半(50.000%)。scale 开销在 per-tensor 下只有 4 B,可以忽略;
per-channel 是 16,384 B,per-tensor 的 4096 倍;1×32 block 是 524,288 B,相当于 payload 的
3.125%(524,288/16,777,216)。粒度越细,抗离群值越好,但省下的比例从 50.000% 降到 48.438%。
组 1:M=K=4096,per-tensor。
N = 4096×4096 = 16,777,216 个值;FP8 payload N×1 = 16,777,216 B;BF16 N×2 = 33,554,432 B。4 B;总计 16,777,216+4 = 16,777,220 B。(33,554,432−16,777,220)/33,554,432×100 = 50.000%。组 2:同一张量,per-channel 与 1×32 block。
16,384 B;总计 16,777,216+16,384 = 16,793,600 B;相对 BF16 (33,554,432−16,793,600)/33,554,432×100 = 49.951%。16,777,216/32 = 524,288 个 E8M0,每个 1 B = 524,288 B;总计 16,777,216+524,288 = 17,301,504 B;相对 BF16 (33,554,432−17,301,504)/33,554,432×100 = 48.438%。524,288/16,777,216×100 = 3.125%。这个模型简化了什么:只算权重 payload 和 scale 存储,不含对齐/padding、metadata、梯度、optimizer、激活、通信 buffer 和 cast 临时张量;假设 FP8 每值 1 B、E8M0 每 scale 1 B。per-channel 假设按输出行量化;若按列量化,scale 数会变成 K。Float8BlockScaling 的块大小 TE 文档未列出;按所引 DeepSeek-V3 §3.3,激活为 1×128、权重为 128×128,本页只实现 1×32 MXFP8 模式,不计入该模式。
系统级显存没有 50% 这么整齐。FP8-LM Table 5 的 GPT 模型在相同 batch 下,FP8 相对 BF16 的显存降幅是 28–39%; 因为权重只是其中一块,梯度、optimizer、激活和通信缓冲各有自己的精度策略。 更反直觉的是 FP8 (TE) 这一行:GPT-175B 的 TE 显存是 69.6 GB,比 BF16 的 66.1 GB 还高; FP8-LM 的 40.3 GB 来自 FP8 梯度、FP8 通信和 FP8 optimizer,而不是单纯把 GEMM 换成 FP8。
compute-only 路径里 FP8 只出现在 GEMM 输入;full-FP8 还把梯度通信和 optimizer 状态按精度分层。amax 归约与 scale 更新在高精度,optimizer 更新按 recipe 执行。
Table 1c(本页归纳):一个训练步里的精度分配;Megatron 默认值见 transformer_config.py:749-808,FP8-LM 的 optimizer 取舍见 §2.2 与 Table 6。
| 阶段 | 张量 | 精度 | 说明 |
|---|---|---|---|
| forward GEMM | 激活 / 权重 | FP8 E4M3 | GEMM 内部 FP32 累加,输出反缩放 |
| backward dgrad | grad_output | E5M2(hybrid) | 范围换精度,适合梯度 |
| backward wgrad | grad_weight | FP8(fp8_wgrad=True) | FP32 累加;设为 False 可让 wgrad 走更高精度 |
| 非 GEMM | softmax / LN / loss | BF16 / FP32 | 没有大量乘加,量化收益小 |
| optimizer | master / m / v | FP32,或 FP16/FP8 recipe | FP8-LM 的 6 B/参数见 Eq.8;二阶矩仍用 16 bit |
fp8_autocast 里完成 cast、amax 更新和下一步 scale 的计算。1/(sx·sw) 回到高精度。per-channel 时按输出通道广播,block-wise 时按块广播。hybrid),dgrad 和 wgrad 仍走 FP8 GEMM;wgrad 在 FP32 累加。Megatron 的 fp8_wgrad=True 是默认,关掉它会让 wgrad 走更高精度(transformer_config.py:791-795)。get_amax_reduction_group(with_context_parallel=True, tp_only_amax_red=...)(fp8_utils.py:881-890);tp_only_amax_red=True 时只归约 TP/CP,不跨 DP(parallel_state.py:2078-2101)。
如果每个 rank 用自己的 amax 算 scale,同一个权重在不同 rank 上会被编码成不同 scale 的 FP8 值;
all-reduce 或 all-gather 把这些值直接相加,等于把不同单位的数据混在一起。所以 TE 的 fp8_autocast
接收一个 fp8_group,在 autocast 退出时统一归约 amax。Megatron 把这个组设成 TP+DP+CP(或仅 TP/CP),
对应 fp8_utils.py:881-890 和 parallel_state.py:2078-2101。
FP8-LM 的梯度 all-reduce 是另一套:先在各 rank 取全局最小 scale s'_g = min(s'_1,…,s'_N),
把每个 rank 的 FP8 梯度重标到同一个 scale,再走标准 NCCL all-reduce;聚合后 s = N·s'_g(Eq.4-6)。
它的 auto-scaling 用 0.001% 的溢出阈值调 μ:超过阈值下一步 μ=1/2,
比例持续不超过阈值则在 1,000 步内指数升到 2(§2.1)。
两者不要混:Megatron 的 amax 归约是给 TE 的 per-tensor scale 用的;FP8-LM 的 s'_g 是给 FP8 梯度通信用的。
一个是量化窗口,一个是 all-reduce 的单位。
TE 的 extra state 里存 scale_fwd/scale_bwd 和
amax_history_fwd/amax_history_bwd,再序列化成 _extra_state
(transformer_engine.py:2942-2975、:3097-3122)。恢复时如果只加载权重、
不加载这些状态,第一步的 scale 就是错的。
Megatron 的 --stream-ckpt-dequant 明确不支持 --fp8-recipe delayed:
delayed 的 scale 只在 load_state_dict 里恢复(arguments.py:1728-1730)。
换并行度、换 recipe 或换 checkpoint 格式时,先确认 scale 和 amax history 的来源。
先看条件再看数字:模型、硬件、并行度、micro batch 和精度方案都写在同一行。
Table 5(FP8-LM):Azure NDv5 H100 80GB;TP/PP/DP 与 micro BS 见行;Weight-related Comm. 含 weight all-gather 与 weight-grad reduce-scatter。
| 模型 | TP | PP | DP | Micro BS | 精度 | GPU Mem. (GB) | Throughput (samples/s) | TFLOPS | MFU (%) | 权重通信占比 (%) | 权重通信量 (GB) |
|---|---|---|---|---|---|---|---|---|---|---|---|
| GPT-7B | 1 | 1 | 32 | 2 | BF16 | 69.6 | 159.2 | 445 | 45.0 | 10.1 | 37.2 |
| GPT-7B | 1 | 1 | 32 | 2 | FP8 (TE) | 77.3 | 224.5 | 627 | 31.7 | 9.7 | 37.2 |
| GPT-7B | 1 | 1 | 32 | 2 | FP8 (Ours) | 49.4 | 219.8 | 615 | 31.1 | 7.9 | 13.9 |
| GPT-7B | 1 | 1 | 32 | 4 | FP8 (Ours) | 69.3 | 230.5 | 645 | 32.6 | 10.4 | 13.9 |
| GPT-13B | 2 | 1 | 16 | 2 | BF16 | 68.2 | 79.3 | 420 | 42.5 | 11.1 | 34.3 |
| GPT-13B | 2 | 1 | 16 | 2 | FP8 (TE) | 76.4 | 111.7 | 592 | 29.9 | 7.1 | 34.3 |
| GPT-13B | 2 | 1 | 16 | 2 | FP8 (Ours) | 48.9 | 109.5 | 575 | 29.1 | 3.9 | 12.4 |
| GPT-13B | 2 | 1 | 16 | 4 | FP8 (Ours) | 67.8 | 121.5 | 644 | 32.5 | 9.3 | 12.4 |
| GPT-175B | 8 | 4 | 4 | 1 | BF16 | 66.1 | 22.4 | 386 | 39.0 | 8.8 | 23.4 |
| GPT-175B | 8 | 4 | 4 | 1 | FP8 (TE) | 69.6 | 28.7 | 493 | 24.9 | 3.9 | 23.4 |
| GPT-175B | 8 | 4 | 4 | 1 | FP8 (Ours) | 40.3 | 27.1 | 473 | 23.9 | 2.5 | 8.2 |
| GPT-175B | 8 | 4 | 4 | 4 | FP8 (Ours) | 57.7 | 39.3 | 677 | 34.2 | 10.9 | 8.2 |
先看 7B 和 13B:7B 的显存 69.6→49.4 GB,相对 BF16 降 29.0%; 吞吐 159.2→219.8 samples/s,相对提升 38.1%;权重通信量 37.2→13.9 GB。 13B 的显存 68.2→48.9 GB(28.3%),吞吐 79.3→109.5(38.1%), 通信 34.3→12.4 GB。这两组是同一 micro BS=2 的对照。
175B 要分两行看。micro BS=1 时,BF16 66.1 GB / 22.4 samples/s,FP8-LM 40.3 GB / 27.1 samples/s: 显存相对降 39.0%,吞吐相对升 21.0%,这是同 micro BS 的对照。 micro BS=4 时,FP8-LM 是 57.7 GB / 39.3 samples/s;相对 BF16 的 22.4 是 75.4%,但 micro BS 已经从小 1 提到 4,不是同 batch 对照。 论文 headline 的 75% 对应后者。
同一张表里,FP8 (TE) 的 175B 是 69.6 GB / 28.7 samples/s。它的显存比 BF16 的 66.1 GB 还高, 因为 TE 只把 GEMM 压到 FP8,optimizer 和梯度仍高精度;FP8-LM §3.2.2 也是这么解释的。 这也是为什么本页把 FP8-LM 的 40.3 GB 和 8.2 GB 通信量单独标出来——它们来自 FP8 梯度、FP8 通信和 FP8 optimizer, 不是 GEMM 一项的功劳。
MFU 也不是单调上升:175B 的 BF16 是 39.0%,FP8 micro BS=4 是 34.2%,低 4.8 个百分点;micro BS=1 时 FP8 只有 23.9%。 吞吐是 samples/s,MFU 是模型 FLOPs 利用率,两者口径不同;表里的 +75.4% 同时换了 micro batch, 不能当作同 batch 的算法加速。
论文 headline 还说“比 TE 快 37%”。按 Table 5 复算,39.3 vs 28.7 是 36.9%,但这也是 micro BS=4 对 TE micro BS=1; micro BS=1 时 FP8-LM 的 27.1 反而比 TE 的 28.7 低 5.6%。 这组对照说明:headline 的加速比不是单纯由精度方案决定的,batch 和并行配置也在里面。
模型质量方面,Table 2 的 zero-shot 平均分是:GPT-7B 的 BF16 58.4、FP8 58.0;GPT-13B 的 BF16 61.0、FP8 60.4。 SFT(Table 3,LLaMA-7B,Vicuna-v1.1 设置)的 GPU 显存 51.1→44.0 GB、吞吐 103→131, AlpacaEval FP8 67.20 vs BF16 66.15、MT-Bench FP8 5.70 vs BF16 5.75。 RLHF(Table 4,AlpacaFarm)的 weights 15,082→10,292 MB、optimizer states 15,116→5,669 MB, AlpacaEval FP8 72.42 vs BF16 72.05、MT-Bench FP8 6.04 vs BF16 6.16。 这些是报告值,不是统计显著性检验。
Table 4(FP8 Formats for Deep Learning):模拟 FP8 的 GPT perplexity;175B 的 6.65/6.68 是 75% 训练时的值,BF16 baseline 未跑完(§4.1)。
| 模型 | Baseline perplexity | FP8 perplexity |
|---|---|---|
| GPT 126M | 19.14 | 19.24 |
| GPT 1.3B | 10.62 | 10.66 |
| GPT 5B | 8.94 | 8.98 |
| GPT 22B | 7.21 | 7.24 |
| GPT 175B | 6.65 | 6.68 |
这张表来自 FP8 Formats 的模拟 FP8实验:先把输入 clip 到 FP8 可表示值,再回到 FP16/BF16 做算术。 它验证的是数值可行性,不是 H100 上的速度;本页的吞吐数字来自 FP8-LM Table 5,两者不要混。 论文对 175B 的措辞是“在报告的任务上与 16-bit 训练相当”,不是“无损”;175B 的 baseline 只跑到 75%, 长期收敛也没有覆盖。
FP8-LM:FP8 梯度、FP8 all-reduce、FP8 optimizer(一阶矩 FP8、二阶矩 FP16)、FP16+tensor scaling 的 master weights; 6 B/参数(Eq.8)。Table 5 的 40.3 GB / 8.2 GB / 39.3 samples/s 都属于这套。
Megatron-Core 默认:fp8=None,开启后 fp8_recipe='delayed'、
fp8_amax_history_len=1、fp8_amax_compute_algo='most_recent'、fp8_param=False、
fp8_wgrad=True;optimizer 状态不变,梯度 all-reduce 也不是 FP8。
本页没有给这套配置对应的吞吐数字,也不把它和 Table 5 直接比较。
FP8 的失败模式往往不是算错,而是 scale 选错;分布式下还会变成 rank 之间不一致。
FP8 Formats §4.3 给了一组推理 PTQ 的证据:GPT3 1.3B 在 wikitext103 上的 BF16 baseline perplexity 是 10.19。 只量化 GEMM 输入、用固定 exponent bias 7 时,若干 bias 取值可以匹配 baseline; 但如果连 residual connection 也量化,bias 7 的 perplexity 变成 12.59。 改成 per-tensor scaling 后,GEMM-only 是 10.29,GEMM+residuals 是 10.44。 这说明 per-tensor scale 对 residual/离群值是必要的;但这是推理 PTQ,不能直接外推成“训练一定没问题”。
E4M3 的最大 normal 是 448,最小 subnormal 是 2−9 = 0.001953125。
如果某一步的 amax 是 640,delayed scaling 下一步的 scale 是 448/640 = 0.70;
此时低于 0.001953125/0.70 = 0.00279 的值已低于最小 subnormal;按 RTNE,只有低于其一半(约 0.00140)的值才舍入为 0,介于两者之间的值量化为最小 subnormal。
反过来说,如果 scale 太大(amax 被低估),超过 448 的值会饱和到 448。
delayed scaling 的 history 长度和 max/most_recent 就是在这两个失败模式之间取平衡:
Megatron 默认窗口 1、most_recent,对最近一步最敏感;TE 默认窗口 1024、max,更平滑。
FP8 Formats 推荐梯度用 E5M2,因为它的范围比 E4M3 宽;代价是只有 2 个 mantissa 位。 FP8-LM 的消融进一步说明:FP8 master weight 会掉点;FP16 master weight 加 tensor scaling 可以匹配 FP32; BF16 master 比 FP16+scaling 略差;二阶矩用 FP8 会发散(Table 6 #4、Fig.8)。 所以 FP8-LM 的 6 B/参数不是“所有状态都 8 bit”:master 是 2 B(FP16+scaling),二阶矩是 2 B(FP16), 只有梯度和一阶矩是 1 B。
TE 的 amax 必须在 TP/DP/CP 范围内归约;Megatron 的 tp_only_amax_red 可以把范围缩到 TP/CP,
但这时跨 DP 的 scale 是否一致要由调用方保证。FP8-LM 则用全局最小 scale s'_g 统一梯度通信,
聚合后 s=N·s'_g;N 是 all-reduce 的 rank 数,换 DP 规模时这个乘积会变,
恢复训练时要确认实现是否重新对齐(论文未展开,推断)。
checkpoint 侧,TE 把 amax history 和 scale 放进 extra state;Megatron 的 --stream-ckpt-dequant
不支持 delayed,因为 delayed 的 scale 只在 load_state_dict 里恢复(arguments.py:1728-1730)。
TE 文档要求 Ada 或更新架构(SM 8.9+);block scaling(Float8BlockScaling)需要 Hopper(SM 9.0+)或 Blackwell;MXFP8 的 1 E8M0 / 32 值是 Blackwell 的能力。 H100 是 FP8-LM 的实验平台;A100 是 SM 8.0,不在 TE 的 FP8 支持范围内。 收敛方面,FP8 Formats 的 175B baseline 只跑到 75%,FP8-LM 的 175B 只训练 40B tokens(Table 1); 两者都说明“报告的任务上相当”,不等于“任意长度、任意模型都稳定”。
Table 1d(本页归纳):训练 FP8 与推理 FP8 的区别;推理侧数字来自 FP8 Formats §4.2/Table 5,训练侧来自 FP8-LM Table 5。
| 维度 | 训练 FP8 | 推理 FP8 |
|---|---|---|
| 目标 | 省显存/带宽/算力,同时保持收敛 | 省显存/带宽,保持精度 |
| 量化张量 | 权重、激活、梯度(可选) | 权重、激活 |
| scaling | delayed/current/block,带 amax history | 静态或动态 per-tensor/per-channel,常用校准 |
| 反向 | E5M2 梯度、FP8 GEMM、FP32 wgrad 累加 | 无 |
| 训练状态 | master weights、Adam m/v、amax history、scale | 无 optimizer 状态 |
| 典型失败 | 梯度下溢、scale 不一致、二阶矩发散 | 校准误差、离群值饱和 |
| 本页证据 | FP8-LM Table 5、Table 6 | FP8 Formats Table 5(PTQ) |
fp8_param=False、optimizer 和梯度仍是高精度,省下的主要是 GEMM 输入带宽,不是 16 B/参数。TE 的 175B 行就是例子:69.6 GB 比 BF16 的 66.1 GB 还高。先把 GEMM 输入压到 FP8、用 FP32 累加,再把梯度通信和 optimizer 状态按精度敏感度分层; scale 的粒度、history 和分布式归约决定它稳不稳。算清楚 payload、scale 开销和同 batch 条件, 再决定要不要把 40.3 GB 当成自己的目标。