量化模型推理显存算不准,常见原因是只计算了权重,漏掉了 KV cache、激活峰值、运行时开销和碎片。一个可用的估算公式是:总显存 ≈ 量化权重 + KV cache + 单次前向的激活峰值 + 框架与 CUDA 上下文开销 + 余量。这个公式只能作为起点,真正要判断的是:在你的框架、序列长度和并发下,实际峰值落在哪里。
显存估算要按权重、KV cache、激活峰值、运行时开销和碎片余量五项相加,其中 4bit 权重不能只按参数量除以 2 计算,KV cache 要按层数、KV 头数和序列长度单独计算。估算值只能当起点,最终要在目标框架里用逐级加压的方式实测 OOM 边界,并为长上下文和并发留出余量;多卡、offload 和量化格式差异需要单独确认。
先确认当前场景和目标
先确认四类信息,再开始计算。第一是模型结构:参数量、层数、hidden size、KV 头数、head_dim、词表大小,这些参数决定权重和 KV cache 的量级。第二是量化格式:GPTQ、AWQ、GGUF、FP8 等不同格式的存储布局和 kernel 行为不一样,不能只用“参数量除以 2”推断。第三是推理形态:长上下文 prefill 和短输入高并发 decode 的显存峰值来源不同,前者往往卡在激活,后者往往卡在 KV cache。第四是部署条件:单卡还是张量并行、是否开 CUDA graph、是否有其他进程占用同一张卡。
判断条件可以很直接:如果目标是“能不能装下”,就按最大 batch 和最大序列长度计算峰值;如果目标是“还能开多大并发”,就固定模型和序列长度,逐步提高 batch 直到接近 OOM。两种情况不要混在一起估算。
按这个顺序处理
第一步计算权重。用参数量乘以每参数字节数,4bit 约 0.5 字节/参数,8bit 约 1 字节/参数。量化后还有分组 scale 和 zero point,比如 group size 为 128 时,每 128 个参数附带一份 fp16 的 scale 和 zero,折合到每个参数大约 0.03 字节,量级不大,但在 4bit 下不要直接忽略。embedding 和 lm_head 经常不量化或单独处理,需要看量化配置文件确认。

第二步计算 KV cache。常用公式是:2 × 层数 × KV 头数 × head_dim × 序列长度 × batch × 每元素字节数。GQA 模型要把 KV 头数换成实际的 KV 头数,不是注意力头数。如果框架对 KV cache 做了分页或量化,还要按该格式的存储粒度取整。
第三步给激活和运行时留出空间。激活峰值跟 batch、序列长度和中间维度强相关,prefill 阶段通常远大于 decode 阶段,不建议只靠公式硬算,可以先按框架实测值反推。CUDA 上下文、CUDA graph 池、通信 buffer 和张量并行带来的额外副本也要算进去,单卡上保守留出数百 MB 到 1 GB 量级比较稳妥,具体数值需要结合环境确认。
# 只做量级估算,所有值替换成你自己模型的配置
P = 7e9
w_bytes = 0.5 # 4bit 约 0.5 字节/参数;8bit 用 1.0
L = 32
kv_heads = 8 # GQA 的实际 KV 头数,不是注意力头数
head_dim = 128
seq = 4096
batch = 4
kv_bytes = 2.0 # KV cache 精度,fp16 为 2 字节
weight = P * w_bytes
kv = 2 * L * kv_heads * head_dim * seq * batch * kv_bytes
print("weight GiB", round(weight / 2**30, 2))
print("kv GiB", round(kv / 2**30, 2))
# 再人工加上激活峰值、框架开销和余量第四步把结果落到具体部署上。单卡部署就把各项相加;张量并行要按切分方式分别核对每张卡的权重、KV cache 和通信 buffer,不能简单把总量除以卡数。使用 CPU offload 时,显存会下降,但推理速度也会变化,这属于保底手段,不适合当作性能方案。

改完后怎么验证
先用 nvidia-smi 记录空载时的显存占用,作为基线。再在目标框架里加载模型,记录加载完成后的占用,两者之差基本就是权重加运行时开销。然后固定输入长度,逐步提高 batch,或固定 batch 逐步加长输入,观察峰值显存和是否 OOM。框架侧可以看 torch.cuda.max_memory_allocated(),它反映的是分配器统计,和 nvidia-smi 的进程占用会有差距,两个数都要记录。
预期信号是:加载后占用接近权重估算值;提高 batch 时 KV cache 近似线性增长;长输入时激活峰值明显抬升。如果实测远高于估算,优先检查是否有多余的 fp16 副本、量化 kernel 是否真正生效、是否开启了不必要的 CUDA graph 或缓存池。如果实测低于估算很多,也不要急着下调余量,先用更长序列和更高并发复测。
容易忽略的边界
量化格式和推理框架必须匹配,权重量化了但 kernel 没走量化路径,显存和速度都可能不符合预期。FP8 依赖硬件支持,老卡上不能按 FP8 估算。张量并行、流水并行会改变每卡的实际占用,跨卡通信 buffer 也要计入。显存碎片在长时间运行和动态 batch 下会累积,估算时要留余量;共享显存或 MIG 环境下,nvidia-smi 看到的容量不等于可用容量。出现 OOM 时的回退顺序建议是:先降 batch 或序列长度,再关掉 CUDA graph 和额外缓存,最后才考虑 offload 或换卡,并且每次只改一个变量再复测。