最新资讯
dev.to 部署 Underdog Bark 3.0 Pro:逐字转写、自定义后端与分块设置 dev.to 为艰难对话构建 AI:五个 Agent、一条安全正则与一位盲评监督者 techcrunch.com Palo Alto Networks 洽谈以 5 亿美元收购 AI Agent 安全初创公司 Clutch theinformation.com 黄仁勋被要求就涉华间谍指控向国会作简报 www.infoq.com Edera 推出 Object Capability Bounded Authority,试图将凭证与 AI Agent 工作负载隔离 wechat 这波杀疯了!旗舰级模型不限量、1个月视频额度免费领 bloomberg.com AI 抛售蔓延至信用市场,软银领跌亚洲美元债 dev.to 如何在专用 GPU 服务器上用 OpenAI 兼容 API 部署 LLM dev.to 审计 153 份 Claude Code 与 Codex 会话记录:User-Agent 与 URL 中的邮箱泄露 dev.to 你的 Agent Harness 应具备的 17 项能力 dev.to 离线 AI 记忆中 0.8 余弦阈值为何失效 dev.to Kimi K3.1 Pro 开放权重:自托管 2.8T 模型前的开发者清单
多元动态
登录 注册
大模型 x.com · @Dongxi 东锡 NLP (@dongxi_nlp)

KV cache 原理详解:为什么保留 K/V 而不保留 Q,以及 prefix caching 与 DeepSeek-V4.1-Flash 的跨层共享

原文 我的收藏

文章从自回归生成出发解释 KV cache:prefill 阶段建立各层 K/V 缓存,decode 时新 token 只产生新的 Q/K/V,历史 K/V 继续被读取,因此 Q 用完即可丢弃。由于 causal attention 只能看到自身及之前位置,末尾追加问题不会使旧缓存失效,而修改开头内容会导致后续表示变化、缓存失效。文章给出显存估算示例:24 层、1 条序列、8 个 KV heads、head width 64、每元素 2 bytes 时,4096 位置约 192 MiB,8192 位置约 384 MiB。还区分了 prefill、KV cache 与 prefix caching,并提到 DeepSeek-V4.1-Flash 采用 Causal Encoder-Decoder 与跨层共享,第 21 层生成 decoder global KV bank 供第 21–40 层使用。

AI推荐理由 这是一篇关于 KV cache 原理的深度技术科普,正文具体解释了 prefill、KV cache、prefix caching 的区别,并给出 24 层、8 个 KV heads、head width 64、2 bytes 配置下 4096 位置约 192 MiB、8192 位置约 384 MiB 的显存估算,还讨论了 DeepSeek-V4.1-Flash 的 Causal Encoder-Decoder 与跨层共享设计。信息密度高、有可验证的计算示例,对理解推理优化和 Agent 长上下文成本有实际价值。局限是内容为教学性解释,DeepSeek-V4.1-Flash 相关描述来自作者转述,未提供官方论文或代码链接,且正文在关键处被截断。

78 浏览 32,623 点赞 220 评论 3 转发 39

正文

