ARTICLE · 1181897
AI基础|KV Cache
AI基础|KV Cache

最近在复习大模型推理优化,KV Cache 是一个绕不开的概念。 它看起来只是一个缓存机制,但实际上同时影响大语言模型的生成速度、显存占用、并发能力和长上下文能力。很多关于长文本、推理加速和大模型部署的问题,最后都会落到 KV Cache 上。 本文从 LLM 如何生成文本开始,逐步解释 KV Cache 的工作原理,以及量化、Token 剔除、GQA、MLA 等常见压缩方法。 大语言模型并不是一次性生成完整回答,而是一个 Token、一个 Token 地生成。 例如,模型要回答: 它可能先生成“KV”,再生成“Cache”,然后生成“有”,最后生成“什么用”等后续 Token。每生成一个新 Token,模型都要结合前面的上下文,预测下一个最可能出现的 Token。 这个过程通常可以分成两个阶段: Prefill(预填充):模型一次性处理用户输入的 Prompt,计算出上下文表示,并建立 KV Cache。 Decode(解码):模型逐个生成回答中的新 Token,每一步都读取历史缓存,并把新 Token 的 Key 和 Value 追加进去。 真正逐 Token 生成的是 Decode 阶段。KV Cache 主要就是为了减少这个阶段的重复计算。 模型使用 Attention 机制判断:在预测当前 Token 时,之前哪些 Token 更重要。 Attention 中有三个核心向量: Query(Q):当前 Token 想要查找什么信息。 Key(K):每个历史 Token 可以被怎样匹配,类似索引或标签。 Value(V):每个历史 Token 实际携带的内容。 可以把它理解成一次查资料的过程: 当前 Token 产生一个 Query,表示自己正在寻找什么。 Query 和所有历史 Token 的 Key 进行匹配,得到注意力分数。 根据注意力分数,对历史 Token 的 Value 进行加权汇总。 汇总结果继续传给模型后面的网络,用来预测下一个 Token。 实际模型通常包含很多层,每层又包含多个 Attention Head。不同的 Head 可能关注语法关系、实体关系、位置关系或其他模式。 假设当前已经生成了 100 个 Token,现在模型要生成第 101 个 Token。 在 Attention 计算中,第 101 个 Token 的 Query 需要和前面 100 个 Token 的 Key 进行匹配,并使用这些 Token 的 Value 完成计算。 如果没有缓存,模型每生成一个新 Token,都需要重新计算之前所有 Token 的 Key 和 Value。随着序列变长,历史 Token 会被反复处理,产生大量重复工作。 KV Cache 的做法是: 第一次处理 Token 时,计算它的 Key 和 Value。 将计算结果保存到缓存中。 后续生成新 Token 时,直接读取历史 Token 的 Key 和 Value,不再重复计算。 当前新 Token 的 Key 和 Value 计算完成后,再追加到缓存中。 因此,KV Cache 保存的是历史 Token 的K 和 V。 Query 只服务于当前正在生成的 Token。 当前 Token 使用自己的 Query,去查询历史 Token 的 Key 和 Value。这个 Token 的计算完成以后,它的 Query 通常不会在后续步骤中再次使用。 而历史 Token 的 Key 和 Value 会被之后生成的每一个 Token 反复访问,所以只需要缓存 K 和 V。 用一个简化例子说明。 假设模型需要生成 100 个 Token,并且只统计历史 K、V 的重复计算次数: 没有 KV Cache 时,每一步都需要重新处理不断增长的历史序列。按这个简化例子计算,重复处理规模约为:2 + 3 + 4 + …… + 100 = 5049。 使用 KV Cache 后,历史 Token 的 K、V 只在首次处理时计算,后续每一步只需要处理新 Token,计算规模约为:2 + 1 + 1 + …… + 1 = 101。 这是一个简化的数量级比较,实际计算量还会受到模型结构、矩阵乘法和实现方式影响。但它说明了 KV Cache 的核心价值:把随着上下文增长而反复进行的历史计算,转化为一次计算、多次读取。 序列越长,KV Cache 带来的计算收益通常越明显。 KV Cache 节省的是计算时间,但需要额外的显存或内存保存历史 Token 的 Key 和 Value。 对于一个典型的 Transformer 模型,单个 Token 的 KV Cache 大小可以粗略估算为: 2 × 层数 × 每层 KV Head 数 × Head 维度 × 每个数值占用的字节数。 其中: 层数:Transformer 的层数; 每层 KV Head 数:每层用于生成 K、V 的 Head 数; Head 维度:每个 Head 的向量维度; 每个数值占用的字节数:例如 FP16 通常按 2 字节估算; 前面的 2:分别对应 Key 和 Value。 以一个采用标准 MHA、FP16 存储的模型为例,假设: 32 层; 每层 32 个 KV Head; 每个 Head 的维度为 128; FP16 每个数值占用 2 字节。 代入上述数值,单个 Token 的缓存大小约为: 2 × 32 × 32 × 128 × 2 = 524,288 字节,约 0.5 MB。 那么: 4000 个 Token 约需要 2 GB; 100000 个 Token 约需要 50 GB。 这是一个便于理解的估算。实际大小还要考虑模型是否采用 GQA/MQA、数据类型、批处理方式和框架实现。 GPU 显存还需要存放模型权重、中间激活、临时计算空间以及其他运行数据。因此,当上下文很长,或者同时服务很多用户时,KV Cache 很容易成为显存瓶颈。 而且,每生成一个新 Token,模型都需要读取历史 KV Cache。缓存越大,显存带宽压力也越大,生成速度可能逐步下降。 所以,KV Cache 是一种典型的权衡: KV Cache 压缩,是指在尽量保持模型输出质量的前提下,降低缓存占用的各种技术。 常见方法主要有两类: 对已有模型进行推理侧优化:例如量化、Token 剔除。 在模型架构和训练阶段进行设计:例如 GQA、MQA、MLA。 不同方法压缩的是不同对象:有的降低每个数值的存储位数,有的减少保存的 Token 数量,有的减少 K、V 的副本数,还有的直接改变 K、V 的表示方式。 量化是指使用更少的位数存储缓存中的数值。 KV Cache 默认常用 FP16 或 BF16,也就是每个数值占 16 位。通过量化,可以将其压缩为 INT8,甚至 INT4: 16 位降到 8 位,缓存大小约变为原来的二分之一; 16 位降到 4 位,缓存大小约变为原来的四分之一。 量化的优点是: 通常可以直接应用于已经训练好的模型; 不需要删除任何 Token; 工程集成相对简单; 在合理配置下,精度损失通常较小。 缺点是量化会带来舍入误差。位宽越低,误差通常越明显,尤其是在长上下文和对数值敏感的任务中。 Key 和 Value 的数值分布也不完全相同。Key 的部分维度可能存在离群值,因此像 KIVI 这样的方案会对 Key 和 Value 使用不同的量化策略,以降低误差。 Token 剔除是直接从缓存中移除相对不重要的 Token 的 Key 和 Value。 在很多场景中,模型的注意力并不是平均分布在所有历史 Token 上,而是集中在少数关键 Token 上。这些被重点关注的 Token 有时被称为Heavy Hitters。 一种常见策略是: 设置固定的缓存预算,例如 1000 个 Token; 始终保留最近的一段 Token,因为近期上下文通常比较重要; 在更早的历史 Token 中,保留注意力分数较高的 Token; 删除其余 Token。 这样,无论对话继续多长,缓存大小都不会无限增长。 研究发现,模型经常会给最开始的几个 Token 分配较高注意力,即使这些 Token 本身并没有特别重要的语义内容。 一种解释是:Attention 的权重需要归一化。当模型找不到合适的历史位置承接部分注意力时,可能会把多余的权重集中到开头 Token,这些 Token 就像注意力的“汇聚点”,因此被称为Attention Sinks。 如果简单删除这些 Token,注意力分布可能被破坏,模型输出质量会明显下降。因此,一些流式推理方案会固定保留开头的少量 Token,同时保留最近窗口。 Token 剔除的优点是缓存大小可以被固定住;缺点是被删除的信息无法再被精确访问。如果用户后续提问涉及已删除的细节,模型可能无法准确回答。 因此,它更适合对话、摘要和流式生成等场景,不适合要求精确回忆全部历史细节的任务。 前面的量化和 Token 剔除,主要是在模型训练完成以后压缩缓存。 MQA 和 GQA 则是在模型架构设计和训练阶段,减少 K、V 的副本数量。 在标准的 Multi-Head Attention(MHA)中,每个 Attention Head 都有自己独立的 Key 和 Value。假设一层有 32 个 Head,就需要保存 32 组 K 和 32 组 V。 但研究发现,不同 Head 不一定需要完全独立的 K、V。它们可以共享部分 K、V,同时保留各自独立的 Query。 MQA 让同一层中的所有 Query Head 共享一套 Key 和 Value。 如果原来有 32 个 Head,理论上 K、V 的缓存规模可以缩小到约原来的三十二分之一。它的缓存效率很高,但共享程度过高可能影响模型质量。 GQA 是一种折中方案。它把多个 Query Head 分成若干组,每组共享一套 Key 和 Value。 例如,32 个 Query Head 分成 8 组,则 K、V 的数量大约变为原来的四分之一。 GQA 的优点是: 显著减少 KV Cache; 不需要在推理时删除 Token; 通常不会明显损害模型质量; 已被许多主流开源模型采用。 缺点是:它通常需要在模型训练前确定,不能把一个已经训练好的标准 MHA 模型无损地直接改成 GQA。 此外,GQA 只是降低了每个 Token 的缓存大小,缓存仍然会随着上下文长度增加。 低秩压缩不再直接保存完整的 Key 和 Value,而是保存一个更低维的隐向量,需要进行 Attention 计算时再通过变换恢复所需表示。 可以把它理解为:不保存完整原图,而是保存一个经过训练的压缩编码,需要使用时再还原出足够完成计算的信息。 DeepSeek-V2 和 DeepSeek-V3 中使用的MLA(Multi-Head Latent Attention)就是这种思路的代表。 在传统结构中,每层每个 Token 需要保存多个 Head 的 K 和 V;在 MLA 中,可以将它们压缩为一个维度更低的 latent vector。 假设传统结构每层每个 Token 需要保存 128 × 32 × 2 = 8,192 个数值,而压缩后的隐向量为 512 维,那么理论上可以获得数量级的缓存缩减。具体压缩比例取决于模型结构和实现方式。 MLA 的优点是: 显存压缩率高; 不需要直接删除历史 Token; 模型从训练阶段就适应压缩表示,质量通常较好。 缺点是: 需要从模型架构和训练阶段开始设计; 恢复或变换隐向量会引入额外计算; 不能简单应用到任意现成模型。 怎么压缩:降低每个数值的存储位数。 是否需要重新训练:通常不需要。 是否丢弃历史信息:不丢弃 Token,但会产生量化误差。 缓存大小是否固定:不会,缓存仍随上下文长度增长。 典型收益:约 2 到 4 倍,取决于量化位宽和实现。 代表方案:KIVI。 怎么压缩:减少需要保存的 Token 数量。 是否需要重新训练:不需要。 是否丢弃历史信息:会丢弃部分 Token,可能失去历史细节。 缓存大小是否固定:可以通过设定预算来固定。 典型收益:取决于缓存预算和保留策略。 代表方案:H2O、StreamingLLM。 怎么压缩:减少每层 K、V 的副本数量。 是否需要重新训练:通常需要模型架构支持。 是否丢弃历史信息:不丢弃 Token。 缓存大小是否固定:不会,缓存仍随上下文长度增长。 典型收益:约 4 到 8 倍,具体取决于分组数量。 代表方案:MQA、GQA。 怎么压缩:降低每个 Token 的缓存表示维度。 是否需要重新训练:需要模型架构支持。 是否丢弃历史信息:不直接丢弃 Token。 缓存大小是否固定:不会,缓存仍随上下文长度增长。 典型收益:特定架构可达到数量级压缩,具体比例视实现而定。 主要代价:增加额外计算,并受模型架构限制。 代表方案:MLA。 这些方法并不互斥,可以组合使用。例如,采用 MLA 的模型仍然可以进一步量化隐向量,在超长上下文场景中也可以配合 Token 管理策略。 优先考虑 KV Cache 量化。它通常不需要重新训练,也不会直接删除 Token,适合作为工程优化的第一步。 可以考虑 Token 剔除,并结合 Attention Sink 和最近窗口策略,让缓存维持在固定预算内。 谨慎使用 Token 剔除,因为被删除的 Token 无法再被精确访问。可以优先选择量化,或者使用原生支持 GQA/MLA 的模型,并配合外部检索系统。 可以在架构设计阶段考虑 GQA、MQA 或 MLA。它们从根本上改变缓存的生成方式,通常比训练完成后再压缩更稳定。 可以组合使用多种方法,例如 MLA 加 KV Cache 量化,再根据任务特点使用动态 Token 管理。 不是。 KV Cache 压缩,是直接减少缓存内容本身的大小,例如降低位宽、减少 Token 或压缩表示维度。 Paged Attention 主要解决的是 KV Cache 的内存管理问题。它借鉴操作系统分页的思想,将 KV Cache 分成若干块,按需分配和回收,从而减少显存碎片和动态分配浪费。 可以简单理解为: KV Cache 压缩:让每份缓存变小; Paged Attention:让缓存空间分配得更高效。 两者可以配合使用。vLLM 等推理系统中的 Paged Attention,重点就是提升已有显存的利用率,并不等于对 KV Cache 内容进行压缩。 会。模型仍然需要计算新 Token 的 Query、Key 和 Value,只是跳过历史 Token 的重复 K、V 计算,并直接读取缓存。 可以。在多个请求具有相同前缀时,系统可以复用这部分前缀对应的缓存,这通常被称为 Prompt Caching。是否支持以及如何复用,取决于具体的推理框架和服务实现。 不能。它减少了重复计算,但本身需要占用显存,而且会随着上下文长度和并发请求增加而增长。Paged Attention、量化、Token 管理和架构设计分别解决不同层面的问题。 通常不能直接无损套用。GQA 改变了 K、V 的组织方式,需要在训练时采用相应架构,或者通过转换、蒸馏和再训练等方式适配。 每生成一个新 Token,模型都需要读取历史缓存参与 Attention 计算。缓存变大后,显存读写量和带宽压力增加,Decode 阶段可能逐渐受到内存带宽限制。 KV Cache 的核心思想很简单: 它让 LLM 的逐 Token 生成变得高效,但也引入了显存开销。随着上下文长度和并发用户增加,KV Cache 会成为推理系统中的重要瓶颈。 不同压缩方法解决的是不同问题: 量化:降低每个数值的存储成本; Token 剔除:减少需要保存的历史 Token; MQA/GQA:减少 K、V 的副本数量; MLA:用低维隐向量表示 K、V; Paged Attention:提高缓存空间的分配和管理效率。 如果把 LLM 推理看成一个不断查阅历史资料的过程,那么 KV Cache 就是这份资料的临时索引。它决定了模型能以多快的速度回看历史,也决定了系统需要为这些历史资料付出多少显存成本。

