跳转到正文
技术周刊保持好奇,认真求证

AIGC标识 04 · 模型显存与训练的四本账:别再只用“几 B 配几 G”选硬件

04 · 模型显存,算清四本账:系列封面

定位:原理研究与算式复核。 本文整理 llm-memoryllm-training 教程。仓库没有因此完成一套模型训练或显存基准测试;文中数值是明确假设下的计算,不是设备实测。

“七十亿参数,半精度每个参数两字节,所以准备十四 GB”只算了一笔账:权重。模型真正运行时,还可能需要注意力缓存、中间激活、梯度、优化器状态和框架工作区。

显存问题最有效的解法不是背硬件口诀,而是把资源拆成可以分别解释、分别测量的账本。

一、第一本账:到底有多少权重需要常驻

最基本的估算是:

权重字节数 ≈ 常驻参数数量 × 每个参数的存储字节数

这里每一个词都重要。参数数量与架构有关;常驻参数不等于每个 token 激活的参数;量化后的平均位宽也不包含所有元数据、缩放因子和未量化层。

估算 Transformer 参数时,不能只看层数乘隐藏维度。词表嵌入、注意力投影、前馈层、归一化和输出头都有贡献。输入嵌入与输出头是否共享,可能带来很大的差别:以词表 128256、隐藏宽度 4096 为例,一张额外矩阵就约有 5.25 亿参数,不能当成舍入误差。

原始参数教程提供了拆解方法,但具体模型应以配置和实际张量为准。遇到 GQA、MoE、共享权重或不同前馈结构,更不能直接套用另一种架构的系数。

二、第二本账:KV Cache 随任务规模增长

对普通、完整缓存的解码器注意力,采用统一层配置时,可以估算:

KV 字节数 = 2 × layer_count × kv_head_count × head_dimension
             × sequence_length × batch_size × bytes_per_element

系数 2 对应 Key 与 Value。GQA 改变的是 KV 头数,不能把查询头数直接当作 KV 头数。sequence_length 是缓存中需要保留的 token 数,服务中的并发请求会使总量进一步增加。

以下算例可直接复算,无需下载模型:

layer_count = 16
kv_head_count = 8
head_dimension = 64
sequence_length = 1024
batch_size = 1
bytes_per_element = 2

kv_bytes = (
    2 * layer_count * kv_head_count * head_dimension
    * sequence_length * batch_size * bytes_per_element
)
print(kv_bytes / 2**20)

结果为 32 MiB。原始推理显存表的一行按其给出的配置应得到这个结果,而不是其中写出的约 16 MB。本系列采用重新计算的结果,不继续传播原表数字。

这仍然是缓存张量估算。分页分配、共享前缀、滑动窗口、混合层结构、缓存量化和碎片都会改变实际占用。GB 是十进制,GiB 是二进制;比较硬件、框架报告和公式时也要统一单位。

三、第三本账:中间激活与临时工作区

激活不是另一个固定参数包。它与序列长度、批量、训练或推理模式,以及注意力实现相关。长输入的 prefill 和逐 token decode 的资源形态也不相同,不能用一次短回答的峰值推断所有请求。

训练时,为反向传播保存的激活往往占据显著空间。梯度检查点通过少存中间结果、在反向阶段重算来换取显存;它降低的是部分激活开销,不会让主权重或全部优化器状态凭空消失。

框架还可能预留内存池、通信缓冲区和算子工作区。所以“理论张量之和低于显卡容量”只是必要检查,不是不会 OOM 的保证。

四、第四本账:训练状态取决于实现布局

原始资料列举了一种混合精度 Adam 布局:

每个参数关联状态 字节数
FP32 主权重 4
BF16 计算副本 2
BF16 梯度 2
FP32 一阶矩 4
FP32 二阶矩 4
合计 16

这一特定布局下,70 亿参数的静态状态约为 112 GB,也就是 104.3 GiB,还没有加入激活和工作区。它不是所有框架都必须遵守的常数,但足以说明:直接宣称某张小于这一静态账本的卡可以全参训练,必须补充状态精度、分片、卸载或优化器实现等条件。

LoRA 主要减少需要训练的参数及其梯度和优化器状态;QLoRA 还涉及基础权重的量化。两者不等于免除基础模型的前向计算与激活开销。ZeRO 或其他分片策略改变状态在设备间的分布,也带来通信和调度成本。

先区分运行模式,再计算显存;特定布局算例 · 未进行设备基准测试

图 04:推理缓存示例为 32 MiB。训练条带仅对应文中指定的混合精度 Adam 布局:每参数 16 字节,70 亿参数约 112 GB ≈ 104.3 GiB,未含激活与工作区。

五、训练阶段不同,资源与评估目标也不同

预训练、监督微调、偏好优化和带奖励的训练,不应该只按“训练一次模型”来估算。它们的数据结构、参考模型、奖励或验证过程可能不同。

后训练资料里有一处适合用来说明“公式也需要代码审查”的例子:DPO 应使用策略与参考策略的对数概率之差,不是将两个对数概率相除。用简化记号表示:

relative_logprob(answer) = log policy(answer | prompt)
                           - log reference(answer | prompt)

loss = -log sigmoid(
    beta * (relative_logprob(preferred) - relative_logprob(rejected))
)

这只是单个偏好样本的标准形式说明,不是完整训练脚本。实际实现还需处理序列概率、掩码、批量聚合、数值稳定性及所用算法变体。

同理,教程中的 TRL 配置片段、GRPO 伪代码和未定义辅助函数,应当作为理解思路的材料,而不是直接承诺可执行的工程交付。

六、从估算走向真实验证

建议采用以下顺序,避免一次运行混入太多未知量:

  1. 固定模型配置、运行时版本、存储精度和缓存策略。
  2. 分别列出权重、缓存、激活和训练状态的估算,统一字节单位。
  3. 从小批量、短序列开始,分别记录加载、prefill、decode 或训练步的峰值。
  4. 一次只增加一个维度:输入长度、输出长度、并发或批量。
  5. 对比理论值与实测差额,解释内存池、工作区、分片和碎片,而不是用一个任意“安全系数”掩盖差异。

结语

显存估算的价值不是算出一个看似精确的数字,而是知道每一项为什么存在、随什么增长、由什么策略改变。只有账本清楚,硬件选型、量化、微调和服务容量才能进入可验证的工程讨论。

posted @ 2026-09-16 08:59  哀莫  阅读(3)  评论(0)    收藏  举报