8 位还能训吗?
本课一问:16 位已经稳了。矩阵乘占了训练的绝大部分算力,如果它能用 8 位算,理论上又快一倍——可 8 位一共只有 256 个位型。还能训吗?
这一课先不碰:4 位(岔路课);激活占多少显存(第 5 课);推理时的量化(C13)。
从已有出发
同一步训练,这回矩阵乘换成 FP8。看格式标签:前向、反向的矩阵乘变成了 FP8,可梯度、主权重反而留在 FP32,m、v 改成了 BF16——这是 DeepSeek-V3 的实际做法,本课逐项讲为什么。
- 解决了范围到 ±3.4 × 10³⁸,1 附近的间距 2⁻²³ ≈ 1.2 × 10⁻⁷,数值上几乎不用操心。卡在每个参数 4 + 4 + 4 + 4 = 16 字节(权重、梯度、Adam 的 m、v):锚点 27,781,427,952 个参数就是 444.5 GB,是 80 GB 单卡的 5.6 倍,还没算激活。
- 解决了矩阵乘和激活用 16 位,带宽与激活显存减半;FP32 主权重接住小更新,loss 乘 S 把小梯度推出下溢区,更新前再除回来。卡在指数只有 5 位:fp16(10⁻⁸) = 0,fp16(1 + 10⁻⁴) = 1,fp16(70,000) = inf;缩放倍数要一直调,溢出就得跳过这一步。
- 解决了8 位指数,范围与 FP32 相同:不再下溢,不需要 loss scaling。2026 年的默认训练精度。卡在1 附近间距粗到 1/128:bf16(257) = 256,bf16(1 + 10⁻³) = 1——主权重和优化器状态仍要 FP32,账本还是 16 字节/参数。
- 解决了激活每 1 × 128、权重每 128 × 128 一个缩放:张量 [1000, 0.01, 0.02, 0.5] 里,0.01 的误差从整张量缩放的 12.8% 降到 0.45%;DeepSeek-V3 相对 BF16 的 loss 相对误差 < 0.25%(官方自报)。卡在只有 256 个位型,E4M3 最大 448;块的形状要跟着离群值走——激活梯度改按 128 × 128 块量化,16B MoE 训到约 300B token 发散(附录 B.2);主权重和梯度仍是 FP32。
- 解决了MXFP4 每 32 个数共用一个 2 的幂(4.25 位/参数),NVFP4 每 16 个数一个 E4M3 缩放;K3、DeepSeek-V4 在后训练阶段对 MoE 专家权重做 QAT。卡在E2M1 只有 16 个码点:块 [0.3, −1.2, 2.7, 0.05] 用 MX 缩放后,0.05 → 0;全 FP4 预训练仍是前沿。
- 解决了不把 s × s 的注意力分数存下来:锚点一条 4,096 token 的序列,激活从 162.5 GiB 降到 42.5 GiB(选择性重计算 / FlashAttention);全量重计算每层只存输入,只剩 2.5 GiB(标准块公式近似)。卡在重计算用多算一遍前向换显存;可 444.5 GB 的模型状态一张 80 GB 卡仍然放不下 → C10 并行。
为什么要 8 位:DeepSeek-V3 把线性层的三个矩阵乘(前向 Fprop、求激活梯度的 Dgrad、求权重梯度的 Wgrad)都放进 FP8,报告说这在理论上让计算速度比 BF16 翻倍 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条原文:「theoretically doubles the computational speed compared with the original BF16 method」;另外 Wgrad 在 FP8 里做,反向要用的激活也能以 FP8 缓存。 出处DeepSeek-V3 §3.3.1(本地 PDF) ↗ 。例子沿用前两课的做法:追踪具体的数,贯穿例句不进入这一层。
8 位里一共有哪些数
8 位有两种常用分法:E4M3(4 位指数、3 位尾数)和 E5M2(5 位指数、2 位尾数)。把它们能表示的每一个正数画成一根刻度:
试三件事:① 数一数 1 到 2 之间的刻度:E4M3 有 8 根(间距 0.125),E5M2 只有 4 根;② 点「E4M3」展开全位型表,找到 NaN:只有指数、尾数全为 1 的那一个位型(正负各一,0x7F 与 0xFF)——E4M3 把 IEEE 留给 inf 的那一整行拿来多表示了 7 个数,最大值才到 448;③ 看两条刻度带的左端:E5M2 能表示到 1.5 × 10⁻⁵,E4M3 只到 0.00195。
FP8 的格式定义出自 NVIDIA、Arm、Intel 的联合提案:E4M3 不表示 inf、只保留一个 NaN 位型,把最大值扩到 448;E5M2 沿用 IEEE 的规则,最大 57,344。提案建议权重和激活用精度更高的 E4M3,梯度用范围更大的 E5M2 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条格式定义与推荐用法出自原文 §3;原文在最大 175B 的模型上验证,但训练是模拟 FP8:只把矩阵乘的输入裁到 FP8 可表示值,运算仍用 16 位。 出处Micikevicius et al. 2022(需联网) ↗需联网 。
但 E4M3 最大才 448,激活和梯度的真实数值随便就能超出——所以 FP8 没法「直接存」,必须先缩放:乘一个缩放因子,让张量的最大值刚好落到 448,量化;用的时候再除回来。
先自己设计:整张量共用一个缩放
最自然的做法:整张量取一个最大值 amax,缩放因子 = 448 ÷ amax。拿一个 4 个数的小张量试试:
- ① 朴素方案
- ② 它在哪失败
- ③ 改进方案
张量是教学构造:8 个 token × 256 个通道,固定种子的标准正态数,其中第 17、90 两个通道(同在前 128 个通道里)整体乘离群倍数 k——模拟「少数通道数值特别大」,不是任何模型的真实激活。量化、反量化、误差全部由浏览器实算(元素格式与 FloatLab 同一套编解码,开发时与 torch 的 float8_e4m3fn 逐值对照)。
张量是教学构造:8 个 token × 256 个通道,固定种子的标准正态数,其中第 17、90 两个通道(同在前 128 个通道里)整体乘离群倍数 k——模拟「少数通道数值特别大」,不是任何模型的真实激活。量化、反量化、误差全部由浏览器实算(元素格式与 FloatLab 同一套编解码,开发时与 torch 的 float8_e4m3fn 逐值对照)。
这里有一个容易讲错的地方,值得停下来说准:浮点数在正规区里的相对精度处处一样。E4M3 有 3 位尾数,不管数在 0.02 附近还是 200 附近,舍入误差都在同一个相对量级——缩放因子小一点,本身不增加相对误差。离群值真正的害处是把同一块里的其他数推出正规区的下沿:块最大值被放到 448,正规区最小是 2⁻⁶ = 0.015625,比块最大值小 448 ÷ 2⁻⁶ = 28,672 倍以上的数就进了次正规区,有效位变少;再小就直接变 0。手算例里 1000 和 0.01 差了 10 万倍,远远越过这条线。
在 8 × 256 的张量上试三件事:① 把离群倍数 k 拖到 1,000 以下:四种粒度的误差几乎一样——没有越过 28,672 倍那条线;② 拖到 10,000 以上:「整张量」那一行落进次正规区的比例成片上升(k = 10,000 时约一半),「1 × 128」只有含离群通道的那一半受影响,「1 × 32」受影响的块更少;③ 点「1 × 128」那一行看热力图:两个离群通道都在前 128 个通道里,坏掉的只有左半张,后 128 个通道完全不受影响。
改进:DeepSeek-V3 的细粒度缩放
DeepSeek-V3 的做法是把「一个缩放管整张量」换成「一个缩放管一小块」,而且激活和权重切法不同:
- 激活:每 1 × 128 一块,也就是每个 token、每 128 个通道一个缩放——离群值往往集中在某几个 token 或某几个通道,按 token 切,一个坏 token 只拖累它自己的 128 个数;
- 权重:每 128 × 128 一块;
- 缩放因子在线计算:每一步按当前这一块的最大值现算,不沿用历史值(config 里写作
activation_scheme: dynamic) 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条原文:激活按 1x128 tile、权重按 128x128 block 分组缩放;在线计算每个 tile / block 的最大绝对值。 出处DeepSeek-V3 §3.3.2(本地 PDF) ↗ 。
DeepSeek-V3 发布的 config 里就写着这个块大小:quantization_config 的 fmt = e4m3,weight_block_size = 128, 128。把一次前向的矩阵乘画成张量,用它一个路由专家的 up 投影的真实维度:
| 维度 | 含义 | 真实规模 | 小规模 |
|---|---|---|---|
| T | 这一批进入该专家的 token 数(这里取贯穿例句的 5 个,作为输入规模) | 5 | 3 |
| K | 输入维 = hidden_size | 7168 | 8 |
| N | 输出维 = 路由专家的中间维(moe_intermediate_size) | 2048 | 8 |
| Kb | K 方向的块数 = K ÷ 128 | 56 | 2 |
| Nb | N 方向的块数 = N ÷ 128 | 16 | 2 |
还有两处配套,缺一不可:
- 所有张量都用 E4M3。先前的做法前向 E4M3、反向 E5M2(梯度要范围);DeepSeek-V3 认为细粒度缩放相当于让一小块元素共享指数位,范围问题已经被缩放吸收,于是全用精度更高的 E4M3。
- 累加提升到 FP32。他们实测 H800 上 FP8 矩阵乘的累加器只保留约 14 位,K = 4096 的两个随机矩阵相乘,最大相对误差接近 2%;解法是每累加 NC = 128 个元素,就把部分和搬到 CUDA 核心的 FP32 寄存器里累加——第 2 课「FP32 累加」那一条,到了 8 位变得更要紧 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条约 14 位、近 2%、N_C = 128 都是原文数字,硬件是 H800。 出处DeepSeek-V3 §3.3.2(本地 PDF) ↗ 。
结果:在约 16B 参数训 1.33T token、约 230B 参数训 0.9T token 两档 MoE 模型上,FP8 训练相对 BF16 基线的 loss 相对误差始终低于 0.25%;报告称这是首次在超大规模模型上验证 FP8 训练 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条官方自报,未见第三方复现。更早的 FP8 训练框架有 FP8-LM(Peng et al. 2023)。 出处DeepSeek-V3 摘要、§3.3、附录 B.1(本地 PDF) ↗ 。
新的失败:块的形状要跟着离群值走
DeepSeek-V3 的附录记了一次失败:为了省事,他们试过让激活梯度也像权重那样按 128 × 128 的块量化(反向时只需转置,不用重新分组)。结果在一个约 16B 参数的 MoE 模型上训到约 300B token 时发散。他们的解释是:激活梯度在不同 token 之间极不均衡,离群值是「跟着 token 走」的,一个 128 × 128 的块横跨 128 个 token,一个坏 token 拖累整块 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条发散是实验事实;「token 相关的离群值」是作者的假设(原文用 hypothesize)。 出处DeepSeek-V3 附录 B.2(本地 PDF) ↗ 。
这正是上面热力图里看到的事:缩放粒度的形状要和离群值的形状对齐。离群值按通道分布,按通道切;按 token 分布,就得按 token 切。DeepSeek 自己后来也在收窄块:V4.1-Flash 的 config 里 weight_block_size 变成 [32, 32],缩放格式是 ue8m0(只存 2 的幂的 8 位指数),路由专家用 fp4——那是岔路课的 4 位。
什么没有变成 8 位
FP8 训练不是「所有东西都存 8 位」。DeepSeek-V3 明确保留在高精度的有:embedding、输出头、MoE 门控、归一化算子、注意力算子(BF16 或 FP32);主权重、权重梯度仍是 FP32;Adam 的 m、v 改用 BF16,「没有可观察到的性能下降」 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条原文列出的组件与格式;m、v 用 BF16 是官方自报。 出处DeepSeek-V3 §3.3.1、§3.3.3(本地 PDF) ↗ 。所以第 1 课的账本变成 4 + 4 + 2 + 2 = 12 字节/参数(不含计算用的低精度副本):
FP8 真正省下的是矩阵乘的算力与带宽,以及以 FP8 缓存的那部分激活;模型状态只从 16 字节降到 12 字节。报告也说,这些高精度部分的显存开销可以通过在数据并行的各卡之间分片来摊薄——那是 C10 的事。
常见误解一:「FP8 训练 = 所有东西都用 8 位存。」
8 位的是矩阵乘的输入(和一部分缓存的激活);主权重、梯度是 FP32,m、v 是 BF16,敏感算子留在 BF16 / FP32。账本是 12 字节/参数,不是 1。
常见误解二:「缩放因子取整张量的最大值就行。」
整张量一个缩放,等于让最大的那个离群值替整张量决定网格。越过 E4M3 正规区的动态范围(约 2.9 万倍)之后,小数成片掉进次正规区甚至变 0;缩放要按块做,块的形状还要跟着离群值走(附录 B.2 的发散)。
常见误解三:「E4M3 一定比 E5M2 好。」
两者是精度与范围的取舍:E4M3 多一位尾数,E5M2 多一位指数。没有细粒度缩放时,梯度需要 E5M2 的范围(FP8 提案的推荐);DeepSeek-V3 能全用 E4M3,前提正是一小块一个缩放。
证据三问
| 问 | 本课的回答 |
|---|---|
| ① 它解决什么问题? | 让矩阵乘用 8 位算(理论上比 BF16 快一倍),而不被 256 个位型的窄范围拖垮 |
| ② 数学上为什么可能有效? | 缩放把每一块的最大值对齐到 448,块内其余数只要不小于它的 1/28,672 就留在正规区,相对精度与格式本身一致 证据等级 · 数学可证明 在写明的前提下可以严格推导出来,不依赖实验。只要前提成立,换数据、换规模仍然成立。 本条E4M3 正规区 2⁻⁶ 到 448;浮点数在正规区内的相对舍入误差上界只由尾数位数决定。本页组件逐元素实算。 出处待补 ;块越小,「块内最大值 ÷ 最小值」越可能落在这个范围内——但这不保证训练收敛 证据等级 · 有理论动机 有数学或机制上的理由说明它可能有效,但没有证明它一定更好。最后是否采用,仍然要看实验。 本条块的形状要和离群值的分布对齐,这是经验与动机,不是定理;附录 B.2 就是反例。 出处待补 |
| ③ 实验上为什么被选中? | DeepSeek-V3 在两档 MoE 模型上 loss 相对误差 < 0.25%,随后用它训练了 671B 的正式模型 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条官方自报。 出处DeepSeek-V3 附录 B.1(本地 PDF) ↗ |
一句话
8 位只有 256 个位型,E4M3 最大 448;整张量共用一个缩放,一个离群值就把小值挤进粗网格——修法是每一小块各配一个缩放,累加回到 FP32。
先停在这里:记住「FP8 要先缩放;缩放按小块做,块的形状跟着离群值走;主权重和梯度仍是 FP32」就可以进第 5 课。
再深一层:走岔路课看 4 位;或者读 DeepSeek-V3 §3.3 和附录 B.2。
下一个缺口
参数、梯度、矩阵乘都压下来了,为什么一开长序列显存还是爆?
你可能会问
- 「推理时的 FP8 和训练时的 FP8 是一回事吗?」 格式一样,用法不同:推理只做前向,没有梯度的范围问题,缩放可以离线校准;训练的缩放要每一步在线算,还要覆盖反向的两个矩阵乘。C13 讲推理量化。
- 「为什么是 128?」 报告的说法是:NC = 128(相当于 4 次 WGMMA)是能显著提升累加精度、又不带来明显开销的最小间隔;缩放块沿 K 方向也是 128,两者对齐,缩放因子正好在部分和搬到 FP32 时乘上去(§3.3.2)。它是工程上的选择,不是推导出来的最优值——V4.1 把权重块缩到了 32 × 32。
- 「INT8 不行吗?」 整数格式的间距处处相等,离群值对它的伤害比对浮点大得多:一个大数会让所有小数都挤在最低的几个整数格里。FP8 自带指数,在正规区内相对精度不随缩放变化,这是它更适合训练的原因之一。