GGUF 文件实测发现 Q4_K_M 格式实际体积比参数估算大 23%,且长上下文下 KV 缓存可超过模型本身权重。附 GGUF 实际大小对照表和显存估算公式。
"每十亿参数需要半 GB VRAM"——这是大家在为本地模型选硬件时反复念叨的经验法则,而且效果还不错——直到模型加载完成、运行几分钟后,在对话过程中因内存溢出错误而崩溃。模型是装进去了,但上下文没留出空间。我在 DevToolLab 上写了完整的计算公式,归结为两个大多数人都从不检查的数字。
首先,量化后的权重比量化名称暗示的要大。其次,在长上下文下 KV 缓存可以比模型本身还重。以下是实际发生的情况,已对照 Hugging Face 上的真实 GGUF 文件验证,而非纯粹算术推导。
Q4 听起来像是每个权重 4 位。乘以参数量再除以 8,一个 8B 模型应该是 4.01 GB。但下载实际的 bartowski/Meta-Llama-3.1-8B-Instruct-GGUF Q4_K_M 文件,大小是 4.92 GB,比算出来的多 23%。

这个差距不是四舍五入的误差。K-quants 在量化权重旁边存储了每块的 scale 和最小值,而且 "M"/"L" 变体会以更高精度保留少量敏感张量(embeddings、output layer)。后缀命名的是主导格式,而非整个文件的平均值。精度越高,开销越小:
如果需要一个规划数字,用 Q4_K_M 每十亿参数 0.61 GB。这就是 "每十亿参数 0.6 GB" 这个民间说法的真正来源。
权重是一次性成本。KV 缓存随对话中每个 token 增长,在开启新对话之前永远不会收缩。公式是每个 token 对应两个张量(K 和 V),每层一份:
cache_bytes_per_token = 2 x layers x kv_heads x head_dim x bytes_per_element
Llama 3.1 8B 有 32 层,head dimension 为 128,而且只有 8 个 KV head(不是 32——分组查询注意力在 query head 之间共享 key/value 投影,相比完整的多头注意力节省了 4 倍缓存)。代入 fp16 计算,得到每个 token 128 KB:
看最后一行。模型本身不到 5 GB。而在其宣称的 128k 上下文窗口之上,缓存是 17.18 GB——模型加上缓存共 22.10 GB,一个被大家称为"什么机器都能跑"的模型。这在 16 GB 显存的卡上装不下,在 24 GB 的卡上也剩不下任何空间。完整的分解步骤,包括可运行的脚本,都在我的另一篇文章里,演示了如何从任意模型的 config.json 推导这些数字。
以上都是针对一次对话。把一个模型放到 API 后面,两个人同时访问,缓存就要乘以正在处理的序列数量,因为每个序列都维护自己的 K/V 状态。四个并发的 8k 会话在这模型上就是 4.29 GB 的缓存,对抗 4.92 GB 的权重。十六个会话就是 17.2 GB,在 24 GB 卡上全没了,而任何单次对话看起来仍然完全够用。这就是 vLLM 等推理框架在分页注意力上投入如此之大的原因:朴素分配会为尚未生成的 token 预留空间。
如果你在为团队而不是自己的笔记本做容量规划,把缓存列乘以峰值并发数,而且在做任何其他操作之前先量化缓存。
Apple Silicon 会让这张表有些偏差,因为它是统一内存而非独立显存——32 GB 的 M 系列 Mac 可以装下一个模型加缓存,而那在独立 GPU 上需要 32 GB。它每 token 比同等 NVIDIA 卡慢,但天花板是总 RAM,而不是板载显存能装多少。
量化 KV 缓存。 最大的杠杆。llama.cpp 暴露了 --cache-type-k / --cache-type-v,Ollama 也有等效的 flag,切到 q8 大约能把缓存内存减半,而大多数人在聊天中根本不会注意到质量损失。
主动设置上下文。 运行时往往会按宣称的完整窗口预留空间,不管你用不用。把这个模型从 128k 降到 32k 能释放近 13 GB,而且大多数聊天/编码会话根本到不了 32k。
先降量化级别再降模型大小。 Q4_K_M 的 13B 通常比 Q8_0 的 8B 表现更好,内存占用也相近——更大的模型、更便宜的权重,在降到 Q3 以下之前都成立,而 Q3 以下质量会明显下降。
把层卸载到 CPU 而不是直接放弃。 llama.cpp 可以在 GPU 和 CPU 之间分割层。会变慢,有时候慢很多,但一个慢的模型比一个加载不了的模型强。
模型加载之后,检查真实数字而不是信任算术——分配器开销、CUDA context 和内存碎片都叠加在理论数字之上。在 NVIDIA 上,在长对话期间运行 nvidia-smi --query-gpu=memory.used,memory.total --format=csv 可以实时看到缓存增长。在 Apple Silicon 上,Activity Monitor 的内存标签页显示同样的信息,而且要注意机器开始 swap 的迹象,表现为 tokens/sec 崩塌而非干净的报错。
缓存问题的典型信号:生成在前几次交互时很快,然后随着对话增长急剧变慢或崩溃。权重在启动时加载一次,所以后期才出现的故障几乎总是缓存吃掉了你没有预算到的内存。
要估算一个还没下载的模型的容量,从 Hugging Face 上它的 config.json 拉四个字段:num_hidden_layers、num_key_value_heads、hidden_size 和 num_attention_heads(head dimension 是 hidden_size / num_attention_heads)。特别关注 num_key_value_heads——当它等于 num_attention_heads 时,模型使用完整的多头注意力,其每 token 缓存比同等规模的 GQA 模型大好几分。正因为这个原因,两个 8B 模型在缓存内存上可以相差 4 倍。
本地模型容量规划其实是两个独立的计算:参数量乘以实际每权重位数得到权重,再乘以层数、kv_heads、head_dim 和每 token 字节数得到缓存。在长上下文下,第二个数字通常比第一个更重要。买卡之前两个都要检查——DevToolLab 的 Data Storage Converter 处理 GB 和 GiB 的转换,File Size Converter 可以用来核对下载的 GGUF 与仓库标称大小是否一致——而且在考虑妥协模型大小之前先量化缓存。
Local LLM VRAM Requirements: How Much Memory You Actually Need - 原文,附完整可运行的容量规划脚本
bartowski/Meta-Llama-3.1-8B-Instruct-GGUF on Hugging Face