MQA(Multi-Query Attention) 是一种对标准多头注意力的改进,它让所有注意力头共享同一份键(K)和值(V),而只保留查询(Q)是各自独立的。
流程
输入表示
对于序列中第 \(i\) 个位置的输入向量 \(\mathbf{x}_i\),我们计算:
- 查询向量(每个头独立):
\[ \mathbf{q}_i^{(s)} = \mathbf{x}_i \mathbf{W}_q^{(s)} \quad (s=1,\dots,h) \]
- 键向量(所有头共享):
\[ \mathbf{k}_i = \mathbf{x}_i \mathbf{W}_k \]
- 值向量(所有头共享):
\[ \mathbf{v}_i = \mathbf{x}_i \mathbf{W}_v \]
参数含义
- \(i\):输入序列中的位置索引,取值范围为 \(1\) 到序列长度 \(L\),对应每个位置的词或特征向量 \(\mathbf{x}_i\)。在自注意力中,每个位置会与序列所有位置进行交互。
- \(s\):注意力头的编号,取值范围为 \(1\) 到总头数 \(h\)。每个注意力头学习不同的表示子空间,用于计算查询向量 \(\mathbf{q}_i^{(s)}\)。键向量 \(\mathbf{k}_i\) 和值向量 \(\mathbf{v}_i\) 在原文中为所有头共享,不随 \(s\) 变化。
- \(h\):注意力头的总数,是多头注意力机制的超参数(常用如 8、12、16)。每个头使用独立的查询变换矩阵 \(\mathbf{W}_q^{(s)}\),而键/值变换矩阵 \(\mathbf{W}_k, \mathbf{W}_v\) 在所有头间共享(与标准 Transformer 不同)。所有头的输出会拼接或聚合为该位置的完整表示。
注意力计算
注意力计算(对每个头 \(s\)):
在解码的第 \(t\) 步,第
\(s\) 个头的输出 \(\mathbf{o}_t^{(s)}\)
是通过将当前查询 \(\mathbf{q}_t^{(s)}\)
与之前所有位置的共享键 \(\mathbf{k}_{1},\dots,\mathbf{k}_t\)
做点积,得到注意力权重,再对共享值 \(\mathbf{v}_{1},\dots,\mathbf{v}_t\)
加权求和: \[
\mathbf{o}_t^{(s)} = \frac{\sum_{i=1}^{t} \exp\left(
\mathbf{q}_t^{(s)} \cdot \mathbf{k}_i^\top \right)
\mathbf{v}_i}{\sum_{i=1}^{t} \exp\left( \mathbf{q}_t^{(s)}
\cdot \mathbf{k}_i^\top \right)}
\]
最终输出
将所有 \(h\) 个头的输出向量拼接起来,得到第 \(t\) 步的注意力输出: \[ \mathbf{o}_t = \left[ \mathbf{o}_t^{(1)}, \mathbf{o}_t^{(2)}, \dots, \mathbf{o}_t^{(h)} \right] \]
这样做的优势:因为 \(K\) 和 \(V\) 的投影矩阵被所有头共享,模型在推理时需要缓存的数据量大大减少(只需缓存一份 \(K\) 和 \(V\)),从而显著节省内存并提高推理速度,同时通常能保持与标准多头注意力相近的模型效果。