夜雨聆风学习资料网

ARTICLE · 1181897

AI基础|KV Cache

AI基础|KV Cache

KV Cache 到底是什么:为什么 LLM 越聊越慢、越聊越占显存?

最近在复习大模型推理优化,KV Cache 是一个绕不开的概念。
它看起来只是一个缓存机制,但实际上同时影响大语言模型的生成速度、显存占用、并发能力和长上下文能力。很多关于长文本、推理加速和大模型部署的问题,最后都会落到 KV Cache 上。
本文从 LLM 如何生成文本开始,逐步解释 KV Cache 的工作原理,以及量化、Token 剔除、GQA、MLA 等常见压缩方法。

一、LLM 是怎样生成文本的?

大语言模型并不是一次性生成完整回答,而是一个 Token、一个 Token 地生成。
例如,模型要回答:

KV Cache 有什么用?

它可能先生成“KV”,再生成“Cache”,然后生成“有”,最后生成“什么用”等后续 Token。每生成一个新 Token,模型都要结合前面的上下文,预测下一个最可能出现的 Token。
这个过程通常可以分成两个阶段:
    Prefill(预填充):模型一次性处理用户输入的 Prompt,计算出上下文表示,并建立 KV Cache。
    Decode(解码):模型逐个生成回答中的新 Token,每一步都读取历史缓存,并把新 Token 的 Key 和 Value 追加进去。
真正逐 Token 生成的是 Decode 阶段。KV Cache 主要就是为了减少这个阶段的重复计算。

二、Attention 中的 Q、K、V

模型使用 Attention 机制判断:在预测当前 Token 时,之前哪些 Token 更重要。
Attention 中有三个核心向量:
    Query(Q):当前 Token 想要查找什么信息。
    Key(K):每个历史 Token 可以被怎样匹配,类似索引或标签。
    Value(V):每个历史 Token 实际携带的内容。
可以把它理解成一次查资料的过程:
    当前 Token 产生一个 Query,表示自己正在寻找什么。
    Query 和所有历史 Token 的 Key 进行匹配,得到注意力分数。
    根据注意力分数,对历史 Token 的 Value 进行加权汇总。
    汇总结果继续传给模型后面的网络,用来预测下一个 Token。
实际模型通常包含很多层,每层又包含多个 Attention Head。不同的 Head 可能关注语法关系、实体关系、位置关系或其他模式。

三、为什么需要 KV Cache?

假设当前已经生成了 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?

Query 只服务于当前正在生成的 Token。
当前 Token 使用自己的 Query,去查询历史 Token 的 Key 和 Value。这个 Token 的计算完成以后,它的 Query 通常不会在后续步骤中再次使用。
而历史 Token 的 Key 和 Value 会被之后生成的每一个 Token 反复访问,所以只需要缓存 K 和 V。

四、KV Cache 能带来多大收益?

用一个简化例子说明。
假设模型需要生成 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 压缩是什么?

KV Cache 压缩,是指在尽量保持模型输出质量的前提下,降低缓存占用的各种技术。
常见方法主要有两类:
    对已有模型进行推理侧优化:例如量化、Token 剔除。
    在模型架构和训练阶段进行设计:例如 GQA、MQA、MLA。
不同方法压缩的是不同对象:有的降低每个数值的存储位数,有的减少保存的 Token 数量,有的减少 K、V 的副本数,还有的直接改变 K、V 的表示方式。

七、方法一:KV Cache 量化

量化是指使用更少的位数存储缓存中的数值。
KV Cache 默认常用 FP16 或 BF16,也就是每个数值占 16 位。通过量化,可以将其压缩为 INT8,甚至 INT4:
    16 位降到 8 位,缓存大小约变为原来的二分之一;
    16 位降到 4 位,缓存大小约变为原来的四分之一。
量化的优点是:
    通常可以直接应用于已经训练好的模型;
    不需要删除任何 Token;
    工程集成相对简单;
    在合理配置下,精度损失通常较小。
