训练一个参数,要几字节?
本课一问:C8 让模型放大了也不炸。真要训练锚点模型 Qwen3.8-27B,第一个问题变成「放不放得下」——训练时,一个参数到底要占几个字节?
这一课先不碰:每个数该用几位、为什么(第 2–4 课);激活为什么比参数还大(第 5 课);一张卡放不下时怎么分到多张卡上(C10)。
从已有出发
C4 的岔路课把锚点模型的参数一个个数清了:27,781,427,952 个。C5 讲 Adam 时留了一句「m、v 各存一份 FP32,完整的显存账在 C9」。这一课把这本账算完。
贯穿全课程的那句话「小明把苹果给了小王」,在 Qwen3.8 里是 5 个 token。设想用它训练一步:取一批 → 前向 → loss → 反向 → 优化器 → 更新。先别管 16 位还是 8 位,就按最朴素的 FP32 训练,看这一步在显存里留下了什么:
点「反向传播」「优化器」「更新 θ」三个环节:梯度、m、v、权重,每一份都和参数一样多。这一批数据本身只有 5 个整数,占显存的是它流过模型时留下的东西。
- 解决了范围到 ±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 并行。
先自己设计:只算权重
最先想到的算法:参数有多少个,乘上每个参数的字节数。锚点 Qwen3.8 的权重以 BF16 发布,每个数 2 字节:
- ① 朴素方案
- ② 它在哪失败
- ③ 改进方案
同一个模型,只把「推理」换成「训练」,堆叠条就从 80 GB 线的左边跑到了远远的右边。多出来的每一段是什么、为什么非存不可,下一节逐项讲。
改进:把训练一步要放的东西列全
训练一步要同时放在显存里的「模型状态」有五份,每一份都和参数一样多(下面按混合精度训练的标准做法记账,为什么要这样混着用,第 2 课讲):
| 这一份 | 谁用它 | 格式 | 每参数字节 | 锚点合计 |
|---|---|---|---|---|
| 16 位权重 | 前向、反向的矩阵乘 | BF16 / FP16 | 2 | 55.6 GB |
| 16 位梯度 | 反向算出来,交给优化器 | BF16 / FP16 | 2 | 55.6 GB |
| FP32 主权重 | 优化器在它上面做更新,再转成下一步的 16 位权重 | FP32 | 4 | 111.1 GB |
| Adam 一阶矩 m | 梯度的滑动平均(C5 第 2 课) | FP32 | 4 | 111.1 GB |
| Adam 二阶矩 v | 梯度平方的滑动平均 | FP32 | 4 | 111.1 GB |
| 合计 | 16 | 444.5 GB |
这正是 ZeRO 论文给出的记账方式,把公式逐项拆开:
这个账是纯算术 证据等级 · 数学可证明 在写明的前提下可以严格推导出来,不依赖实验。只要前提成立,换数据、换规模仍然成立。 本条每一份张量都和参数同形,字节数 = 参数个数 × 每个元素的字节数;只依赖「存哪几份、每份什么格式」这个前提。 出处待补 ,ZeRO 论文用同样的算法得出「7.5B 模型的模型状态需要 120 GB」 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条论文原文数字;2Ψ + 2Ψ + KΨ、K = 12 由核实 agent 在 PDF 原文核对。 出处ZeRO §3.1、图 1(需联网) ↗需联网 。锚点的账:444.5 GB,一张 80 GB 的卡要 5.6 张才放得下——还一个激活都没存。
试三件事:① 在上面「训练一步的账」里切到「FP32 + AdamW」:4 + 4 + 4 + 4,合计还是 16 字节,一个字节都没省;② 点堆叠条上最宽的几段,读出它们的代入式——m、v、FP32 主权重三段合起来是 333.4 GB;③ 再切回「推理:只存 BF16 权重」,看训练和推理差了多少倍(8 倍)。
常见误解一:「显存主要花在参数上。」
参数本身(16 位权重)只占 16 字节里的 2 字节。训练时最大的一块是优化器状态,FP32 主权重、m、v 三份合计 12 字节,是参数本身的 6 倍。这也是后面那些「省显存」的办法最先砍的地方:把 m、v 存成 BF16(DeepSeek-V3)、只存一份动量(Muon)、或者把它们切到多张卡上(C10 的 ZeRO)。
常见误解二:「混合精度把训练显存减半。」
对模型状态来说不是:FP32 训练 4 + 4 + 4 + 4 = 16,混合精度 2 + 2 + 4 + 4 + 4 也是 16。混合精度原论文确实说总显存「几乎减半」,但它说的是另一件事——FP32 主权重让权重那部分多了 50%,而它的模型里显存大头是激活,激活改存 16 位就省下了一半 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条原文口径:总显存以激活为主,激活存 FP16 使总体约减半。这和 LLM 的 16 字节账本是两回事,核实 agent 在原文核对。 出处Micikevicius et al. 2017 §3.1 与摘要(需联网) ↗需联网 。它省下的是矩阵乘的带宽与算力(16 位的 Tensor Core 快得多)和激活(第 5 课),不是这 16 字节。
常见误解三:「推理跑得动,训练就放得下。」
推理只存权重(外加 KV cache,C3 第 8 课),55.6 GB;训练的模型状态是它的 8 倍,444.5 GB。
🔍 为什么还要一份 FP32 主权重?16 位的权重不是已经在了吗?(第 2 课兑现)
因为更新太小。一个权重 1.0 每步只该动 10⁻⁴ 左右,可 16 位格式在 1 附近的间距是 2⁻¹⁰(FP16)或 2⁻⁷(BF16),比这次更新大得多——直接加在 16 位权重上,舍入回来还是 1.0,这一步白走了。所以更新在 FP32 的主权重上做,做完再转一份 16 位的给下一步前向用。第 2 课会在浏览器里让同一个 w 连加 1,000 次,看它在三种格式里各停在哪。
同一本账的几种记法
堆叠条的「方案」下拉框里还有三种记法,都是真实存在的做法:
- 再加一份 FP32 梯度累积缓冲:20 字节/参数。梯度累积(把几个小批的梯度加起来再更新)时,在 FP32 里累加更稳,多出 4 字节。Ultra-Scale Playbook 的显存一节同时给出了 16 字节与 20 字节两种口径 证据等级 · 数学可证明 在写明的前提下可以严格推导出来,不依赖实验。只要前提成立,换数据、换规模仍然成立。 本条记账算术;16 = BF16 参数 2 + BF16 梯度 2 + FP32 主权重 4 + Adam 8,20 = 再加 FP32 梯度累积 4。核实 agent 在原文核对(原文另有一句「12 bytes in total」与公式矛盾,属笔误)。 出处Ultra-Scale Playbook「Mixed precision training」(需联网) ↗需联网 。
- 单动量优化器:12 字节/参数。只存一份动量的优化器(带动量的 SGD;C5 第 7 课的 Muon 对矩阵参数也是一份),K 从 12 降到 8。这是算术对照:真实模型里 embedding、输出头仍用 AdamW,锚点这两张表合计 2,542,796,800 个参数,那部分省不了。
- DeepSeek-V3 式 FP8 训练:4 + 4 + 2 + 2 = 12 字节/参数。主权重和梯度留在 FP32,m、v 改存 BF16,不含计算用的低精度权重副本。第 4 课细讲。
极小真数字:我们自己的模型
锚点的数太大,换我们从 0 训练的小模型算一遍:3,688,704 个参数,按 FP32 + AdamW 训练(项目基线就是 FP32),16 × 3,688,704 = 56.3 MiB。
这 56.3 MiB 要和实测对账:实验脚本分阶段读显存(建模型后 / 前向后 / 反向后 / 第一步后),差额里最大的一块是什么,第 5 课和实验课回答。
证据三问
| 问 | 本课的回答 |
|---|---|
| ① 它解决什么问题? | 训练前先知道「放不放得下」:模型状态 = 参数个数 × 每参数字节数,混合精度 + Adam 是 16 |
| ② 数学上为什么是这个数? | 每一份状态都和参数同形,字节数直接相加 证据等级 · 数学可证明 在写明的前提下可以严格推导出来,不依赖实验。只要前提成立,换数据、换规模仍然成立。 本条记账算术,只依赖「存哪几份、每份什么格式」。 出处待补 |
| ③ 实验上呢? | 这本账不需要实验来支持;需要实验回答的是「能不能少存几份、存得更粗」而不伤训练——第 2–4 课和 DeepSeek-V3 的做法 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条把 m、v 改存 BF16「没有可观察到的性能下降」,官方自报。 出处DeepSeek-V3 §3.3.3(本地 PDF) ↗ |
一句话
训练时一个参数不是 2 字节而是 16 字节:16 位的权重和梯度各 2,FP32 主权重与 Adam 的 m、v 各 4;锚点 27.8B 就是 444.5 GB,还没算激活。
先停在这里:记住「推理 2 字节、训练 16 字节,最大的一块是 12 字节的 FP32 优化器状态」就可以进下一课。
再深一层:在上面的堆叠条里把四种模型、六种方案都切一遍,再展开「为什么还要一份 FP32 主权重」。
下一个缺口
16 字节里有 12 字节是 FP32。能不能全换成 16 位? → 第 2 课:全换成 FP16,为什么会丢梯度?
两处回扣先记下:这本账怎么切到多张卡上,C10 第 2 课(ZeRO)兑现;激活那一段现在还没画进来,第 5 课补上。
你可能会问
- 「推理时为什么只要 2 字节?」 推理不反向、不更新:没有梯度,也没有优化器状态,只剩权重(外加 KV cache,C3 第 8 课、C13)。所以同一张卡上,能推理的模型比能训练的大得多。
- 「LoRA、8 位优化器省的是哪一块?」 LoRA 冻住原模型权重,原权重不需要梯度和优化器状态,只给很小的低秩矩阵存这些(C12);8 位优化器把 m、v 压成 8 位。两者砍的都是那 12 字节,不是权重本身。
- 「H100 标 80 GB,真能用满 80 GB 吗?」 不能全用满:CUDA 上下文、分配器的碎片和临时缓冲都要占一部分。本章的 80 GB 线只是标称容量,放不放得下的判断都留了余量。