拓冰建站拓冰建站
首页 / 资讯中心 / 正文

为什么 KV cache 只存 K 和 V 向量,不存 Q

interview 采访/面试consequenceˈkɒnsɪkwənsn. 结果后果重要性价值how exactly is具体是怎样的noticeablyˈnəʊtɪsəbliadv. 显著地明显地引人注目地visualˈvɪʒuəladj. 视觉的视力的栩栩如生的n. 视觉资料指说明性的图片、影片等自回归生成中每生成一个新 token 只需要该位置的 query而 key/value 必须与所有历史位置对齐。因此每个 decode 步骤只需计算新位置的 Q、K、V缓存 K 和 V、丢弃 QLLM 中的 KV CachingInsight1标准 Transformer 自回归生成图上半部分模型只需要最后一个 token 的 hidden state就可以预测下一个 token输入 token 序列送入 Transformer 层输出一整串隐状态 hidden states只拿最后位置的隐状态送入 Projection 层得到词表 logits做 ArgMax 选出下一个 token新 token 拼到输入末尾把全部历史 token 重新完整跑一遍前向再预测下一个⚠️ 原始问题每多生成 1 个字就要把前面所有 token 全部重新计算一遍序列越长速度越慢大量重复计算Insight2注意力公式视角图中间新 token 的输出只依赖当前新 token 的 Query 向量所有历史 token 的 Key、Value 向量Q只有最新生成的 token 才需要计算每次都变(K、V)历史每一个 token 算出来的 Key、Value一旦算完就永远不变重点不需要反复重新算历史 token 的 K、V直接存起来复用即可这就是 KV Cache 的来源Insight3KV Cache 实际工作流程以第 5、6、7 号 token 生成为例生成第 5 个 token算出 K_5, V_5存入 KV 缓存生成第 6 个 token直接读取缓存里 K_1 ~ K_5V_1 ~ V_5不再重新计算只计算当前 token 的 Q_6同时算出新增 K_6,V_6 追加进缓存生成第 7 个 token复用全部历史缓存K_1~6,V_1~6只算当前 Q_7追加 K_7,V_7核心结论The key vectors and value vectors used during previous tokens do not change. Cache them to avoid recomputing them.历史 token 的 K、V 向量不会改变把它们缓存下来避免重复计算used during previous tokens前面那些 token 在计算时生成出来的为什么不缓存 QQuery 是当前步新 token 专属每一步都完全不一样没有复用价值所以 KV Cache 只存 K、V不存 Q收益生成长文本时大幅减少算力开销token 生成速度显著提升代价需要占用显存存储所有历史 K/V上下文越长KV 缓存占用显存越大这也是长上下文推理显存压力的主要来源背景LLMs are autoregressive so each token is predicted from every token before it, one at a time. This autoregressive nature has a direct consequence inside the model.语言模型属于自回归模型因此每个 token 的预测都是基于之前的所有 token 来进行一次只预测一个 token。这种自回归特性在模型中会产生直接的影响A forward pass over tokens produces hidden states, but only the last one is projected to logits and is required to generate the next token.So to understand why KV cache just stores K and V vector, we must back track to see how exactly is the last hidden state produced.通过传递 个 token可生成 个隐藏状态但只有最后一个状态会被映射到 logits 中并且是生成下一个 token 所必需的因此要理解为什么 KV 缓存只是存储了 K 和 V 向量必须回推看看最后那个隐藏状态究竟是如何生成的演示用 10 个 token 的提示来演示这个过程1) Prefill 预填充All 10 tokens go through the model in one forward pass, in parallel (with causal masking), since the whole prompt is already known.所有 10 个 token 都在一次前向传播过程中通过模型处理这些过程是并行进行的同时使用了因果掩蔽技术因为整个提示已经已知了At every layer, each of the 10 positions produces a query, a key and a value vector, and attention at each position runs against all positions up to it.每一层中这 10 个位置中的每一个位置都会产生一个 query、一个 key 和一个 value 向量。每个位置上的注意力机制都会与所有与其相邻的位置进行交互This pass is compute-heavy, and it’s why the first token takes noticeably longer than the ones after it. TTFT is mostly prefill.这个流程需要大量的计算资源这就是为什么第一个 token 的处理时间明显比后面的 token 要长处理 prompt 的 token 比较多。TTFT 过程大部分都是预填充操作2) The first output token 第一个输出 tokenTo generate the 11th token, only the 10th token’s hidden state is needed. So this is projected from the hidden-dim to vocab-dim to generate logits over vocab.要生成第 11 个 token只需要使用第 10 个 token 的隐藏状态即可。因此这个过程是从隐藏维度 hidden-dim 映射到词汇维度 vocab-dim从而生成关于词汇的 logitsThese logits then go through softmax and sampling to generate token 11这些 logits 随后会经过 softmax 函数和采样过程从而生成 token 113) Back-track the hidden state 追溯隐藏状态The last hidden state is the last row of the feedforward block’s output. The feedforward block is position-wise (it’s applied to each row independently) so that row comes from the last row of the attention output before it.最后一个隐藏状态就是前馈模块feedforward block输出的最后一行数据前馈模块 FFN 按位置进行独立应用于每一行因此该行来自其前一行的注意力输出的最后一行So now we need to see how the last row of attention is computed现在需要了解最后一行的注意力值 attention 是如何计算出来的┌──────┼─row0(t1)──FFN── row0 Attention输出矩阵 ────┼─row1(t2)──FFN── row1 ┼──────┼─row2(t3)──FFN── row2 └──────┼─row3(t4)──FFN── row3 ← last hidden stateAttention 层会做全局交互每个位置能看到全部 tokenFeedForward(FFN)position‑wise每个位置只处理自己的向量行之间没有交互FFN 只干活不串门每一行向量自己单独过一遍网络所以序列末尾 token 对应的那一行只来自上一步 attention 输出的末尾那一行4) Attention matrix 注意力矩阵QKᵀ for a 10-token prompt will give a 10 × 10 matrix.对于包含 10 个 token 的提示QKᵀ 将返回一个 10×10 的矩阵Rowwill have the dot product of querywith every key.行将会与每个键进行点积运算Row 10 is therefore Q₁₀·K₁, Q₁₀·K₂, all the way to Q₁₀·K₁₀因此第 10 行可以表示为 Q₁₀·K₁、Q₁₀·K₂一直到 Q₁₀·K₁₀Notice that only Q₁₀ appears in it. Q₁ through Q₉ only belong to their corresponding rows 1-9, and those rows’ hidden states we already discarded because they were never needed.注意只有 Q₁₀出现在其中。Q₁到 Q₉只属于它们对应的第 1 到 9 行而那些行的隐藏状态已经被我们丢弃了因为它们根本没有必要存在The last row of attention goes through softmax and multiplies the full stack of value vectors, V₁ through V₁₀, to give the last row of the attention output.最后一行的注意力输出是通过 softmax 函数计算得出的。该函数会将所有价值向量 V₁到 V₁₀相乘从而得到最终的注意力输出结果So the last hidden state depends on exactly three things: Q₁₀, every key, and every value.因此最后一个隐藏状态取决于三件事物Q₁₀、每一个 key以及每一个 value5) Generating token 12 生成 token 12Token 11 is appended, and this time, we need row 11’s hidden state to generate token 12.现在需要添加第 11 个 token而这次需要使用第 11 行的隐藏状态来生成第 12 个 tokenMathematically, attention operation turns out to be Q₁₁ against K₁ through K₁₁, then multiplied by V₁ through V₁₁.从数学角度来看注意力操作的流程可以表述为先对 K₁到 K₁₁进行 Q₁₁运算然后再将结果乘以 V₁到 V₁₁K₁ through K₁₁ and V₁ through V₁₁ are bit-for-bit what prefill first token produced since under causal masking, a token’s key and value depend on that token and the ones before it, never on anything after, so appending token 11 cannot change anything at position 3.从 K₁到 K₁₁以及从 V₁到 V₁₁这些值都是经过预填充和第一个标记后产生的。一个标记的关键值和值取决于该标记以及它之前的所有标记而不取决于之后任何标记的值。因此添加标记 11 并不会改变第三个位置的值原因是在因果掩码causal masking约束下某个 token 的 K、V只由它自己以及它前面的 token 决定永远不会依赖它后面的 token所以在序列末尾追加第 11 号 token完全不会修改位置 3 处的 (K_3,V_3)bit‑for‑bit逐比特完全相同数值一丝一毫都不会变不是近似相等是内存里每一位都一模一样Prefill 阶段输入 prompt[t₁,t₂,t₃ … t₁₀]一次性并行全部计算一次性算出 K_1…K_10,V_1…V_10)存入 KV Cache之后进入 decode 循环生成第 11 个 token t_11只新算 K_11,V_11追加到缓存末尾K_1,K_2,K_3…K_10 全部沿用 prefill 算出来的旧值不会重新计算不会被改写appending token 11 cannot change anything at position 3在尾巴上新增加 token11不会把位置 3 的 K3/V3 改一丝一毫6) The cache state 缓存状态Overall, this implies that you just need to retain the keys and values at each decoding step, and compute only the new position’s Q, K and V.总体而言这意味着只需要记住每个 decoding 步骤中的 key 和 value并仅计算新位置的 Q、K 和 V 参数Each decode step requires one query vector, which is never used again, so they are never cached across the decoding process.每个 decode 步骤都需要一个 query 向量而这个向量在之后不会再次被使用因此它们在整个 decoding 过程中不会被缓存起来That said, KV cache is only one of four separate caching layers in an LLM stack.不过KV 缓存只是 LLM 架构中四个独立缓存层中的一个而已The other three are prefix caching on the server, prompt caching billed by a provider, and a semantic cache that skips the model entirely.另外三种方式是服务器端的前缀缓存、由 provider 提供的提示缓存以及完全不使用模型的语义缓存
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门