精度与显存:一个参数要几字节
主线定位
- 上游 · C8 稳定性:放大后为什么会炸:C8 让模型放大了也训得稳,并用 μP 把小模型上调好的超参搬到大模型。真要训锚点 Qwen3.8-27B,第一个问题变成了「放不放得下」。
- 本章:回答「训练一个参数要几字节、每个数用几位、激活为什么比参数还大」。结论是:模型状态 16 字节/参数(混合精度 + Adam);16 位靠 BF16 + FP32 主权重,8 位靠细粒度块缩放,4 位靠更小的块和 QAT;激活靠 FlashAttention 与重计算。
- 下游 · C10 并行:一张卡不够怎么办:所有招数用完,锚点的模型状态仍是 444.5 GB,是 80 GB 单卡的 5.6 倍。C10 第 2 课(ZeRO)把这本账切到多张卡上。
这一章回扣了前面好几处伏笔:C4 岔路课「训练约 278 亿参数的模型要多少显存」、C5 第 2、3 课「m、v 各 4 字节,完整的显存账在 C9」在第 1 课兑现;C4 第 3 课「ε 在低精度下」、C8 第 4、5 课「logit 漂到上千时 BF16 还分得清吗」在第 3 课兑现;C0 第 4 课「不把整张分数表写出来」、C6 第 1 课「开了重计算实际约 8ND」在第 5 课兑现;C3 第 8、10 课「把缓存量化到更低精度」在岔路课有一个真例子。
本章问题链
每一课只回答一个问题;上一课的失败,逼出下一课的问题。
| 课 | 这一课的问题 | 朴素方案在哪个真数字上失败 | 一句话 |
|---|---|---|---|
| 第 1 课 | 训练一个参数,要几字节? | 只算权重:55.6 GB「装得下」;训练一步实际要 444.5 GB | 训练时一个参数 16 字节:16 位权重、梯度各 2,FP32 主权重与 m、v 各 4 |
| 第 2 课 | 全换成 FP16,为什么会丢梯度? | fp16(10⁻⁸) = 0;fp16(1 + 10⁻⁴) = 1;fp16(70,000) = inf | 指数只有 5 位:小梯度下溢、小更新被吞;修法是 FP32 主权重 + loss scaling |
| 第 3 课 | 能不要 loss scaling 吗? | FP16 的窗口只有 40 个 2 的幂,缩放只能平移;换 BF16 后 bf16(257) = 256 | BF16 用尾数换指数,范围同 FP32、不用缩放;精度粗到 1/128,主权重仍要 FP32 |
| 第 4 课 | 8 位还能训吗? | 整张量缩放:[1000, 0.01, 0.02, 0.5] 里 0.01 的误差 12.8% | 一小块一个缩放(1 × 128 / 128 × 128),块形状跟着离群值走,累加回到 FP32 |
| 岔路 | 4 位只剩 16 个数,怎么训? | 块 [0.3, −1.2, 2.7, 0.05] 用 MX 缩放后 0.05 → 0 | 更小的块 + 各自的缩放;2026 年主要是 MoE 专家权重的 QAT |
| 第 5 课 | 激活为什么比参数还占显存? | 全存:锚点一条 4,096 token 的序列约 162.5 GiB,注意力分数占 73.8% | 激活由 s、b、h、L 决定,分数项带 s²;FlashAttention 不写出它,重计算用算力换显存 |
| 实验课 | 四种精度在我们的模型上比一比,再把显存账对一遍 | — | 只改精度,先预测再看曲线、下溢比例和显存截面 |
演进地图
- 解决了范围到 ±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 并行。
贯穿任务与例句
贯穿任务:在一张 80 GB 的卡上训练锚点 Qwen3.8-27B 的一步。每一课在同一本账上多记一笔:第 1 课记模型状态,第 2–4 课决定每个数用几位,第 5 课补上激活。
贯穿例句「小明把苹果给了小王」(Qwen3.8 真 tokenizer 切成 5 个 token)是这一步的那一批数据:它的激活只有几十 MB,模型状态已经 444.5 GB;把序列拉到 4,096,激活就反超了参数本身。第 2–4 课讲浮点格式时换成追踪「一个参数 w 和它的一次更新 Δw」,每课开头写明了原因。
三层交互
| 层 | 本章里的组件 | 在哪一课 |
|---|---|---|
| 概念动画 | 一次迭代的显存时间线(可播放:前向攒激活、反向放激活长梯度、step 分配 m、v);loss scale 把整批梯度沿数轴平移;浮点位条逐位翻转 | 第 5 课、第 2 课、第 2–3 课 |
| 算法模拟器(真算法、小数据) | FP32 / FP16 / BF16 / FP8 / FP4 手写编解码(与 torch 逐值对照);同一个 w 连加 1,000 次的累加被吞;E4M3 与 E5M2 全部 256 个位型;整张量 vs 1 × 128 vs MX / NVFP4 块缩放的误差热力图;显存账本与 Korthikanti 激活公式(s、b 滑杆) | 第 1–5 课、岔路 |
| 真模型 / 真数据 | 锚点与前沿模型的官方 config(param_total、DeepSeek-V3 与 V4.1 的 quantization_config);我们自己从 0 训练的小模型:四种精度的训练日志、下溢探针的真实梯度直方图、4 个实测显存截面(项目数据生成后显示) | 第 1、3、4、5 课、岔路与实验课 |
项目推进:给我们的模型定训练精度,并对上显存账
对应 04-实践项目/MILESTONES.md 的「C9 精度与显存」。分工照旧:代码 AI 来写,决策你来做。项目里 train.precision(fp32 / bf16 / fp16,autocast)、手写的动态 loss scaler(日志含 loss_scale、skipped_steps)、每步恰为 0 的梯度比例、显存分项都已实现,实验脚本 experiments/exp_c9_precision.py 导出本章组件读取的 c09-precision.json。
你要拍板的三件事
- 用 fp32、bf16,还是 fp16 + 动态缩放?理由要同时引用 loss 曲线、跳过步数与下溢比例(第 2、3 课与实验课)。
- fp16 为什么需要 loss scaling,bf16 为什么不需要?用下溢探针的直方图回答:落在 2⁻²⁴ 以下的比例、乘 65,536 之后的比例(第 2、3 课)。
- 显存账:手算模型状态与激活,与实测分项对账(第 1、5 课)。
验收标准(全部可测):
- 对照表包含 tokens/s、显存、val loss、跳过步数四列(实验课第一部分)。
- 模型状态的理论值:FP32 + AdamW 16 字节 × 3,688,704 = 56.3 MiB,与实测「第一步后」的张量占用对比,差额由激活解释——写出按第 5 课公式的估算与实测差多少。
- fp16 不缩放时,平均「恰为 0 的梯度」比例高于 fp16 + 动态缩放(方向可测,幅度以实测为准);fp16 + 动态缩放的跳过步数写进报告。
坑与带走
| 坑 | 症状 | 后果 | 怎么办 |
|---|---|---|---|
| 按「参数量 × 2 字节」估训练显存 | 一开训就 OOM | 低估 8 倍(还没算激活) | 模型状态 16 字节/参数(第 1 课) |
| 以为混合精度把显存减半 | 换了 BF16 还是放不下 | 模型状态一个字节没省 | 混合精度省的是算力、带宽与激活;要省状态,看 m、v 的格式、优化器与 ZeRO(第 1 课、C10) |
| 用 FP16 训练却不做 loss scaling | loss 降得比 FP32 慢;一部分参数几乎不动 | 小梯度下溢成 0 | 用 BF16;或 FP16 + 动态 loss scaling(第 2、3 课) |
| 在 16 位的权重上直接做更新 | 长时间训练后 loss 停滞 | 小更新被舍入吞掉 | 更新落在 FP32 主权重上(第 2、3 课) |
| FP8 用整张量缩放 | 部分层的量化误差大,甚至发散 | 小值掉进次正规区或变 0 | 细粒度块缩放,块的形状跟着离群值走(第 4 课) |
| 按参数量估激活(旧版组件的错误) | 拉长序列就 OOM | 漏掉了与参数量无关、还带 s² 的那部分 | 按 sbh(34 + 5as/h) 估;FlashAttention、重计算(第 5 课) |
| 以为 autocast 把参数也变成了 16 位 | 显存账对不上 | 误判省了多少 | autocast 只让算子在低精度里算,参数、梯度、优化器状态仍是 FP32(实验课) |
| 拿推理显存估算器估训练 | 预算差好几倍 | 买错卡、配错并行 | 训练 = 模型状态 + 激活 + 临时缓冲,分项算(第 1、5 课) |
这一章你应该带走的(各课「一句话」):
- 训练时一个参数不是 2 字节而是 16 字节:16 位的权重和梯度各 2,FP32 主权重与 Adam 的 m、v 各 4;锚点 27.8B 就是 444.5 GB,还没算激活。
- FP16 丢梯度,是因为指数只有 5 位:比 2⁻²⁵ 还小的梯度直接变 0,比权重小 2¹¹ 倍以上的更新被吞;修法是 FP32 主权重 + 把 loss 乘大、更新前再除回来。
- BF16 把 FP16 的 3 位尾数挪给指数,范围和 FP32 一样宽,于是不下溢、不用 loss scaling;代价是 1 附近间距粗到 1/128,主权重和优化器状态仍要 FP32。
- 8 位只有 256 个位型,E4M3 最大 448;整张量共用一个缩放,一个离群值就把小值挤进粗网格——修法是每一小块各配一个缩放,累加回到 FP32。
- 4 位只有 16 个码点,只能靠「更小的块 + 各自的缩放」撑住;2026 年它主要以 QAT 的形式用在 MoE 专家权重上,全 FP4 预训练仍是前沿。
- 激活由 s、b、h、L 决定,和参数量无关;注意力分数那一项带 s²,序列一长就压倒一切——FlashAttention 不写出这张表,重计算用多算一遍前向换不存。
深入这一章
每条都实际打开核对过(2026-09-26):写清它讲清了什么、对应哪一课、为什么推荐。外部链接都需要联网;本地 PDF 可离线打开。
🎮 想更直观 10–30 分钟
🔬 想吃透原理 1–3 小时
📄 想读原文 按需
排除了什么(也是筛选标准):① ZOMI 酱 AI System 系列——它的仓库里混合精度、重计算两章写着「这个内容还没有」,搜到的一条低比特量化视频讲的是推理;② 1–3 分钟的速通或面试向短视频(如 BV1NbNi6ZEfK、BV1ZhsJzcEqr、BV1hQh76JEdr)——只给结论,讲不清下溢与缩放;③ 偏推理显存或格式科普的视频(BV1aq7P6LEp6、BV1zPQGBfEHu、BV1SZ42187du)——和训练的精度问题不对口;④ 阿里云开发者社区《大模型训练显存优化实战指南》(旧版课程的侧栏资料)——「80% 的显存来自优化器状态」没有出处,显存饼图是示意比例,与「激活随 s、b 变化」相矛盾;⑤ HF 的模型显存估算器(旧版收录)——旧版页面自己注明它算的是推理加载显存、不含激活,拿来估训练会严重偏低;⑥ NVIDIA FP8 博客的中文机翻版——把 current scaling 译成「电流缩放」。另注:B 站上还有两条播放量低、内容未能核实的精度视频(BV1L8hF6rE61、BV1qhMtz5E21),暂不收。