跳转至

0x06 KV-Cache

LLM 生成过程做的是 Next Token Prediction,也就是输入前 \(n\) 个 token,预测第 \(n+1\) 个 token。首先考察做 Self-Attention 时,按照正常计算方式是如何运作的。下面举例输入是三个 token 的情况,也就是根据前三个 token 预测第四个 token:

\[ O = \begin{bmatrix} O_0\\ O_1\\ O_2 \end{bmatrix} = \mathrm{softmax}\!\left( \frac{ \begin{bmatrix} Q_0K_0^\top & Q_0K_1^\top & Q_0K_2^\top\\ Q_1K_0^\top & Q_1K_1^\top & Q_1K_2^\top\\ Q_2K_0^\top & Q_2K_1^\top & Q_2K_2^\top \end{bmatrix} }{\sqrt{d_k}} \right) \begin{bmatrix} V_0\\ V_1\\ V_2 \end{bmatrix}. \]

注意,在计算 \(QK^\top\) 之后,还会对 Attention Score 矩阵进行下三角的 Mask,也就是实际有效的注意力分数为:

\[ \begin{bmatrix} Q_0K_0^\top & 0 & 0\\ Q_1K_0^\top & Q_1K_1^\top & 0\\ Q_2K_0^\top & Q_2K_1^\top & Q_2K_2^\top \end{bmatrix}. \]

从计算中可以看到,实际参与到第三个 token 预测 \(O_2\) 中的只有 Attention Score 计算的最后一行,和最后的 \(V\) ,也就是:

\[ \begin{aligned} \mathrm{softmax}\!\left(\frac{[\,Q_3K_1^\top,\;Q_3K_2^\top,\;Q_3K_3^\top\,]}{\sqrt{d_k}}\right) \begin{bmatrix} V_1\\ V_2\\ V_3 \end{bmatrix}. \end{aligned} \]

在这个计算中包含之前历史的 \(K、V\) 以及当前输入的第三个 token 对应的 \(Q_3\)

简单推广一下

  • 在输入 \(n\) 个 token 预测第 \(n+1\) 个 token 的时候,会用到前 \(n\) 个 token 的 \(K,V\) 以及第 \(n\) 个 token 的 \(Q\)
  • 在输入 \(n+1\) 个 token 预测 \(n+2\) 个 token 时,会用到前 \(n+1\) 个 token 的 \(K,V\) 以及第 \(n + 2\) 个 token 的 \(Q\)

不难发现,这其中包括对历史 KV 的重复使用,并且这些 KV 在后续逐个 token 的生成过程中,并不会发生改变,因此避免对这些 KV 的重复计算,很直接的方式就是将他们缓存起来,达到减少计算,提高升成速度的效果。

  • 为什么不把 Attention Score 矩阵(\(QK^\top\))整个缓存?
  • 为什么不缓存历史的 \(Q\)