Attentionの変種図鑑 — MQA・GQA・スライディング窓・線形注意
MQA・GQA・スライディング窓・線形注意は、それぞれ勝手に生まれた別々の工夫ではありません。KVキャッシュの大きさを決める一本の掛け算式があり、変種は「どの項を叩いたか」で分類できます。系譜を式の上に並べ直すと、何を捨てて何を得たのかが一目で見えます。
要点
- KVキャッシュの大きさは「層数 × KVヘッド数 × ヘッドの幅 × 溜まったトークン数 × 同時実行数」の掛け算で決まる。
- MQAはKとVのヘッドを1本だけにし、GQAはQueryヘッドをグループに分けてグループごとにKVを共有する。
- スライディング窓注意は各トークンが直前 トークンだけを見るので、計算量は になる。
- 線形注意はsoftmaxをやめて過去を1つの箱に畳み込み、箱の大きさは文の長さによらない。
- FlashAttentionは変種ではなく、出力は素の注意と数学的に完全に一致し、KVキャッシュは1バイトも減らない。
Fast Transformer Decoding: One Write-Head is All You Need
一次資料 — この記事の根拠
この解説の公開 2026-08-22
Fast Transformer Decoding: One Write-Head is All You NeedarXiv:1911.02150論文ページ·PDFGQA: Training Generalized Multi-Query Transformer Models from Multi-Head CheckpointsarXiv:2305.13245論文ページ·PDF
Longformer: The Long-Document TransformerarXiv:2004.05150論文ページ·PDF
Transformers are RNNs: Fast Autoregressive Transformers with Linear AttentionarXiv:2006.16236論文ページ·PDF
「注意」は一種類ではない
Attention機構を1から理解するで見たのは、教科書に載っている素の自己注意でした。全単語が全単語を見渡し、内積で重みを決め、Valueを混ぜる。美しい仕組みですが、実際に動いているモデルの設定ファイルを開くと、そのままの形はむしろ少数派です。
会議の比喩を続けます。あなたは参加者全員の名札(Key)を見比べ、発言(Value)をメモに混ぜる係でした。参加者が10人ならこれで困りません。ところが実際のLLMは、会話が進むほど参加者が増え続ける会議室にいます。1万トークンの文脈とは、1万人分の名札と発言を捨てずに机に積み上げたまま、次の一言を考えるということです。
変種と呼ばれるものたちは、この積み上がった机をどう畳むかの答えです。そして重要なのは、それぞれが好き勝手に生まれた別々の工夫ではないことです。畳める場所は数えられるほどしかなく、変種はそのどれを叩いたかで整理できます。
高いのは2か所ある
まず「何が高いのか」を分けます。混同されがちですが、コストの正体は場面によって別物です。
学習時と、プロンプトを一度に読み込むプリフィルの段階では、( は文の長さ)の点数表そのものが重荷になります。文が2倍なら表は4倍。ここは計算量とメモリの両方の話です。
一方、1トークンずつ吐き出す生成の段階では、点数表は1行しかできません。新しいトークンのQueryと過去全部のKeyとの照合、それだけです。にもかかわらず生成は遅い。理由は計算ではなく、過去のKとVを丸ごと読み出すことにあります。これがKVキャッシュで、その大きさは全部が掛け算で決まります。
要するに、KVキャッシュの大きさは「層数 × KVヘッド数 × ヘッドの幅 × 溜まったトークン数 × 同時実行数」の掛け算で、それにKとVの2種類ぶんと、数値1個あたりのバイト数を掛けただけです。順に読みます。先頭の はKとVの2種類ぶん。 は層の数(層ごとに別のキャッシュを持つ)。 はKVヘッドの本数。 は1ヘッドあたりの幅。 は溜まったトークン数。 は同時に捌いている会話の数。 は数値1個のバイト数(FP16なら2)。
全部が掛け算だ、という点が肝心です。どれか1つを にすれば、全体がそのまま になります。逆に言えば、削れる場所は式の中にしかありません。だから変種の系譜は、この式のどこを叩いたかで分類できます。
- を減らす → MQA / GQA(そしてMLA)
- 実効的な を減らす → スライディング窓
- そもそも の表を作らない → 線形注意
- を減らす → KVキャッシュの量子化
MQA — 名簿を1冊にする
最初の一手は、いちばん乱暴な引き算でした。2019年にNoam Shazeerが提案した Multi-Query Attention(MQA) は、Queryヘッドは複数のまま、KとVのヘッドを1本だけにします。質問は人それぞれでよいが、参照する名簿は1冊を全員で回し読みする設計です。式の が になるので、キャッシュはヘッド数ぶんの1に縮みます。
なぜ速度に直結するのか。生成では1トークン作るたびにKVキャッシュを丸ごと読み出します。演算自体は軽く、読み出しの帯域が律速です。読むバイト数が になれば、そのまま待ち時間が縮む。FLOPsはほとんど変わらないのに速くなる、という一見奇妙な現象の正体はこれです。
代償は表現力です。ヘッドごとに違う見方を持てるのが利点だったのに、見る対象を全ヘッドで共有してしまう。品質の低下は小さいと報告されましたが、ゼロではありません。
GQA — 間を取る
2023年、Ainslieらが出した折衷案が Grouped-Query Attention(GQA) です。 本のQueryヘッドを 個のグループに分け、グループごとに1組のKVを共有します。 ならMQA、 ならMHA。両端に既存手法を持つ連続したつまみです。
もう一つの貢献は、既存のMHAチェックポイントからグループ内のKVヘッドを平均して初期化し、元の事前学習に比べればごくわずかな追加学習(uptraining)でGQA版に変換できると示したことです。ゼロから学習し直さなくてよいのが普及の決め手でした。Llama 2 の70Bは64本のQueryヘッドに対しKVヘッドが8本で、キャッシュはMHAの8分の1です。
スライディング窓 — 見る範囲を切る
ここまでは「誰の名札を持つか」の話でした。次は「どこまで見るか」を削ります。スライディング窓注意では、各トークンは直前 トークンだけを見ます。 の表のうち対角線に沿った幅 の帯だけを残し、外側は捨てる。計算量は になり、 に対して直線的にしか増えません。
そんなに切って文全体が分かるのか、と思うところですが、層を重ねると受容野が広がります。1層目で各トークンは 先まで見る。2層目が見る相手はすでに 先を含むので、実効的に 先まで届く。 層あれば概ね の範囲に伝わる、という伝言リレーです。
2020年のLongformerが長文向けに整備し、2023年のMistral 7Bが窓幅4096でデコーダに採用して一気に知られました。生成時のうまみが大きく、キャッシュに残すのは直近 トークンぶんだけなので、メモリが文脈長に比例して増えなくなります。弱点も明確で、窓の外を正確に引用する類のタスクは苦手です。Longformerは一部のトークンにだけ全域を見る権限(global attention)を与えて補いました。
線形注意 — softmaxを外して順番を入れ替える
最後は、いちばん大胆な変種です。 の表をそもそも作らない。
素の注意が を経由するのは、softmaxが 全体にかかっているからです。行列積には結合則があるので、本来なら を と計算し直せます。前者は の表を作り、後者が作るのは の小さな箱です。ところが間に非線形なsoftmaxが挟まると、この入れ替えができません。
ならばsoftmaxをやめよう、というのが線形注意です。指数関数の代わりに、非負の特徴写像 を通した内積で類似度を測ります。
つまり、分子は「これまでの名札と発言の組を全部足し込んだ箱」に、いまの質問を当てているだけです。分母は重みの合計を1にするための正規化。肝心なのは の部分で、過去が1つの箱に畳み込まれているという点です。箱の大きさは文の長さによりません。
S = np.zeros((d_k, d_v)) # 状態: 名札×発言 の箱
z = np.zeros(d_k) # 正規化用の合計
for k, v, q in stream: # 1トークンずつ
S += np.outer(phi(k), v) # 箱に足し込むだけ
z += phi(k)
out = (phi(q) @ S) / (phi(q) @ z + 1e-6)
これが Katharopoulos らの論文タイトル「Transformers are RNNs」の意味です。生成時の線形注意は、固定サイズの状態を1つ持って更新するRNNと同じ形になり、KVキャッシュが「長さに比例して伸びるリスト」から「大きさの決まった行列」に変わります。
では、なぜ標準にならなかったのか。捨てたものが大きすぎたからです。softmaxは、必要とあらばたった1つのトークンに重みを振り切れます。「この代名詞が指すのは3語前のあの名詞だ」という鋭い参照は、その振り切りで実現されている。非負の特徴写像の内積では同じ鋭さを作りにくく、実用モデルでの採用が限定的なのはこの一点によるところが大きいと考えられています。
混ぜて使う、という現在地
実際のモデルは、これらを排他的に選んではいません。GQAを土台に、層ごとにスライディング窓と全域注意を交互に配置する組み合わせが普通です。窓付きの層で近くを密に見て、時々挟まる全域の層で遠くを拾う分業になります。KVそのものを低ランクの潜在ベクトルに圧縮してキャッシュする MLA(Multi-head Latent Attention, DeepSeek-V2) のような手も出ています。
一つだけ線を引いておきます。FlashAttentionは変種ではありません。 の表を高速メモリの中でブロックごとに処理し、遅いメモリに書き出さずに済ませる実装上の工夫で、出力は素の注意と数学的に完全に一致します。近似ではないので品質は変わらず、そしてKVキャッシュは1バイトも減りません。
現場ではこう使う
この知識が要るのは、モデルを選ぶ人と、推論基盤を運用する人です。
モデル選定のとき。 config.json の num_attention_heads と num_key_value_heads を見比べます。同数ならMHA、KVが1ならMQA、その間ならGQA。キャッシュ量は num_key_value_heads に比例するので、この1行だけで「同じGPUで何並列さばけるか」の桁がおおよそ決まります。sliding_window があれば窓付きです。
サーバを立てるとき。 vLLMなら --max-model-len、--gpu-memory-utilization、--kv-cache-dtype が主な握りどころです。同時実行数はおおむね「KVキャッシュに回せるメモリ ÷ 1リクエストあたりのキャッシュ量」で決まるので、冒頭の掛け算式がそのまま見積もりに使えます。
設計レビューで問われる形。 「MQAとGQAの違いを、どこが速くなるかまで説明してください」。筋道は、削るのはKVヘッド数 → 減るのは生成のたびに読み出すバイト数 → 律速はメモリ帯域なので速くなる、そしてFLOPsはほとんど変わらない、まで言えると締まります。
落とし穴を3つ。
- テンソル並列とKVヘッド数。 KVヘッドが8本のモデルを8枚を超えるGPUで分割すると割り切れません。実装によってはKVヘッドを複製して辻褄を合わせるので、期待したメモリ削減が目減りします。並列度を決める前にKVヘッド数を見ること。
- 「128k対応」の読み方。 窓付きモデルの対応長は、全トークンを等しく参照できることを意味しません。長文の一箇所を正確に引用させる用途では、実データで確かめてから採用する。
- FlashAttentionへの期待違い。 入れてもKVキャッシュは減りません。OOMに効くのは多くの場合こちらではなく、文脈長・バッチ・KVのdtypeの側です。
まとめ
- 変種は、KVキャッシュの大きさを決める掛け算式のどの項を叩くかで整理できる
- MQA/GQAは を、スライディング窓は実効的な を、線形注意は の表そのものを削る
- 生成が遅いのは計算ではなくメモリ帯域のせいなので、読むバイト数を減らす工夫がそのまま速度になる
- FlashAttentionは近似ではなく厳密な実装最適化で、KVキャッシュは減らさない
見積もりの手順はKVキャッシュを1から理解するにまとめてあります。変種を選ぶ前に、自分のワークロードでどの項が効いているかを数えるのが順序として正しいやり方です。
コメント
コメントにはログインが必要です