前两天群里又有人拿着一张 8G 显存的卡问:能不能本地跑 7B 的 LLM?这个问题如果只回答"能"或者"不能",那基本等于没答——同样是 7B,FP16 半精度加载权重就要吃掉 14GB,INT4 量化后才 4GB 出头;同样是推理,聊 2K 上下文和聊 32K 上下文,KV Cache 的差距可能再拉开几 GB。显存预算从来不是一个固定数字,而是一道需要按权重、KV Cache、运行时开销逐项相加的算术题。
这篇就把这道题的每一笔账单拆开,给你一套能直接从参数量换算到显存需求的计算公式,并用 7B/14B/32B/72B 四档常见模型实际算一遍。不管你是要在服务器上部署生产接口,还是用 Ollama 在家里显卡上跑着玩,都能照着预估,少走弯路。
1. 显存账单拆解:跑一个大模型,钱到底花在哪几块
先说结论:推理显存 = 权重显存 + KV Cache + CUDA Context/运行时 + 激活值与临时缓冲。其中权重是固定开销,KV Cache 随序列长度增长,另外两项数值虽小,但往往决定了你离 OOM 还有多远。
1.1 权重参数:只要加载就逃不掉的"底薪"
服务端推理的大头永远是模型权重。一个稠密 Transformer 模型里,embedding、Q/K/V 投影、FFN 的多个线性层、LayerNorm 之类的参数全部要驻留在显存里。以 7B 模型为例,如果参数用 FP16/BF16 保存,每个参数占 2 字节,总权重就是约 14GB;用 FP32 就是 28GB;用 INT4 量化才降到 3.5GB 左右。这也是为什么很多人第一次看到"7B 模型要求 16GB 显存"会觉得夸张——如果你下载的是 .safetensors 原始 FP16 权重,16GB 几乎是下限,而不是上限。
这里说的"底薪"是指,只要模型加载到显存,这笔钱就固定支出,跟你对话长短没有关系;你只能通过降低精度来压它。量化是最直接的手段,也是本地部署玩家最常用的手段。
1.2 CUDA Context 与运行时:容易漏算的"房租"
很多初学者对着权重一算:"7B FP16 = 14GB,我 16GB 卡刚好够。"结果一跑就 OOM,漏掉的就是运行时开销。PyTorch/CUDA 在初始化时会在显存里建立 context,通常 300MB~1GB;一些推理框架还会预分配内存池,例如 PyTorch 的 caching allocator 会保留已释放的块不放回驱动,导致 nvidia-smi 里看到的显存占用比实际"正在使用"的高。
用 Ollama 这类 llama.cpp 衍生项目时,驱动加载和计算图的开销相对小一些,但任意框架在 prefill 阶段都会出现瞬时峰值。我的建议是:算完权重显存后,至少再留 1~2GB 给"房租"。如果卡上同时还要跑桌面、浏览器和编码软件,这个余量还得加大。
1.3 KV Cache:随上下文长度长大的"计件工资"
Transformer 做自回归生成时,每生成一个新 token 都要重新计算它跟之前所有 token 的注意力。为了不做重复计算,框架把历史 token 的 Key(K)和 Value(V)矩阵缓存下来,这就是 KV Cache。它只缓存 K 和 V,不需要缓存 Query,因为 Q 是当前生成步临时算的。
KV Cache 占多少,主要由四件事决定:层数、KV 投影维度、上下文长度、并发序列数。精度通常是 FP16 或 FP8。这也是为什么短对话你感觉不到它,一旦上传长文档或打开 32K 上下文,显存就肉眼可见地涨。GQA(分组查询注意力)能显著瘦身,具体逻辑放在第 2 节公式里一起说。
1.4 激活值与临时张量:prefill 瞬间的"突击队"
很多人只看权重和 KV Cache,结果做长 prompt 时照样爆显存。原因是 prefill(预填充)阶段会把整段 prompt 的所有 token 一次性并行送进模型,激活值在那个瞬间被放大到接近"权重 × batch × seq"的规模。
比如你给它喂一段 8K 的 system prompt 或检索结果,即便生成阶段 KV Cache 不大,prefill 的瞬时峰值也容易高出 1~2GB。这也是很多部署文档强调"显存要看峰值,不是看平均"的原因。如果你打算让它处理长文本,务必用一块有富余显存的卡做测试,别拿"短对话看起来很稳"当做结论。
2. 一套从参数量到显存需求的通用计算流程
2.1 权重显存:参数规模乘每参数字节数
最常用的公式先给出来:
模型权重显存(GiB) = 参数量 × 单个参数字节数 ÷ 1024³精度对照如下:
| 精度 | 单个参数占用 |
|---|---|
| FP32 | 4 字节 |
| FP16 / BF16 | 2 字节 |
| INT8 | 1 字节 |
| INT4 | 0.5 字节 |
举例,7B FP16:7×10⁹ × 2 = 14×10⁹ 字节;除以 1024³ 约等于 13.04 GiB;用十进制 GB 显示则是 14.0GB。这里的差异容易引起困惑——nvidia-smi 显示的是二进制 MiB(1 MiB = 1048576 字节),而显卡标称的 16GB 实际上是 16×1024³ = 17.18 十进制 GB。写文档和博客的人经常混用 GB/GiB,实际差了 7.4%,所以部署时别卡着理论值买卡。
用 Python 算可以这样写:
def weight_gib(params: float, bits: int = 16) -> float: return params * bits / 8 / (1024 ** 3) print(weight_gib(7e9, 16)) # 约 13.04 GiB print(weight_gib(14e9, 4)) # 约 6.52 GiB print(weight_gib(32e9, 4)) # 约 14.90 GiB注意,所谓"7B"往往是近似数,像 Qwen2.5-7B 实际参数量是 7.61B,8B 模型通常 8.03B。真要精算,去模型卡页看 safetensors 文件总大小最靠谱。
2.2 KV Cache:MHA 粗算和 GQA 精算,结果能差好几倍
完整的 KV Cache 公式如下:
KV Cache 字节数 = 2(K 和 V 两组矩阵) × 2(FP16 每个元素 2 字节) × num_layers(层数) × seq_len(当前上下文长度) × batch_size(并发序列数) × num_kv_heads × head_dim(KV 投影总维度)如果模型没有用 GQA,即 MHA(多头注意力)模型,那么num_kv_heads × head_dim就等于hidden_size,公式可以简化成2 × 2 × layers × seq × batch × hidden_size。很多粗算文章用的就是这版,所以你在网上看到的不同数字经常对不上。
但如今主流开源模型基本都上了 GQA。以 Qwen2.5-7B 为例:28 层,hidden 为 3584,注意力头 28 个,KV 头只有 4 个,head_dim 为 128,所以 KV 投影总维度 = 4 × 128 = 512,只是 hidden 的 1/7。同样的序列长度下,GQA 的 KV Cache 比 MHA 模型小 7 倍。选模型优先选带 GQA 的,长文本场景差异极其明显。
2.3 把 7B/14B/32B/72B 代入公式算一遍
我以 Qwen2.5 系列的公开配置作为参考,这是本地部署最常见的开源系列之一:
| 模型 | 层数 | hidden size | 注意力头数 | KV头数 | head_dim | KV投影总维度 |
|---|---|---|---|---|---|---|
| 7B | 28 | 3584 | 28 | 4 | 128 | 512 |
| 14B | 48 | 5120 | 40 | 8 | 128 | 1024 |
| 32B | 64 | 5120 | 64 | 8 | 80 | 640 |
| 72B | 80 | 8192 | 64 | 8 | 128 | 1024 |
代入公式后,单条序列、batch=1 时的 KV Cache 理论值如下:
| 模型 | seq=2048 | seq=8192 | seq=32768 |
|---|---|---|---|
| 7B | 约 0.12GB | 约 0.47GB | 约 1.88GB |
| 14B | 约 0.40GB | 约 1.61GB | 约 6.44GB |
| 32B | 约 0.33GB | 约 1.34GB | 约 5.37GB |
| 72B | 约 0.67GB | 约 2.68GB | 约 10.74GB |
能看到,GQA 模型在 7B 级别时 KV Cache 其实不大,32K 上下文也就 1.88GB。真正让显存爆掉的主因还是权重,以及并发数上来了之后 KV Cache 同步翻倍。如果是生产服务,batch=8 时 KV Cache 直接乘 8,这个账必须提前算。
3. 显存预算总表:四档模型在不同精度下的实际需求
3.1 只看权重:FP16 / INT8 / INT4 的差距
把 4 档模型、3 种精度的权重显存列成一张表,一眼就能看清:
| 模型 | 参数量(约) | FP16/BF16权重 | INT8权重 | INT4权重 | 4bit量化后+KV Cache+运行时,建议最低显存 |
|---|---|---|---|---|---|
| 7B | 7.6B | 约 14GB | 约 7GB | 约 4.4GB | 8GB |
| 14B | 14.8B | 约 28GB | 约 14GB | 约 8.5GB | 16GB |
| 32B | 32.8B | 约 64GB | 约 32GB | 约 19GB | 24GB |
| 72B | 72.7B | 约 144GB | 约 72GB | 约 42GB | 48GB |
这里的 INT4 权重是按真实模型文件大小计的,不是理想的 0.5 字节/参数。因为 GGUF 文件还包含少量元数据、tokenizer、注意力偏置等额外开销,Q4_K_M 文件会略大于理论值。比如 Qwen2.5-7B 的 Q4_K_M 大约是 4.68GB,而不是 3.8GB。
实际选型时有个更快的经验法则:4bit 量化后,显存需求大概等于 参数量 × 0.6~0.7 GB。也就是 7B 约 4.5GB,14B 约 9GB,32B 约 20GB,72B 约 44GB,再加 2GB 系统余量。买卡选型用这个粗算非常快。
3.2 从显卡反推:8G/16G/24G/48G 分别能跑什么
反过来从常见显卡容量看:
- 8GB 甜点卡:7B 模型 Q4 量化 + 4K~8K 上下文问题不大,跑 3B/4B 类小模型更从容。14B 的 Q4 单权重大约 8.5GB,算上 KV 和运行时,8G 卡会非常紧张,建议直接放弃或上 Q3_K 加 CPU offload。
- 16GB 卡:7B 可以上 Q8 甚至 FP16 短文本;14B Q4 跑 8K~32K 上下文顺畅;32B Q4 太重,需要 offload 一部分层到 CPU。
- 24GB 卡:32B Q4 是甜点档,上下文可以开到很大;72B Q4 仍然放不下,只有靠量化加 offload 慢速跑。
- 48GB 卡:72B Q4 + 8K 上下文可以完整放进去。多卡方案另算,但要注意 PCIe 带宽对速度的影响。
我见过不少人拿 8G 卡硬跑 14B,把 num_ctx 压到 1024,最后模型倒是能出字,但质量明显下降,因为上下文窗口太小,稍微长一点的指令就截断了。显存不足时,优先降量化等级,其次降并发,最后才降上下文长度,这个顺序能让损失最小化。
4. 除了换更小的量化,还有哪些低显存运行手段
4.1 GGUF 量化等级怎么选
GGUF 是 llama.cpp / Ollama 的事实标准格式。以 7B 模型为例,不同量化等级的实际大小大约是这样:
| 量化等级 | 文件大小参考 | 效果评价 |
|---|---|---|
| Q2_K | 约 2.8GB | 掉点明显,应急用 |
| Q3_K_M | 约 3.5GB | 能跑,细节开始糊 |
| Q4_K_M | 约 4.4GB | 性价比之王 |
| Q5_K_M | 约 5.2GB | 接近原版 |
| Q6_K | 约 5.9GB | 几乎无损 |
| Q8_0 | 约 7.2GB | 接近 FP16 水平 |
| FP16 | 约 14GB | 原版 |
排序下来,Q4_K_M 是多数本地玩家的默认选择,显存富余就上 Q5_K_M。14B 的 Q4 大约 8.5GB,32B 的 Q4 大约 19GB,72B 的 Q4 大约 42GB,这些数字可以直接用来对照自己手里的卡。
4.2 上下文长度与并行数:Ollama 里改几个参数就能省显存
Ollama 默认上下文长度通常是 2048 或 4096,很多人不知道这个默认值可以直接覆盖。降低 num_ctx 是省显存最快的手段之一,因为 KV Cache 和上下文长度是线性关系。把 num_ctx 从 32768 降到 8192,KV 开销立刻少 3/4。
在 Ollama 里可以通过环境变量或 Modelfile 控制:
# 设置默认上下文长度 8192 OLLAMA_CONTEXT_LENGTH=8192 # 模型加载时指定并行数,越少越省显存 OLLAMA_NUM_PARALLEL=1 # 模型空闲后立即卸载,不驻留显存 OLLAMA_KEEP_ALIVE=0对单个模型写 Modelfile 更灵活:
FROM qwen2.5:7b PARAMETER num_ctx 8192 PARAMETER num_gpu 999num_gpu 999表示尽可能把所有层放到 GPU;如果显存不够,Ollama 会自动把一部分层放在 CPU 上,也就是 offload。OLLAMA_MAX_LOADED_MODELS=1可以保证同一时间只加载一个模型,避免多个模型互相挤占显存。
4.3 选有 GQA 的模型、开 Flash Attention,从结构上省内存
前面的公式已经证明,KV Cache 的大小跟注意力头设置强相关。选模型时优先选带 GQA 的,比如 Qwen2.5、Llama 3、DeepSeek 系列,同样的参数量拥有更小的 KV Cache。这在长上下文下能省出好几个 GB。
另一个手段是 Flash Attention。它不能减少 KV Cache 本身的存储,但能显著降低 prefill 阶段的激活值和注意力计算时的临时显存。在 vLLM 里开启enable_flash_attention,在 transformers 里用attn_implementation="flash_attention_2",都能让峰值显存降一截,长 prompt 场景效果尤其明显。
4.4 别拿稠密模型的公式套 MoE 模型
必须提醒一个高频误区:DeepSeek-R1 这种 MoE 模型总参数 671B,如果按稠密模型计算,INT4 量化后也要 400GB 左右,看起来个人电脑毫无希望。但 MoE 推理时不是所有参数都被激活,真正决定推理速度的是激活参数(约 37B),而决定显存占用的仍然是全部专家和共享参数的权重总和。
所以对 MoE 模型,你要用总参数量去估算文件大小和显存占用,而不是拿激活参数套公式。R1 这类 600B 级 MoE 想在个人电脑上跑,只能大幅量化加 CPU offload,速度属于"能出字但肯定不流畅"的级别;想跑得爽还是得靠多卡或者云服务。这个提醒很重要,因为现在"能否离线跑 DeepSeek R1"是高频问题,答案的核心就在权重总大小,而不是宣传里的激活参数。
5. 实测复盘:为什么公式算出来的显存,和 nvidia-smi 看到的不一样
5.1 Ollama 跑 7B Q4,实际吃多少
我自己在 8G 显存的卡上跑过 Qwen2.5-7B-Instruct Q4_K_M,刚启动后 nvidia-smi 看到约 4.3GB,正常对话窗口(num_ctx=4096 左右)稳定在 4.6~5GB。提高 num_ctx 到 32K 后,显存明显爬升,峰值接近 6GB。
这个实测基本验证了公式:权重约 4.4GB + CUDA context 0.3~0.5GB + KV Cache 几百 MB 到 1GB 多。用 PyTorch 原生加载 FP16 7B 的话,14GB 权重加 1GB 缓存,16GB 卡跑短文本刚好,开长上下文就危险。你看到的"动态显存变化",正是很多人对不上账的原因。
5.2 prefill 峰值:一个容易被忽略的尖峰
我用 vLLM 部署 7B FP16 模型时监控过一次:prompt 从 100 token 突然拉到 6000 token,显存瞬时比 decode 阶段多了 1.5GB 左右,然后又回落。这不是泄漏,是 prefill 的激活值峰值。
如果你的服务允许用户粘贴长文档,最好按最坏情况预留显存,或者在服务端限制最大输入长度,否则一定会有人帮你复现 OOM。我自己踩过这个坑后,凡是上线长文本服务,必加--max-model-len或--max-input-tokens限制。
5.3 显存不释放:Ollama 的 keep_alive 和 PyTorch 的缓存分配器
跑 Ollama 遇到"模型卸了显存还占着",十有八九是 keep_alive 机制在起作用:默认模型在结束会话后还会驻留显存好几分钟,方便下次快速加载。用OLLAMA_KEEP_ALIVE=0可以彻底关闭。
PyTorch 也有类似现象——caching allocator 会缓存已释放的显存块,表面上进程一直占着好几 GB,实际是"保留未用"。这时候看 nvidia-smi 会高估真实需求;更准确的指标是看进程的峰值内存,或者用torch.cuda.max_memory_allocated()查看峰值分配量。
5.4 我的显存检查小流程
最后分享一个我自己的操作顺序,照着走基本不会翻车:
- 先用公式估算:权重 + KV Cache + 1.5GB 运行缓冲;
- Ollama 场景直接看
ollama ps,它会列出每个模型当前的显存占用和处理器分布; - 用
nvidia-smi -l 1监控生成过程,重点看 prefill 瞬间的峰值; - 如果接近 OOM,优先减
num_ctx,其次换量化等级,最后才考虑换卡。
如果你也是在 8G 或 16G 这种甜点卡上折腾,建议先从最小的 3B/7B Q4 跑通整个流程,再往上加参数量,别一上来就挑战 72B。显存这道算术题,算明白了,后面选模型、配量化、调上下文都是一马平川的事。