你把一份很长的项目文档交给 AI,让它帮你总结。 回答开始出现,一个 token 接着一个 token 生成。生成下一段内容时,模型仍然需要利用前面的文档和已经生成的回答。 难道每多写一个 token,都要把越来越长的全文从头再算一遍? KV cache 让模型保留了一部分已经完成的计算。 但它为什么保留 K 和 V?同样参与 attention 的 Q,为什么通常可以用完就放下? 这篇就来讨论这个问题,理解 KV cache。 每生成一个 token,都要从头再算吗? 把输入缩短为 “The sky is”,假设模型接着生成 “ blue”,再生成 “.”。 模型先处理整个 prompt,建立各层的 KV cache。这一步叫 prefill。最后一个 prompt 位置的输出经过 vocabulary head,得到用来选择第一个输出 token 的 logits。 选出“ blue”后,模型把它作为新输入,继续预测“.”。 看下面的动画:“blue”出现前后,哪些方块留在原位,哪些方块是新加入的? 已有 K/V 留在原位,新位置的 K/V 加在后面。新 token 仍然要经过模型计算,只是省去了对有效历史状态的重复计算。 留意顺序:选出“blue”时,它的 K/V 还没有生成;把它送回模型、预测后续 token 时,才会计算它的 K/V。 为什么留下 K/V,却通常不留下 Q? 在一个 attention 层里,每个位置都有一个输入向量,也叫 hidden state。三个可学习的投影把它变成 query(Q)、key(K)和 value(V)。 处理 blue 这个位置时,它的新 Q 会与 The sky is 以及 blue 自身的 K 计算匹配分数。 分数经过缩放和 softmax,变成 attention weights。再按这些权重对对应的 V 加权求和,得到这个 head 的输出。 下一位置会产生自己的 Q,但仍可能需要读取这些位置的 K/V。 历史 Q 已经完成了对应位置的计算,普通 decode 无需再次使用它。 把这个过程写成公式,就是下面这一行: 这解释了 KV cache 这个名字:K/V 继续供后续位置读取;Q 随新位置重新产生。 Context 变长了,旧计算为什么仍然有效? 回到项目文档。假设开头写着“预算为 100 万元”,后面是进度、人员和交付计划。 现在比较两个操作: 在文档末尾追加:“请总结风险。” 把开头的“100 万元”改成“50 万元”。 哪一种操作会让旧 cache 失效?先想一下,再看动画。 图中的字母代表 token 位置。 区别来自 causal attention 的信息方向:一个位置只能使用它自己和前面的输入,无法读取后来的内容。 所以,末尾追加问题,不会反过来改变文档位置已经完成的计算。修改开头的预算,则可能影响后续位置的表示,不能直接沿用原来的整份 cache。 也就是说,开头的 “100 万元”改成“50 万元”,影响了接下来每一个 token 生成时的 context,那么旧的 cache 就会失效。 新 token 到来时,究竟计算了什么? 现在放大“blue”被送回模型的那一步。只看一个 attention head:历史 K/V 不动,新位置产生 Q/K/V,新的 Q 读取可用的 K/V。 新 K/V 先加入 cache,再参与 attention,所以当前 Q 也能关注自身位置。这个 head 的输出被后续计算使用后,K/V 仍然保留,供下一位置读取。 图中的 Head output 还需要经过层内后续计算和模型其他部分,才能得到 next-token logits。在输入和执行条件兼容时,cached 与 uncached 结果应在适当的数值误差范围内一致。 省下重复计算,为什么又增加了显存开销? 项目文档越长,需要保留的位置就可能越多。使用随序列增长的完整 KV cache 时,新加入的位置会继续占用存储空间。 可以按维度数一遍:多少条序列、多少层、每层保留多少位置、多少个 KV heads、每个 head 多宽、每个元素占多少 bytes。 假设 K/V 分开存储、宽度相同、各层结构一致,乘起来就是: 给这份文档安排一个假设配置:24 层、1 条序列、8 个 KV heads、head width 为 64,每个元素占 2 bytes。 保留 4,096 个位置:KV tensor 共 192 MiB。 保留 8,192 个位置:增加到 384 MiB。 看动画里的两次变化:先增加位置,再回到原来的长度,比较 KV heads 更少的另一种配置。 同一份文档,换个问题,还能接着用吗? 你刚让 AI 总结完风险,又想知道截止日期。假设两条请求这样组织: 请求 A: [同一份项目文档] + “总结风险。” 请求 B: [同一份项目文档] + “列出截止日期。” 如果文档部分的实际 token prefix 完全一致,模型与执行设置兼容,而且系统仍保留这部分 cache,就可以复用它,再分别计算两个问题及其续写。这叫 prefix caching。 prefill,KV cache,prefix caching 三个词可以这样区分:prefill 是处理 prompt 的计算阶段;KV cache 是保存下来的 K/V 状态;prefix caching 是复用已有前缀状态的策略。 即使没有启用 prefix caching,普通生成也可以使用 KV cache。 vLLM 官方的典型例子是:针对同一份年报或软件手册反复提问。上面的项目文档就是同一种用法:第一次通过 prefill 建立缓存,后续命中共同前缀时,直接复用相应 K/V,省去这部分重复计算。 新的问题仍需继续做 prefill,并读取文档的 K/V;新的回答仍需逐 token decode。Prefix caching 主要节省重复的 prefill 工作,不会省掉新回答的生成计算。 如果请求 B 在文档前多放了一段不同的说明,共同 prefix 就可能缩短。即使后面的文档文字相同,也不能直接认定它的 causal states 相同。 如果重新设计 KV 的来源和共享方式呢? 前面从“每层保存自己的 K/V”出发。理解这些计算依赖,就能看懂 DeepSeek-V4.1-Flash 的几个变化。 先看来源:Causal Encoder–Decoder。 Decoder 的 global KV 来自 causal encoder 的最终输出。各 decoder 层仍然生成自己的 Q,以及 local sliding-window KV。 再看跨层:Cross-layer sharing。 在所讨论的配置中,第 21 层利用 encoder output 生成一份 decoder global KV bank,供第 21–40 层使用。 可以盯住第 21、22 层看:它们读取共享的 global entries,但 Q 和 local states 各自保留。共享条目不会强制产生相同的 attention weights;sparse selection 决定读取哪些 global entries。 最后看 K/V 的表示:Shared KV representation。 在 reference core-attention kernel 中,同一个被选中的向量既参与 Q 的匹配评分,也参与输出的加权求和。 看动画:一份 global KV 如何连接多个层?放大一个向量后,它又参与了哪两项计算? 模型仍保留 local caches、indexer state 等数据。Shared KV representation 与把两份独立 K/V 数组相邻存放,有明确区别。 回到项目文档:假设这次输入有 10 万个 token。报告中的 serving 方案可以这样处理: Encoder 处理完整 prompt,提供构建 decoder global KV 的表示。 Decoder 随后处理最后 128 个 prompt tokens,重建 local state。更早的历史仍可通过 global KV 访问。 这说明 global KV 的来源会影响 prefill 中哪些计算可以调整。生成后续 token 时,新输入仍然经过 causal encoder 和 decoder。 再回到开头的那份文档 现在可以沿着同一场景,把 KV cache 连起来: 继续写回答: 新位置产生自己的 Q,读取仍然有效的历史 K/V。 修改文档开头: 从实际 token prefix 的变化处重新检查复用范围。 换一个问题: 检查共同 prefix、执行条件,以及 cache 是否仍被保留。 读一份更长的文档: 关注保留位置带来的存储增长,以及架构如何组织这些状态。

来源报道 · 1 篇 · 以下链接均前往原始媒体

← 返回精选动态 原文

更多内容