当前位置:首页 > 编程开发 > 正文

128K 上下文要多少 KV cache:比权重大 7 倍

约 4295 字 · 预计阅读 11 分钟
一句话总结:一个 12B 模型权重常驻 3.34GB,跑 128K 上下文时 KV cache 要 25GiB,是权重的 7.5 倍;显存的大头从来不在权重,在随上下文线性增长的 KV。

上周有篇新闻挺唬人:12B 参数的模型压到 3.34GB 常驻显存,说是消费级显卡的福音。评论区一片"这下老卡也能跑大模型了"。我第一反应不是质疑压缩率,是想算另一笔账:权重是省下来了,上下文一开长,KV cache 会把省出来的部分加倍还回去。

这篇把 KV cache 这笔账完整算一遍,从公式到代码到优化手段。所有数字都来自实际运行的脚本,贴的是真实输出。

一个 12B 模型的账:3.34GB 权重和 25GiB KV

拿那个 12B 模型的公开架构做例子:d_model 5120、40 层、GQA 32 个注意力头配 8 个 KV 头、head_dim 160。这个配置不是我编的,来自模型发布页的架构描述,是消费级 12B 模型的典型形态。

KV cache 的公式一行就写完:2 × 层数 × KV头数 × head_dim × 序列长度 × 每元素字节数。乘 2 是因为 K 和 V 各存一份。代进去:2 × 40 × 8 × 160 × 131072 × 2 字节,等于 25.0 GiB。

对照权重 3.34GB,128K 上下文的 KV 是它的 7.5 倍。更扎心的是 32K 这个不算长的档位:KV 6.2GiB,已经是权重的 1.9 倍。也就是说上下文超过 30K 左右,"12B 模型只占 3GB"这句话就开始骗人了。

KV cache 公式只有一行,变量藏在层数里

把这个公式写成可复算的脚本,跑一遍对照几个常见规模:

def kv_gib(layers, kv_heads, head_dim, seq, bytes_per=2):
    return 2 * layers * kv_heads * head_dim * seq * bytes_per / 2**30

# 输出:
# 12B 架构(40 层、8 KV 头、160 维)各档位 KV:
#    4K:    0.8 GiB
#   32K:    6.2 GiB  (权重的 1.9 倍)
#  128K:   25.0 GiB  (权重的 7.5 倍)
#  256K:   50.0 GiB  (权重的 15.0 倍)

同为 128K 上下文,不同模型的 KV 差异也很大:7B 级架构(32 层、8 KV 头、128 维)要 16.0 GiB,上面这个 12B 要 25.0 GiB,70B 级(80 层、128 维)要 40.0 GiB。层数翻倍 KV 就翻倍,这个线性关系意味着越深的模型长上下文代价越大。

128K 上下文要多少 KV cache:比权重大 7 倍

图 1:权重固定 3.34GB,KV cache 随上下文档位翻倍增长,256K 时到权重的 15 倍

注意 head_dim 这个变量容易看漏:它是每个头的维度,不是模型总维度除以头数就完事。GQA 下 32 个查询头共享 8 个 KV 头,每个头的 head_dim 是 5120 除以 32 等于 160,KV 按的是这 160 和 KV 头数的 8,不是 5120。看 config 时把这两层除法都做对,算出来的数才对得上。

GQA 是唯一的减法:KV 头砍一半省一半

既然 KV 线性于 KV 头数,减头数就是最直接的省法。GQA 的设计初衷正在这里:32 个查询头共享 8 个 KV 头,比传统 MHA(KV 头等于查询头数)直接省了 4 倍。

往更狠的方向砍还有余地。把 8 个 KV 头减到 4 个,128K 的 KV 从 25.0 降到 12.5 GiB;减到 1 个(MQA)只剩 3.1 GiB,和权重一个量级。代价是能力损失:KV 头减半通常掉几个点,砍到 MQA 在长文本检索任务上损失明显。开源权重改不了结构,这条减法只对自训或微调的人有效。

# 输出:
# KV 头数对 128K KV 的影响(40 层、head_dim 160):
#   8 头: 25.0 GiB
#   4 头: 12.5 GiB
#   2 头:  6.2 GiB
#   1 头:  3.1 GiB

顺带一提,上一篇 27B 的 24GB 卡账里 40K 的上下文边界,用的就是同一套公式。两篇对照着看,会发现"模型多大"和"上下文多长"在显存里是两个独立轴,宣传时只说其中一个。

