KV 缓存压缩账:GQA、量化与分页的组合拳
长上下文推理的显存大头不是权重,是 KV 缓存——它随序列长度线性增长,决定了你能同时服务多少请求。压缩 KV 有四招:架构层(GQA/MQA)、量化层(INT8/INT4)、内存层(分页)、共享层(前缀缓存)。本文把四招的账算清,衔接 长上下文推理。
1. KV 显存账:先算基线
每 token 的 KV 大小(回顾 推理资源账):
例:Llama-3-8B(L=32, K=8, H=128, FP16)→ 128 KB/token;32K 上下文 → 4 GB/请求。一个请求吃掉 4GB,80G 卡最多 ~20 个并发长上下文请求——这就是压缩的动机。
2. 架构层:MHA → GQA → MQA
| 架构 | KV heads | KV/token(相对 MHA) | 质量损失 |
|---|---|---|---|
| MHA | 32(=Q heads) | 1× | 基线 |
| GQA | 8(分组共享) | 1/4 | 很小 |
| MQA | 1(全共享) | 1/32 | 明显 |
判断:GQA 是当前主流折中(质量损失小、KV 省 4 倍);MQA 省更多但仅适合对质量不敏感的场景。模型选型时就把 GQA 算进 KV 账,比事后压缩更省。
3. 量化层:KV INT8 / INT4
KV 用更低精度存储,推理时反量化为 FP16 计算:
| 方案 | KV 显存 | 质量影响 | 说明 |
|---|---|---|---|
| FP16 基线 | 2B/token | — | |
| INT8 | 1B/token(省 50%) | 极小 | 主流默认 |
| INT4 | 0.5B/token(省 75%) | 长上下文略降 | 需要校准 |
判断:INT8 KV 是"白捡"优化——质量几乎无损、显存省一半;INT4 在显存吃紧且可接受精度损失时用(与 模型压缩 的权重量化叠加效果更好)。
4. 内存层:PagedAttention 分页
KV 不是连续大块分配,而是像操作系统一样分页(vLLM 的 PagedAttention):
- 按 block(如 16 token/页)分配,碎片少、显存利用率高;
- 请求间共享前缀页(prompt 相同部分只存一份);
- 效果:同显存下并发请求数提升 2-4 倍(vLLM 的核心卖点,见 vLLM 原理)。
5. 组合拳:四招叠加的效果
| 组合 | KV/token | 32K 上下文/请求 | 80G 卡并发(约) |
|---|---|---|---|
| MHA+FP16 | 128 KB | 4 GB | ~18 |
| GQA+FP16 | 32 KB | 1 GB | ~72 |
| GQA+INT8 | 16 KB | 0.5 GB | ~144 |
| GQA+INT8+分页共享 | ~16 KB(去重后更低) | <0.5 GB | 200+ |
判断:架构(GQA)→ 量化(INT8)→ 内存(分页)三步走,每一步都"白捡",是长上下文服务从"十几个并发"到"数百并发"的完整路径。
6. 可复算 Python
python
def kv_compression(L, K, H, seq, bytes_per=2, head_share=1, quant=1, paging=1.0):
"""KV 显存(GB):head_share=GQA 共享倍数,quant=量化系数,paging=去重因子"""
per_token = 2 * L * K * H * bytes_per / head_share / quant * 1e-6 # KB
return per_token * seq * paging / 1e6 # GB
kv_compression(32, 8, 128, 32768, head_share=4, quant=2, paging=0.8)
# ≈ 0.21 GB/请求 vs MHA+FP16 基线 4 GB → 省 95%7. 交付清单
- 选模型先看 KV 架构:GQA 优先,KV 账按实际 K 算;
- 上线默认开 KV INT8,显存吃紧再上 INT4(跑质量回归);
- 推理引擎选支持分页+前缀共享的(vLLM 等),并发提升立竿见影;
- 长上下文场景用 验收基线 实测 KV 显存与并发上限;
- 组合拳每一步单独验证质量与收益,别一步全开难归因。
本文为本站整理思考后撰写,用于知识库学习交流;公式与算例为本站自行推导整理,具体版本与实测数据以现场为准。