缺点是量化会带来舍入误差。位宽越低,误差通常越明显,尤其是在长上下文和对数值敏感的任务中。
Key 和 Value 的数值分布也不完全相同。Key 的部分维度可能存在离群值,因此像 KIVI 这样的方案会对 Key 和 Value 使用不同的量化策略,以降低误差。

八、方法二:Token 剔除

Token 剔除是直接从缓存中移除相对不重要的 Token 的 Key 和 Value。
在很多场景中,模型的注意力并不是平均分布在所有历史 Token 上,而是集中在少数关键 Token 上。这些被重点关注的 Token 有时被称为Heavy Hitters。
一种常见策略是:
    设置固定的缓存预算,例如 1000 个 Token;
    始终保留最近的一段 Token,因为近期上下文通常比较重要;
    在更早的历史 Token 中,保留注意力分数较高的 Token;
    删除其余 Token。
这样,无论对话继续多长,缓存大小都不会无限增长。

Attention Sinks:为什么不能随便删除开头 Token?

研究发现,模型经常会给最开始的几个 Token 分配较高注意力,即使这些 Token 本身并没有特别重要的语义内容。
    一种解释是:Attention 的权重需要归一化。当模型找不到合适的历史位置承接部分注意力时,可能会把多余的权重集中到开头 Token,这些 Token 就像注意力的“汇聚点”,因此被称为Attention Sinks。
如果简单删除这些 Token,注意力分布可能被破坏,模型输出质量会明显下降。因此,一些流式推理方案会固定保留开头的少量 Token,同时保留最近窗口。
Token 剔除的优点是缓存大小可以被固定住;缺点是被删除的信息无法再被精确访问。如果用户后续提问涉及已删除的细节,模型可能无法准确回答。
因此,它更适合对话、摘要和流式生成等场景,不适合要求精确回忆全部历史细节的任务。

九、方法三:MQA 和 GQA

前面的量化和 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。

Multi-Query Attention(MQA)

MQA 让同一层中的所有 Query Head 共享一套 Key 和 Value。
如果原来有 32 个 Head,理论上 K、V 的缓存规模可以缩小到约原来的三十二分之一。它的缓存效率很高,但共享程度过高可能影响模型质量。

Grouped-Query Attention(GQA)

GQA 是一种折中方案。它把多个 Query Head 分成若干组,每组共享一套 Key 和 Value。
例如,32 个 Query Head 分成 8 组,则 K、V 的数量大约变为原来的四分之一。
GQA 的优点是:
    显著减少 KV Cache;
    不需要在推理时删除 Token;
    通常不会明显损害模型质量;
    已被许多主流开源模型采用。
缺点是:它通常需要在模型训练前确定,不能把一个已经训练好的标准 MHA 模型无损地直接改成 GQA。
此外,GQA 只是降低了每个 Token 的缓存大小,缓存仍然会随着上下文长度增加。

十、方法四:MLA 等低秩压缩

低秩压缩不再直接保存完整的 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 数量。
    是否需要重新训练:不需要。
    是否丢弃历史信息:会丢弃部分 Token,可能失去历史细节。
    缓存大小是否固定:可以通过设定预算来固定。
    典型收益:取决于缓存预算和保留策略。
    代表方案:H2O、StreamingLLM。

MQA/GQA

    怎么压缩:减少每层 K、V 的副本数量。
    是否需要重新训练:通常需要模型架构支持。
    是否丢弃历史信息:不丢弃 Token。
    缓存大小是否固定:不会,缓存仍随上下文长度增长。
    典型收益:约 4 到 8 倍,具体取决于分组数量。
    代表方案:MQA、GQA。

MLA 等低秩压缩

    怎么压缩:降低每个 Token 的缓存表示维度。
    是否需要重新训练:需要模型架构支持。
    是否丢弃历史信息:不直接丢弃 Token。
    缓存大小是否固定:不会,缓存仍随上下文长度增长。
    典型收益:特定架构可达到数量级压缩,具体比例视实现而定。
    主要代价:增加额外计算,并受模型架构限制。
    代表方案:MLA。
