基础篇

F16|KV Cache从零解释:为什么可以少算,又记住了什么

基础篇约 2.7 千字1 组分步图解

从具体对象与小例子开始,理解计算过程与适用条件。

本章目录
动态图解 · F16

KV Cache:留下算过的,不省掉该读的

为什么少算了很多步骤,下一Token仍能看到历史?

缓存像保存已经整理好的历史资料,而不是每轮把历史丢掉。因果模型中,追加未来位置不会使旧位置突然能看到未来,所以原前缀的K/V可以复用。新位置仍逐层计算,也仍要从允许的历史位置读信息。

K/V缓存每层保存已处理位置的Key和Value。
缓存失效原来结果的前提改变,不能安全继续复用。
重算重新执行此前算过的部分运算。

输入与适用条件:本图用两层、三个历史位置演示状态;与F15单层小模型是不同教学结构,图内明确标层数。

1 / 5 · 两层分别保存历史

第1步,共5步

窄屏可左右滑动图形,或点击“放大图形”。文字解释紧随图后。

KV Cache:留下算过的,不省掉该读的:两层分别保存历史第0层t0K/V 已存t1K/V 已存t2K/V 已存第1层t0K/V 已存t1K/V 已存t2K/V 已存两个层各自保存历史三个位置的K/V。不是只缓存顶层,更不是只存词表编号。

两个层各自保存历史三个位置的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、分页缓存和长任务压缩,才能避免把不同层面的优化混成一件事。

运行示例 · F16

同一条历史为什么每层都要留一份K/V

输入与条件。教学模型有2层、1个KV头、每头2个数,已处理3个位置;每个数用FP32的4字节。

步骤与对象内部动作中间结果 / 新状态
1数一层K3位置×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]。示例输入和场景由本书明确设定。

课后自检

自检 1

改了Prompt末尾一个Token后,能否无条件复用这一个位置的旧K/V?

查看参考答案收起参考答案
不能。该位置的输入变了,旧K/V不再对应新输入。满足条件的更早前缀可另行判断复用。
自检 2

有KV缓存后,新Query还读历史吗?

查看参考答案收起参考答案
要读。它通过历史K计算权重,并聚合历史V;缓存避免的是重新生成这些旧K/V。

机制依据:[F-CACHE][CACHE]。本节数值与情景除另注外均为原创教学示例;来源不代替中文讲解。

连续数值案例:把同样两句话从Embedding一直算到输出概率,再验证KV缓存。适合完成F01—F16后阅读。
记录保存在本机浏览器。

输入关键词即可检索。

↑ ↓ 选择 Enter 打开Esc 关闭 · 仅本地搜索

阅读设置

阅读设置仅保存在本机浏览器,不上传任何数据。