KV Cache与注意力机制:大模型推理为何缓存K和V?

发布时间:2026/10/26 5:55:15
KV Cache与注意力机制:大模型推理为何缓存K和V? 刚接触大模型部署和推理优化的人多半会被一个词反复拦路KV Cache。去搜资料满屏都是“缓存机制”“显存优化”“PagedAttention”但很少有人把前因后果讲透——为什么大模型推理非要缓存Key和Value为什么缓存的是KV而不是QKV它跟长上下文的“Lost in Middle”现象又有什么关系这篇文章不绕弯子直接从Transformer的自回归解码讲起把这几个问题一次说清楚。适合正在学大模型原理、做推理部署优化、或者准备大模型面试的人花十分钟读完你对推理性能和显存规划的认知会上一个台阶。1. 为什么需要KV Cache先看解码阶段的“重复劳动”1.1 自回归生成每一步都在重读全文大模型的文本生成是自回归的每生成一个token都要把“已经生成的整段历史”重新过一遍注意力计算。这句话看似平淡实际是理解KV Cache的起点。假设当前长度为t模型要预测第t1个token。标准Self-Attention会对所有t个位置计算一次完整的注意力分数和加权求和。也就是说序列每长一个token模型就要把前面所有token的K和V重新算一遍。问题在于这些K和V本质上只是历史token经线性变换得到的向量它们不依赖“当前正在预测什么”只依赖历史token本身。第5个token的Key不会因为你现在要预测第500个token而发生变化。这个场景我用一个生活例子来类比你在写一篇长论文每写一句新内容都要把前面所有段落重新翻出来读一遍重新做一遍笔记而不是把笔记留在桌上直接用。模型在没有KV Cache时干的就是这种事——每一步都从零开始重新编码全部历史计算复杂度是O(t²d)t是序列长度d是隐藏维度。序列从100涨到1000单步计算量直接涨100倍整个推理过程更是不可收拾。我见过不少刚开始接触推理服务的人总把生成慢归咎于“模型太大”“GPU不够”其实在无缓存的情况下很大一部分算力消耗在重复编码历史token上。KV Cache就是把这些算过的K、V存下来新token来了直接复用避免重复计算。1.2 KV Cache的核心直觉缓存“内容”不缓存“查询”要把KV Cache讲明白绕不开那张Attention公式。对于一个token序列每个token都有自己的Query、Key、Value它们分别由输入向量乘三个权重矩阵得到。注意力计算的本质是Query负责提出“我要找什么”Key负责提供“我是谁、我有什么标签”Value负责提供“我具体携带的内容信息”。注意力分数是Query和所有Key做点积再用softmax归一化最后去加权求和对应的Value。这里有个关键细节当生成新token时只有新token的Query是“新鲜”的它需要去和历史所有Key做匹配然后从历史所有Value里取内容。而历史token的Key和Value早在它们刚被生成时就已经算过一次了——后面再做多少次注意力它们的值都不会变。所以缓存K和V是绝对安全的。新token的Q和缓存的历史K做点积得到注意力分数再和缓存的历史V做加权求和数学结果和全量重算完全一致没有任何精度损失。为什么不需要缓存矩阵里那个权重W因为W是模型训练好的参数本来就常驻在模型权重里跟推理过程中的临时缓存完全是两回事。1.3 KV Cache到底省了多少算力无缓存时生成第t1个token需要计算t个位置的K和V。有缓存时只需要计算当前1个token的K和V然后从缓存里取t个历史KV参与注意力即可。从复杂度上看更直观无缓存每步O(t²d)总复杂度O(t³d)量级有缓存每步O(td)总复杂度O(t²d)量级也就是说当序列长度为2048时缓存版本能让注意力计算的整体开销下降约三个数量级。我自己实测过在8B模型上不启用KV Cache时首token之后的每token延迟会随着prompt长度线性飙升而启用后基本稳定在一个很小的区间内。这个差距在长上下文场景里会被放大到无法忽略同样是生成128个token上下文是10K还是1K无缓存版本的速度差距可能有十倍以上。2. 为什么是KV而不是QKV这个问题值得较真网上有个高频追问缓存K和V就算了为什么连Q也不缓存甚至有说法调侃“不如叫QKV Cache”。这个问题看似钻牛角尖其实恰好能检验你对Attention机制的理解程度。2.1 Q是“一次性”的查询条件核心原因一句话历史token的Query在后续解码过程中永远不再参与任何计算。Attention公式明确告诉我们Query只和自己的Key做点积。当模型生成第t1个token时它需要的是“当前新token的Q”去和历史所有Key匹配并不是拿历史token的Q去匹配任何东西。每一个Q在被生成的那个时刻就已经完成了它唯一一次使命——决定自己位置的注意力权重。一旦那一步做完了这个Q就成了彻底的“无用数据”。如果把Q也想当然地缓存下来推理框架需要把所有历史Q存在显存里但每一步真实计算中历史Q连被读取的机会都没有。这不是“多存一份数据”那么简单而是存了一堆永远不会被访问的垃圾。2.2 如果不信从矩阵形状上算一笔账假设当前序列长度为t隐藏维度为d无缓存时每个解码步需要为全部t个位置计算Q、K、V存储开销是三份t×d矩阵有缓存时每步只需计算新位置的Q但K和V是从缓存里读的缓存大小是两份t×d矩阵。如果错误地缓存QKV那就是三份t×d矩阵。KV Cache的显存占用直接增加50%而计算量一点没省——因为这50%的数据永远不会被读取参与运算。纯粹是花钱买了个废品。所以业界所有推理框架比如HuggingFace的past_key_values、vLLM的KV Manager都只缓存K和V不是他们没有考虑到Q而是从数学上就没有缓存Q的必要。理解这一点你在面试或设计推理系统时就不会犯“KV Cache太占显存能不能只缓存一部分”这类方向性错误。2.3 从语义上理解三个字母的分工我建议所有新手都把Q、K、V当成一个信息检索系统来理解Q是你搜索框里输入的关键词K是每篇文章的标题和索引标签V是文章正文内容。你每搜索一次都会输入新的关键词新的Q但文章的标题K和正文V是固定不变的。你不会把旧的搜索词存下来去匹配新的文章标题——那毫无意义。KV Cache做的正是这件事把文章的标题和正文提前整理好每次新搜索来了直接查表而不是把整个图书馆重新编目一遍。3. KV Cache的显存账本与工程实现3.1 一个token吃掉多少显存算给你看KV Cache不是免费的它的代价是显存。很多人在7B模型上跑长上下文发现显存爆得莫名其妙就是没算清这笔账。每个token的KV Cache显存公式每token KV字节数 2K和V两份 × 层数 × 每层KV head数 × head维度 × 每个元素字节数以7B模型为例假设32层、每层40个KV head、head维数128、FP16存储每元素2字节。算下来每个token大约占用2 × 32 × 40 × 128 × 2 655360字节约0.6MB注意这只是一个token。如果上下文长度做到4096光KV Cache就是约2.5GB到32768就奔着20GB去了。一块24GB的消费级显卡在长上下文场景下一大半显存被KV Cache吃掉真正用于模型权重和激活值的空间所剩无几。我整理一个常见量级的参考表方便你估算部署方案参数量层数隐藏维度每token KV CacheFP164K上下文32K上下文7B324096约0.5MB约2GB约16GB13B405120约0.8MB约3.2GB约25GB70B808192约2.5MB约10GB约80GB也正因为如此长上下文推理的显存瓶颈往往不是模型权重而是KV Cache。这也是后面讲工程优化时所有人都在围绕KV Cache做文章的根本原因。3.2 Prefill和Decode两段性格迥异的表演KV Cache的使用分两个阶段很多人只关注到“缓存”本身却忽略了这两个阶段对性能和优化的要求是截然不同的。Prefill阶段预填充模型一次性读入整个prompt并行计算所有token的K和V填充KV Cache然后生成第一个输出token。这个阶段是计算密集型的因为要同时处理大量token的矩阵乘法GPU的算力利用率很高。这也是为什么长prompt的“首token延迟”会比较明显——prompt越长prefill要做的计算越多。Decode阶段解码模型逐个生成后续token每一步只有一个新token进入更新KV Cache并利用缓存计算下一步输出。这个阶段是访存密集型的。每一步的矩阵乘法规模很小但必须把整段KV Cache从显存里读一遍所以GPU算力往往吃不饱瓶颈卡在显存带宽上。我最初做推理优化时犯过一个错误只盯着每token延迟以为只要并行度够高就能解决问题。实际跑下来发现decode阶段的延迟被显存带宽锁死——KV Cache越大每一步读取成本越高。这也是为什么有些模型在长上下文下生成速度会断崖式下降不只是因为计算量大了更重要的是每一轮都要搬运越来越大的缓存块。3.3 业界主流的KV Cache优化手段既然KV Cache的显存占用如此恐怖业内自然发展出了一套组合拳来压制它GQA / MQA分组查询注意力/多查询注意力让多个Query头共享同一组KV head把KV head数量缩小到原来的1/8甚至更少KV Cache也跟着同比缩小。这也是Llama 2到Llama 3、Mistral等新模型普遍采用GQA的原因——不是拍脑袋的设计决策而是为了压显存。PagedAttention分页注意力来自vLLM的核心设计。传统推理框架为请求预分配连续显存块而KV Cache按需增长容易产生大量碎片。PagedAttention把KV Cache切成固定大小的“页”按需分配、按页管理显存利用率大幅提升。KV Cache量化把K和V从FP16压到INT8甚至INT4。由于K、V的数值分布相对稳定量化后精度损失通常可控。我之前在7B模型上做过实测INT8量化后KV Cache占用减半困惑度几乎没变化效果非常香。滑动窗口缓存因为部分模型对“最近”的上下文更敏感可以只保留最近N个token的KV Cache丢弃更早的让显存占用变成恒定的。StreamingLLM那套思路就是在此基础上加了几个锚点token来稳定注意力。Prefix Caching前缀缓存也就是大家常说的Prompt Cache如果多个请求共享同一段系统提示词或长文档前缀那么这段前缀的KV Cache结果可以直接复用。公共前缀越长节省的重复计算越可观。这也就是你搜到的“Prompt的Cache又是什么”这个问题的答案——本质是KV Cache在服务端多请求维度的复用策略。4. KV Cache与“迷失在中间”的矛盾4.1 长上下文里的注意力分布不均如果你让模型读一篇很长的文档再问它文档中间某段的信息它经常答不上来——这就是社区常说的Lost in Middle。很多人的第一反应是“KV Cache不够大把中间的内容丢了”这是个误解。KV Cache是完整保存了所有历史token的K和V的中间部分的信息明明都存在显存里。问题是模型在计算注意力分数时给中间token分配的注意力权重太低低到它可以“视而不见”。注意力分数的分布天然不均匀。softmax归一化后只有少数key能拿到较大的权重序列越长、候选key越多中间token在竞争中就越容易失利。你可以想象一个场景一万个候选答案里大部分key都和当前问题只有微弱关联模型在归一化时被迫把那点微弱关联摊薄到更多的候选上最终没有一个中间token能拿到足够的注意力。4.2 位置编码和训练数据也在“助攻”除了注意力归一化的数学特性Lost in Middle还有两个助推因素。第一个是位置编码在长序列下的表现。以RoPE为例相对位置距离越远旋转角度的变化越剧烈。当序列长度逼近甚至超出训练长度时长距离位置编码之间的区分度会下降模型对“遥远”位置的内容更难精准定位。中间内容恰恰处于一个尴尬的位置离开头和结尾都不近不远位置信息的可分辨性也不如两端。第二个是训练样本的分布偏差。在成千上万的训练文档里关键信息经常出现在开头标题、摘要、首段或结尾结论、总结模型在训练中学会了“注意力往两端放”的策略。这属于归纳偏置——模型并不是不会看中间而是在统计上形成了更倾向于两端的习惯。你在测试长文档问答时经常会遇到这类情况模型的答案言之凿凿但它是从开头或结尾“脑补”出来的中间真正的关键句反而没被引用。4.3 KV Cache管的是“算不快”不是“记不住”这里有一个特别重要的认知KV Cache解决的是“长上下文计算成本过高”的问题它保证的是模型“有条件”看到所有历史token但不保证模型“有倾向”从中间提取信息。换句话说KV Cache是一次磁盘归档Lost in Middle是档案管理员懒得翻中间那几页。两者解决的问题在不同层面KV Cache解决“能不能高效地让所有历史token参与计算”Lost in Middle解决“注意力机制愿不愿意给某些历史token足够的权重”。把这两个问题混为一谈是很多长上下文优化方案无效的根本原因。有人以为扩大KV Cache容量就能缓解Lost in Middle结果上下文加到32K中间信息照样“丢失”——因为问题从来不在容量上而在注意力分配上。4.4 缓解“迷失在中间”的工程打法基于上面的分析解决Lost in Middle有几种靠谱的路径信息前置/后置在构造prompt时把最关键的信息放在开头或结尾。这个方法很“土”但实测非常有效。你可以在RAG应用里把最相关的chunk放在开头或结尾而不是按原始文档顺序排列。我在做一个文档问答项目时仅仅调整了chunk排序策略中间答案的命中率就提升了十几个百分点。对长文档做结构化切分不要让模型一次性读完一篇几万字的长文而是先用检索把问题相关的段落挑出来再让模型基于局部上下文回答。RAG本质上就是主动规避Lost in Middle——既然模型读长文容易漏中间那就别让它读太长。使用长上下文强化后的模型部分新模型通过专门的长上下文训练数据包括把关键信息放在文档中段来调整注意力偏好这类模型在长文档问答上的丢失率会显著更低。选型时把这一点纳入考量比盲目追高上下文长度指标更实在。位置编码层面的改进像YaRN、ALiBi这类位置编码方案在长序列下有更好的外推表现部分框架已经支持在推理时切换或调整位置编码参数。这个方向偏研究工程上如果不是实在没办法优先选模型本身支持的方案。5. 实操手记KV Cache相关的坑与排查5.1 显存不够先看是不是KV Cache在作妖在部署一个模型时如果显存报错OOM不少人第一反应是“换更小的模型”或者“降低batch size”其实更值得检查的是KV Cache配置。我在本地部署7B模型时踩过一次典型的坑显存显示权重只占了4GB但跑16K上下文时直接OOM。后来一查KV Cache吃掉了十几个GB。排查步骤其实很简单先确认推理框架是否支持启用KV CacheHuggingFace的generate默认启用但部分封装代码会在传参时绕过去估算当前上下文对应的KV Cache大小用前面那个公式一算就清楚如果确实紧张优先做KV Cache量化再看能不能用GQA版本模型最后考虑缩短max_length。如果你用vLLM它会在启动时打印KV Cache的预留信息注意看那行日志就能知道显存大头在哪。5.2 多轮对话越到后面越慢甚至突然重算多轮对话系统里有个隐蔽的问题模型有最大上下文限制当对话轮次累计超限时框架会把最早的token的KV Cache丢掉只保留最近的一部分。这个“滚动”操作本身是正常的但不同框架的处理方式差异很大。有些框架在截断旧KV后会把剩余token的KV重新计算一遍因为滑动窗口外的注意力依赖被打断了这一下就会出现明显的延迟尖峰。还有更隐蔽的情况如果你用的框架在内部维护了缓存但对话历史在业务层被修改过比如插入了新消息缓存和输入对不上框架只能放弃整个缓存从头prefill。解决办法是理解你所用框架的缓存失效机制。比如在vLLM里开启前缀缓存可以让相同前缀的请求跳过重复的prefill计算在HuggingFace里则要小心控制输入ID和past_key_values的一致性不要让输入序列前后对不上。5.3 一个验证KV Cache的迷你实验为了帮助大家直观感受KV Cache的作用我写了一个极简的验证思路用PyTorch可以跑通重点是观察“耗时”和“显存”两个指标。这不是完整模型只是把注意力计算的核心逻辑拎出来对比import torch import time def attention_without_cache(q, k, v, maskNone): # 全量重算每步都要对全部历史token计算k和v scores torch.matmul(q, k.transpose(-2, -1)) / (k.size(-1) ** 0.5) weights torch.softmax(scores, dim-1) return torch.matmul(weights, v) def attention_with_cache(q, k_cache, v_cache): # 只计算当前q与缓存kv的注意力 scores torch.matmul(q, k_cache.transpose(-2, -1)) / (k_cache.size(-1) ** 0.5) weights torch.softmax(scores, dim-1) return torch.matmul(weights, v_cache) torch.manual_seed(0) seq_len 1024 d_model 4096 q torch.randn(1, 1, d_model) k torch.randn(1, seq_len, d_model) v torch.randn(1, seq_len, d_model) # 模拟无缓存场景每步都计算全量k和v start time.time() for _ in range(100): _ attention_without_cache(q, k, v) print(无缓存耗时:, time.time() - start) # 模拟有缓存场景 start time.time() k_cache, v_cache k, v for _ in range(100): _ attention_with_cache(q, k_cache, v_cache) print(有缓存耗时:, time.time() - start)真实模型里无缓存版本每步都要重新计算全量K、V的矩阵乘复杂度更高这里我把K、V预先算好了大家能从中看到一次完整推理时重复计算与直接读取的差别。实际操作中配上torch.profiler观察显存和算力占用体会会更明显。5.4 常见问题速查表现象可能原因检查方向长prompt首token延迟高Prefill阶段计算量大压prompt长度、加前缀缓存每token生成速度越来越慢Decode阶段KV Cache读取带宽吃紧缩短上下文、使用GQA模型、KV Cache量化显存OOM但模型权重不大KV Cache占用超预期用公式估算KV大小调整max_length多轮对话某轮突然卡顿KV Cache被截断后触发重算检查框架的缓存失效机制控制对话长度长文档中间信息答不上来Lost in Middle注意力分配问题调整信息位置用RAG切块换长上下文强化模型最后一点个人体会KV Cache这套机制我越深入越觉得它是理解整个大模型推理体系的钥匙。它一方面告诉你在做部署优化时显存花在了什么地方另一方面又把“注意力机制的行为模式”暴露在工程层面上——KV Cache解决计算效率但解决不了注意力分配偏见。这两件事在业务落地时经常纠缠在一起只有把它们的边界分清楚调试长上下文应用时才知道该往哪个方向使劲。如果你也在做长文本问答或者本地部署建议先把KV Cache的显存账算清楚再去看注意力分布的问题。这一步走顺了后面无论是上vLLM、做量化还是调prompt策略都会顺畅得多。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询