这些方法并不互斥,可以组合使用。例如,采用 MLA 的模型仍然可以进一步量化隐向量,在超长上下文场景中也可以配合 Token 管理策略。

十二、不同场景应该怎么选?

已有模型,想快速降低显存

    优先考虑 KV Cache 量化。它通常不需要重新训练,也不会直接删除 Token,适合作为工程优化的第一步。

长对话或持续流式生成

    可以考虑 Token 剔除,并结合 Attention Sink 和最近窗口策略,让缓存维持在固定预算内。

需要精确检索长文档细节

    谨慎使用 Token 剔除,因为被删除的 Token 无法再被精确访问。可以优先选择量化,或者使用原生支持 GQA/MLA 的模型,并配合外部检索系统。

从零训练新模型

    可以在架构设计阶段考虑 GQA、MQA 或 MLA。它们从根本上改变缓存的生成方式,通常比训练完成后再压缩更稳定。

追求极限显存压缩

    可以组合使用多种方法,例如 MLA 加 KV Cache 量化,再根据任务特点使用动态 Token 管理。

十三、KV Cache 和 Paged Attention 是一回事吗?

不是。
    KV Cache 压缩,是直接减少缓存内容本身的大小,例如降低位宽、减少 Token 或压缩表示维度。
    Paged Attention 主要解决的是 KV Cache 的内存管理问题。它借鉴操作系统分页的思想,将 KV Cache 分成若干块,按需分配和回收,从而减少显存碎片和动态分配浪费。
可以简单理解为:
    KV Cache 压缩:让每份缓存变小;
    Paged Attention:让缓存空间分配得更高效。
两者可以配合使用。vLLM 等推理系统中的 Paged Attention,重点就是提升已有显存的利用率,并不等于对 KV Cache 内容进行压缩。

十四、几个常见问题

使用 KV Cache 时,模型还会计算新 Token 吗?

会。模型仍然需要计算新 Token 的 Query、Key 和 Value,只是跳过历史 Token 的重复 K、V 计算,并直接读取缓存。

KV Cache 可以在不同请求之间复用吗?

可以。在多个请求具有相同前缀时,系统可以复用这部分前缀对应的缓存,这通常被称为 Prompt Caching。是否支持以及如何复用,取决于具体的推理框架和服务实现。

KV Cache 能解决大模型的显存问题吗?

不能。它减少了重复计算,但本身需要占用显存,而且会随着上下文长度和并发请求增加而增长。Paged Attention、量化、Token 管理和架构设计分别解决不同层面的问题。

可以把 GQA 直接应用于训练好的 MHA 模型吗?

通常不能直接无损套用。GQA 改变了 K、V 的组织方式,需要在训练时采用相应架构,或者通过转换、蒸馏和再训练等方式适配。

为什么 KV Cache 越大,生成速度可能越慢?

每生成一个新 Token,模型都需要读取历史缓存参与 Attention 计算。缓存变大后,显存读写量和带宽压力增加,Decode 阶段可能逐渐受到内存带宽限制。

十五、总结

KV Cache 的核心思想很简单:

把历史 Token 已经算过的 Key 和 Value 保存下来,后续直接复用。

它让 LLM 的逐 Token 生成变得高效,但也引入了显存开销。随着上下文长度和并发用户增加,KV Cache 会成为推理系统中的重要瓶颈。
不同压缩方法解决的是不同问题:
    量化:降低每个数值的存储成本;
    Token 剔除:减少需要保存的历史 Token;
    MQA/GQA:减少 K、V 的副本数量;
    MLA:用低维隐向量表示 K、V;
    Paged Attention:提高缓存空间的分配和管理效率。
如果把 LLM 推理看成一个不断查阅历史资料的过程,那么 KV Cache 就是这份资料的临时索引。它决定了模型能以多快的速度回看历史,也决定了系统需要为这些历史资料付出多少显存成本。

相关学习资料