2026-10-27 · 深度笔记

显存不够时的三条退路:量化、分层卸载、换更小的模型

OOM 之后别立刻想到换卡。这篇按性价比排了三条退路——量化、分层卸载到内存、换小模型,并说明各自的真实代价和什么时候该停手。

云主机AI推理GPU显存

跑一个 13B 模型,24GB 显存的卡加载到一半报 torch.OutOfMemoryError。第一次遇到的时候我的反应是"换张更大的卡",然后去翻了半小时的规格表。

后来我意识到这个反应是错的——大部分 OOM 是不需要换卡就能解决的,而且换卡往往是最贵的解法。

现在我按这个顺序处理。

先搞清楚显存被谁吃了

OOM 报错只告诉你"装不下",不告诉你"是谁装不下"。分解一下,一次推理的显存大致是四块:

组成7B 模型大概占用说明
权重FP16 约 14GB / 4bit 约 4GB静态,加载后不变
KV cache随上下文与并发线性增长动态,最容易失控
激活值随 batch 和序列长度增长临时分配,峰值短暂
框架开销1GB 到 2GBCUDA context、算子工作区

我怀疑过很多次是权重装不下,后来加了一行日志才知道,真正爆掉的是 KV cache:权重只占了 4GB,剩下 20GB 全被 KV cache 和激活值吃光了,而这还是在并发只有 4、上下文只有 4K 的情况下。

原因是一个很反直觉的公式。KV cache 的大小正比于层数 × 隐藏维度 × 序列长度 × 并发数,而且它的增长不是线性的——序列长度翻倍,KV cache 翻倍;但如果你为了塞下 KV cache 而不得不减少并发,单请求延迟就会成倍上升,最后用户看到的是"偶尔特别慢"而不是"直接失败"。

退路一:量化

这是收益最大的一步,先做。

4bit 量化的 7B,权重从 14GB 降到 4GB 左右,腾出来的 10GB 可以全给 KV cache。相比换卡,量化几乎零成本。

我用的组合是:权重走 4bit(NF4 或 GPTQ 的 Q4 格式),KV cache 走 8bit。只量化权重是不够的,很多人漏掉第二步,权重省下的显存立刻被 KV cache 吃掉。

from transformers import AutoModelForCausalLM

model = AutoModelForCausalLM.from_pretrained(
    "model-path",
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype="float16",
    device_map="auto",
)

再配一个 fp8 的 KV cache:

from transformers import QuantizationConfig

qconfig = QuantizationConfig(
    kv_cache_quantization="fp8",
    kv_cache_dtype="fp8_e4m3",
)
model = model.generate(...)  # 生效

代价是精度。4bit 权重掉的是长尾知识,8bit KV cache 影响的是长上下文的检索精度。

我踩过的坑:把量化当成无损操作,直接拿量化版去做需要精确复现的基准对比,结果发现指标比原始版低了一截,还以为是代码写错了。后来改成始终保留一个 FP16 基线做对照,差异才算清楚。

退路二:分层卸载到内存

权重装不下,但机器内存可能还空着。把一部分层放在 CPU 内存里,需要时再传上来。

llama.cpp 的做法是把前若干层留在 GPU,剩下的放 CPU:

# 24GB 卡上跑 13B:GPU 放不下全部,留 20 层在显卡
llama-server -m model.gguf -ngl 20 -c 8192 --host 0.0.0.0 --port 8080

llama.cpp 这个路线的好处是它支持 GGUF,量化程度可选,而且 CPU 卸载几乎是零配置。

vLLM 那边对应的是 --cpu-offload-gb:

vllm serve model-path --cpu-offload-gb 8 --max-model-len 8192

代价很实在:PCIe 带宽会成为新瓶颈。PCIe 4.0 x16 双向约 32GB/s,H100 的 HBM 是 3TB/s 以上——差两个数量级。每生成一个 token,卸载的那几层权重都要过 PCIe 走一趟。实测下来,从 GPU 全量加载变成卸载 8GB,单 token 延迟能差三到五倍。

还有一个更隐蔽的坑:卸载量超过某个点之后,延迟会断崖式恶化。因为每一层都要等上一层算完,串行依赖下,卸载层的边际成本会被放大。我见过一个实例,卸载 4GB 时延迟只涨了 20%,加到 12GB 直接变成 4 倍。

所以我的判断是:分层卸载适合"能接受慢"的场景——内部工具、低频调用、离线批处理。不适合面向用户的在线服务。

退路三:换更小的模型

这条被严重低估了。

在换卡之前,先问一句:我为什么非要用 13B?业务里那些问题,7B 的量化版答得差吗?

我做过的对比里,一个 7B 指令微调模型在客服问答这类的模板化任务上,跟 13B 的差距远小于预期——因为这类任务的瓶颈是知识覆盖和检索质量,不是模型规模。而模型小一档带来的好处是一整套的:显存宽裕、并发翻倍、延迟减半。

真正需要大模型的地方,瓶颈往往是检索没做好,而不是模型不够大。我有过一次 13B 效果很差,把 RAG 的召回从 3 条提到 8 条、又加了 rerank 之后,7B 就够了。

三条退路的优先级

不是并列的,是有顺序的:

  1. 先量化,成本几乎为零,收益最大。
  2. 再算 KV cache。如果装得下权重但装不下 KV cache,那就降上下文长度、降并发、或者 KV cache 也量化。
  3. 然后才考虑分层卸载,前提是这个服务对延迟不敏感。
  4. 换小模型要和上面几条并行考虑——它的收益是结构性的(并发、延迟、稳定性一起改善),比任何显存技巧都划算。

我自己做过的最有效的一次组合:13B → 8B 量化版 + 4bit 权重 + fp8 KV cache + 上下文从 16K 降到 8K。在同一张 24GB 卡上,并发从 4 涨到 12,P99 延迟还降了。

什么时候才该换卡

只有两种情况:模型精度是硬要求(法律、医疗这类容错低的场景),或者上下文长度是硬要求(要处理整本手册),这时候显存是物理约束,没有技巧可绕。

其他时候,先把上面三条走完。我统计过自己过去一年的 OOM 事故,只有一次是真的需要更大显存。

价格和规格这类数字各厂商调整得很勤,文章的判断方法通用,但落到具体套餐上还是以官方页面为准。

接着读