F16|KV Cache从零解释:为什么可以少算,又记住了什么
从具体对象与小例子开始,理解计算过程与适用条件。
本章目录
KV Cache:留下算过的,不省掉该读的
为什么少算了很多步骤,下一Token仍能看到历史?
缓存像保存已经整理好的历史资料,而不是每轮把历史丢掉。因果模型中,追加未来位置不会使旧位置突然能看到未来,所以原前缀的K/V可以复用。新位置仍逐层计算,也仍要从允许的历史位置读信息。
输入与适用条件:本图用两层、三个历史位置演示状态;与F15单层小模型是不同教学结构,图内明确标层数。
1 / 5 · 两层分别保存历史
窄屏可左右滑动图形,或点击“放大图形”。文字解释紧随图后。
两个层各自保存历史三个位置的K/V。不是只缓存顶层,更不是只存词表编号。
每层cache_len=3默认静止。用“下一步”看完解释再继续;播放到最后一步自动停止。键盘聚焦播放器后,可用左右键逐步查看、空格播放/暂停。图中颜色辅助区分,含义同时用文字注明。
为什么需要
减少逐Token生成时对同一前缀的重复投影和逐位置加工。
怎样实现
每层存K/V,下一步只计算新位置,并用正确的位置偏移和可见性关系读取。
可能的代价与误解
内存随序列增长;低精度KV另有近似误差;跨用户共享还要隔离数据与权限。
如何判断是否做对
固定参数和前缀,对比缓存版与全量前向;不要只比较生成的字是否碰巧相同。
先从普通缓存的含义说起
你已经算出2×3=6,下一步再次需要同一个结果,可以保存6再用。缓存(cache)就是在条件未变时复用已有计算,避免重复工作。关键不是“保存了”,而是“保存的结果是否仍然对应当前条件”。
注意力中每层为已处理Token计算K和V,后面新Query需要读取这些历史K/V。保存它们,就叫KV Cache。
为什么追加Token通常不改变旧K/V
以固定权重、确定性推理、因果注意力、按Token独立归一化的位置为前提:旧位置i只能读到它自己及之前的内容。你在末尾追加一个新Token,旧位置仍看不到这个未来位置。
因此旧位置第0层的输入没变,输出没变;第1层依赖的旧表示也没变,以此类推,每层旧K/V可保持不变。这是依赖关系带来的可复用,不是模型在心里重新记住了全文。
如果用了允许旧位置读取新后缀的双向注意力,或者把所有位置一起归一化,就不能直接使用这段论证。
用两层、三个历史位置数清缓存
假设只有两层,每层单头,每个K或V是4个数,已处理3个位置。
第0层:K表[3,4],V表[3,4]
第1层:K表[3,4],V表[3,4]
共2层×2种表×3位置×4数=48个数。若每数4字节,数据本体192字节。
新Token到来时,第0层算出新K/V各一排4个数并追加;第1层在得到新的深层表示后追加自己的一排。它不是只在最顶层加一次缓存。各层参数不同,缓存来源也不同。
为什么通常不保存历史Q来做后续注意力
新Token的输出需要新Q去匹配历史K,并加权历史V。旧Q已经完成旧位置的查询;预测新位置不需要再用旧Q生成全部旧位置输出,所以通常没有必要为这种增量计算保存所有旧Q。
这并不表示Q不重要或永远不能缓存,只是标准因果增量推理里复用历史K/V的收益更直接。
有缓存为什么仍然不是常数时间
对每个新Token,你仍需计算新表示和投影;新Q还要读取越来越多历史K/V。历史越长,注意力读取与聚合的成本可能越高。KV缓存减少了旧Token在各层的重复前向,不是让长上下文的代价归零。
也不能把KV缓存叫无损压缩的原文文件。它是特定模型、特定前缀、特定位置约定下的中间数值状态,不是通用可编辑知识库。
缓存失效什么时候发生
改变权重、适配器或位置设置可能使缓存不再匹配。修改Prompt中间某位置后,该位置及依赖它的后续状态通常需要重算;修改之前的前缀是否能保留,取决于相同模型与输入条件是否仍成立。
对话摘要压缩改变了输入序列,不等于可以原样沿用旧KV。服务端Prompt Cache可能复用相同前缀计算,但它与应用层“写一份摘要”处在不同层面。
一张表区分三种“记忆”
| 名称 | 保存什么 | 为何保存 |
|---|---|---|
| KV缓存 | 某模型某前缀的各层K/V数值 | 避免重复前向计算 |
| 会话历史或摘要 | 文字、工具结果、任务进展 | 为下一轮提供条件 |
| 长期记忆 | 经筛选的跨会话事实与经验 | 将来按需检索使用 |
能够解释这张表,再去读FlashAttention、分页缓存和长任务压缩,才能避免把不同层面的优化混成一件事。
同一条历史为什么每层都要留一份K/V
输入与条件。教学模型有2层、1个KV头、每头2个数,已处理3个位置;每个数用FP32的4字节。
| 步骤与对象 | 内部动作 | 中间结果 / 新状态 |
|---|---|---|
| 1数一层K | 3位置×2分量 | 6个数。 |
| 2加一层V | 同样6个数 | 每层K/V共12个数。 |
| 3乘两层 | 每层输入表示不同,投影权重也不同 | 总共24个数,即96字节。 |
| 4追加一个位置 | 两层分别增加K和V各2个数 | 共8个数,即32字节。 |
| 5新查询计算 | 每层新Q都读取该层4个Key | 旧Q不需要重算;但仍要读取历史K/V,不是常数成本。 |
把过程连起来。顶层缓存不能替代底层缓存:新Token进入底层时,就已经需要底层历史K/V。
边界与反例。本例只数K/V数据本体,忽略元数据。成立条件是固定权重、固定前缀及因果可见性。
依据与适用范围:[CACHE]。示例输入和场景由本书明确设定。
课后自检
改了Prompt末尾一个Token后,能否无条件复用这一个位置的旧K/V?
查看参考答案收起参考答案
有KV缓存后,新Query还读历史吗?
查看参考答案收起参考答案
延伸阅读:相关系统问题
04|输出是怎样一个Token一个Token生成的07|KV Cache、FlashAttention与PagedAttention的内部过程
08|推理服务:调度、吞吐、延迟和并行
14|上下文压缩:到底压掉什么,丢失后又如何恢复
28|研究深水区与系统综合判断
机制依据:[F-CACHE][CACHE]。本节数值与情景除另注外均为原创教学示例;来源不代替中文讲解。