KV Cache 到底是什么:为什么 LLM 越聊越慢、越聊越占显存?
一、LLM 是怎样生成文本的?
KV Cache 有什么用?
二、Attention 中的 Q、K、V
三、为什么需要 KV Cache?
为什么不缓存 Query?
四、KV Cache 能带来多大收益?
五、速度提升的代价:显存会不断增长
用更多显存,换取更少的重复计算和更快的生成速度。
六、KV Cache 压缩是什么?
七、方法一:KV Cache 量化
八、方法二:Token 剔除
Attention Sinks:为什么不能随便删除开头 Token?
九、方法三:MQA 和 GQA
Multi-Query Attention(MQA)
Grouped-Query Attention(GQA)
十、方法四:MLA 等低秩压缩
十一、各种方法如何比较?
量化
Token 剔除
MQA/GQA
MLA 等低秩压缩
十二、不同场景应该怎么选?
已有模型,想快速降低显存
长对话或持续流式生成
需要精确检索长文档细节
从零训练新模型
追求极限显存压缩
十三、KV Cache 和 Paged Attention 是一回事吗?
十四、几个常见问题
使用 KV Cache 时,模型还会计算新 Token 吗?
KV Cache 可以在不同请求之间复用吗?
KV Cache 能解决大模型的显存问题吗?
可以把 GQA 直接应用于训练好的 MHA 模型吗?
为什么 KV Cache 越大,生成速度可能越慢?
十五、总结
把历史 Token 已经算过的 Key 和 Value 保存下来,后续直接复用。















































