fp8 存 KV:最便宜的省显存开关

改不了结构的人有一个现成开关:KV 量化。fp16 存 KV 改成 fp8,25.0 GiB 直接变 12.5 GiB,128K 上下文从"两张卡"变"一张 24GB 卡加一点运气"。

# 输出:
# fp8 存储 128K KV: 12.5 GiB(省一半)

精度代价存在但要客观:KV 量化对输出质量的影响通常小于权重量化同等力度的影响,因为 KV 是临时缓存、生命周期只有一次请求,个别离群值影响的是单次注意力而不是永久权重。主流推理框架都提供这个开关,vLLM 和 llama.cpp 里各有一两个参数的事。

真要抠精度,还有折中路线:K 用 fp8、V 保持 fp16,或者只对超过一定长度的序列启用 KV 量化。这些混合策略在工程上都验证过,效果比一刀切稳。

什么场景会真的撞上这堵墙

写了这么多,撞墙的场景值得点名。第一是 agent 类应用,多轮工具调用把历史全塞上下文,几轮下来 32K 没了,KV 直接翻着倍涨;第二是代码库级问答,整个仓库几十万 token 喂进去,权重 3GB 的模型配 50GiB 的 KV,画面的荒诞程度不低;第三是多路并发服务,每路请求各有一份 KV,这笔更要单独算。按 12B 架构每 1024 token 消耗 0.195GiB 的比率,10 路并发各 16K 上下文,KV 总量 31GiB,再加权重和激活,一张 32GB 的卡已经爆了。并发服务和单用户试用是两道完全不同的题,前者要按「每路 KV 乘并发数」做容量规划,乘出来的数字经常比直觉大一个量级,这也是推理服务成本容易被低估的主因。

所以选模型时看两个数的乘积:权重决定它住多大房子,层数乘 KV 头决定它开长上下文时再买多大的房。3.34GB 的权重很便宜,25GiB 的 KV 才是房东递来的真账单。这笔账我在K3.1 Pro 部署那篇也算过一遍:1.56TB 的权重配 1M 上下文,单请求 KV 488GiB,比权重本身还吓人。

留个边界声明:本文架构参数取自模型发布页,正式容量规划请以 config.json 实际数值复算;KV 量化的精度影响各框架实现差异不小,上线前拿自己的任务回归一遍。

FAQ

Q:KV cache 为什么随上下文线性增长?

注意力机制要求每个已生成的 token 都保留 K 和 V 向量供后续 token 查询,token 多一个就多一份。没有任何机制让它饱和,这是架构决定的,不是实现问题。

Q:为什么用 KV 头数而不是总头数算?

GQA 架构下多个查询头共享同一组 KV 头,显存里只存 KV 头的份额。12B 例子中 32 个查询头共享 8 个 KV 头,省了 4 倍,这是 GQA 的核心价值。

Q:fp8 存 KV 会掉点多少?

没有统一数字,框架和模型差异大。经验上对常规任务影响在噪声级,长程精确检索任务可感知。建议 A/B 一遍自己的任务再决定,开销就是改一个配置项。

Q:PagedAttention 这类技术能省 KV 吗?

它省的是碎片不是总量。PagedAttention 把 KV 切成页管理,消除预留浪费,同卡能塞更多并发,但每个请求自身的 KV 字节数不变。

Q:官方宣传的上下文长度能信吗?

模型能力上可信,显存预算上是两回事。宣传口径说"支持 128K",你的卡得按 25GiB KV(本例)另算,两个数字都看再做决定。

目录导航
  • 一个 12B 模型的账:3.34GB 权重和 25GiB KV
  • KV cache 公式只有一行,变量藏在层数里
  • GQA 是唯一的减法:KV 头砍一半省一半
  • fp8 存 KV:最便宜的省显存开关
  • 什么场景会真的撞上这堵墙
    • FAQ
  • 相关文章:

  • 145B 模型怎么塞进 29GB:每秒要 400GB 带宽
  • RTX PRO 6000 多少钱:八周涨了 3700 美元
  • Qwen3.8-27B 要多少显存:24GB 卡装下什么
  • Kimi K3.1 Pro 要多少卡:1.56TB 权重
  • 144GB 显存反推:沐曦 C600 的带宽推不出来
  • B300 的 288GB 显存:单卡能装下多大模型
  • LongCat-2.5 需要多少显存:745GiB 起
  • 递归自我改进怎么落地:先优化推理,不造模型
  • 发表评论

    ◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。