KV キャッシュと LLM 推論の最適化
LLM の生成が遅い・GPU メモリを食う原因の多くは KV キャッシュ。その仕組みと見積もり、PagedAttention 等の最適化を押さえれば、推論コストの勘所が一気に見通せる。
- 1.自己回帰生成では過去トークンの Key/Value を毎ステップ再計算するのが無駄。これをキャッシュし1トークンあたりの計算量を O(n²) から O(n) 相当へ落とすのが KV キャッシュ。速さの代償に GPU メモリを消費する。
- 2.KV キャッシュ量は おおむね 2×層数×KVヘッド数×head_dim×系列長×バッチ×精度バイト数 で決まり、長文・大バッチで急増。MQA/GQA はKVヘッドを共有してこれを数分の一〜十数分の一に圧縮する。
- 3.vLLM の PagedAttention はKVをページ単位で非連続に確保し断片化を解消、連続バッチングと併せてスループットを大きく改善する。量子化・FlashAttention も実務の定番。
なぜ「KV キャッシュ」が推論の主役なのか
LLM と Transformer で見たとおり、LLM は 自己回帰でトークンを1つずつ生成します。素朴に実装すると、新しいトークンを出すたびに「これまでの全トークン」を入力し直してアテンションを計算することになります。系列長 n のとき、各ステップで n 個分のアテンションを計算するので、全体では O(n²) に膨らみます。
ここで効くのが KV キャッシュ です。アテンションは入力トークンごとに Query・Key・Value の3つのベクトルを作りますが、過去トークンの Key と Value は、後続のステップでも値が変わりません。つまり一度計算すれば使い回せます。これをメモリに貯めておくのが KV キャッシュであり、現代の LLM 推論で速度とメモリの両面を支配する中心的な仕組みです。
扱うのは推論(inference)時の最適化です。学習時は教師強制で全トークンを一括処理するため事情が異なります。ここでは「学習済みモデルに文章を生成させる」場面に絞ります。
アテンションのどこを再利用できるのか
復習すると、自己回帰のアテンションは概念的に次の式です(数式は描画されないのでテキストで示します)。
Attention(Q, K, V) = softmax(QKᵀ / √d_k) · V
Q(Query): いま処理中のトークンが「何を探すか」K(Key): 各トークンが「自分は何者か」を示す見出しV(Value): 各トークンが実際に運ぶ中身
生成の各ステップで新しい Query は1トークン分だけ作られます。一方 K と V は「これまでの全トークン分」が必要ですが、過去分は前ステップと同一です。だから過去の K・V をキャッシュし、毎ステップ追加するのは新トークン1つ分の K/V だけにできます。
ステップ t:
1. 新トークン x_t から q_t, k_t, v_t を計算
2. K_cache に k_t を追記、V_cache に v_t を追記 ← ここが「キャッシュ」
3. attn = softmax(q_t · K_cacheᵀ / √d_k) · V_cache ← 過去K/Vは再計算しない
4. 次トークンを予測 → t+1 へ
この結果、1トークンの生成計算は O(n²) ではなく O(n) 相当(過去 n 件との内積)に下がります。生成が「文の頭から徐々に遅くなる」ように感じるのは、n が伸びてこの内積コストとキャッシュ参照量が増えるためです。
推論は2フェーズに分かれる:Prefill と Decode
KV キャッシュを理解する鍵が、推論を Prefill と Decode の2段階で捉えることです。性能特性がまったく異なります。
| フェーズ | 何をするか | ボトルネック | 並列性 |
|---|---|---|---|
| Prefill(プロンプト処理) | 入力プロンプト全トークンを一括で処理しKVキャッシュを構築 | 演算律速(compute-bound)。行列積が大きい | 全トークンを並列計算できる |
| Decode(逐次生成) | 1トークンずつ生成しKVを追記 | メモリ帯域律速(memory-bound)。重みとKVの読み出しが支配的 | 本質的に直列、1ステップ1トークン |
Decode が メモリ帯域律速なのが重要です。1トークン生成するだけのために、モデルの全パラメータと全 KV キャッシュを HBM から読み出す必要があり、演算量に対して読み出し量が大きい。だから Decode の高速化は「計算を速くする」より「読み出すデータ量を減らす/読み出し効率を上げる」方向が効きます。量子化や MQA/GQA が効くのはこのためです。
体感の「最初の応答までの待ち時間(TTFT, Time To First Token)」は主に Prefill が支配します。長いプロンプトほど Prefill の行列積が増え、最初の1文字が出るまで待たされます。一方、生成開始後のスループット(TPOT, Time Per Output Token)は Decode 側で決まります。入力が長い問題と出力が長い問題は、効くチューニングが別物です。
メモリ消費を見積もる:長文・大バッチで爆発する
KV キャッシュの泣きどころはメモリです。サイズは次のおおよその式で見積もれます。
KVキャッシュ量(バイト)
≒ 2 × 層数 × KVヘッド数 × head_dim × 系列長 × バッチサイズ × 精度バイト数
↑ KとVで2倍
具体例として、層数48・KVヘッド数(=注意ヘッド数)64・head_dim 128・FP16(2バイト)のモデルで、系列長4096・バッチ1なら概算は次のとおりです。
2 × 48 × 64 × 128 × 4096 × 1 × 2 バイト
≒ 6.4 GB (1リクエストあたり、4Kコンテキストで)
たった1リクエストで数 GB です。系列長とバッチサイズに比例するため、同時実行ユーザーを増やしたり長文を扱ったりすると、モデル重みとは別枠で KV キャッシュが GPU メモリを圧迫します。最大同時実行数や扱える最大コンテキスト長は、しばしば計算速度ではなく KV キャッシュが置けるかで頭打ちになります。
コンテキストウィンドウを4Kから8Kへ倍にすると、計算だけでなくKVキャッシュも倍になります。長文要約や巨大プロンプトが「動くけど急に重い・OOM する」のは、この KV の線形増加が効いているケースが多い。長文を投げる前に、上式で必要メモリを概算する癖をつけると事故を避けられます。
KV を圧縮する:MHA → MQA → GQA
メモリ式の KVヘッド数 に注目すると、ここを削れば KV が直接縮むと分かります。これを狙うのが MQA / GQA です。
- MHA(Multi-Head Attention): 従来型。Query ヘッドと同数だけ K/V ヘッドを持つ。表現力は高いが KV が最も大きい。
- MQA(Multi-Query Attention): 全 Query ヘッドで K/V を1組だけ共有。KV を劇的に削減できるが、品質がやや落ちることがある。
- GQA(Grouped-Query Attention): 中間。Query ヘッドをグループに分け、グループごとに K/V を共有。品質をほぼ保ったまま KV を数分の一に圧縮でき、現在の主流。
| 方式 | KVヘッド数(Qヘッド=64の例) | KVキャッシュ量 | 品質への影響 |
|---|---|---|---|
| MHA | 64 | 基準(最大) | 最良だがメモリ重い |
| GQA(8グループ) | 8 | 約 1/8 | ほぼ劣化なし。実用上の定番 |
| MQA | 1 | 約 1/64 | 最小。タスクにより品質低下 |
KV を縮める他の手段に、KV キャッシュ自体の量子化(FP16 → INT8/INT4 で1要素のバイト数を削る)や、古い/重要度の低いトークンの KV を捨てる手法(ウィンドウ化、エビクション)もあります。いずれも「メモリと品質のトレードオフをどこで取るか」という同じ問題の別解です。
PagedAttention:断片化を解いてスループットを稼ぐ
KV キャッシュを単純に「1リクエスト=連続した大きなメモリ領域」として確保すると、深刻な無駄が生じます。生成がどこまで伸びるか事前に分からないため最大長で先に確保しがちで、実際には使われない領域(内部断片化)や、空きはあるのに連続領域が取れず割り当て不能になる外部断片化が起きます。
vLLM が導入した PagedAttention は、OS の仮想メモリ/ページングの発想を持ち込みます。
- KV キャッシュを**固定サイズの「ブロック(ページ)」**に分割する。
- 各リクエストの KV は、物理的に非連続なブロックの集まりとして管理し、論理→物理の対応表(ブロックテーブル)で参照する。
- 必要になった分だけブロックを割り当てるので、事前の過剰確保が不要になり断片化がほぼ消える。
- 共通プレフィックス(同じシステムプロンプト等)の KV ブロックを複数リクエストで共有でき、メモリをさらに節約できる。
従来: [req A ████████____________] ← 末尾は予約だけで未使用(内部断片化)
[req B ██████______________]
Paged: ブロック単位で必要分だけ確保し、空きブロックを使い回す
A: [b3][b7][b1] B: [b2][b5] 共有: システムプロンプト→[b0]を両者で参照
これにより実効的に置けるリクエスト数が増え、GPU を遊ばせず詰め込めます。さらに、生成が終わったリクエストの枠へ次々と新規リクエストを差し込む 連続バッチング(continuous batching) と組み合わせると、スループットが大きく向上します。これが現代の LLM サービングエンジン(vLLM 等)の核心です。
- KVキャッシュはDecodeを高速化する代わりにGPUメモリを消費する。サイズは系列長×バッチに線形。
- 推論は Prefill(演算律速) と Decode(メモリ帯域律速) の2フェーズ。TTFT は Prefill、TPOT は Decode が支配。
- GQA/MQA=KVヘッド共有でメモリ削減。PagedAttention=ページングで断片化解消+プレフィックス共有。連続バッチングでスループット向上。
周辺の最適化:FlashAttention・量子化・投機的デコード
KV キャッシュ以外にも、推論を支える定番があります。組み合わせて使われます。
| 技術 | 効くフェーズ | 狙い | ひとことで |
|---|---|---|---|
| FlashAttention | Prefill / Decode 双方 | アテンションのメモリ読み書きを削減 | 巨大な中間行列をHBMに書かずタイル計算で高速化 |
| 量子化(重み/KV) | 主に Decode | 読み出しデータ量を削減 | FP16→INT8/INT4で帯域とメモリを節約 |
| 連続バッチング | Decode(サービング) | GPU 稼働率を上げる | 完了枠へ新リクエストを随時投入 |
| 投機的デコード | Decode | 実効生成速度を上げる | 小モデルで先読みし大モデルで一括検証 |
特に FlashAttention は混同しやすいので区別します。FlashAttention は softmax の計算過程を効率化し、n×n の巨大なアテンション行列を丸ごと HBM に書き出さずに済ませる手法です。一方 KV キャッシュは K/V の再計算を省く手法。前者は計算の段取りの最適化、後者は結果の使い回しで、レイヤーが違うため両立します。
自前ホスティングなら、まず vLLM などの最適化済みエンジンに載せるだけで PagedAttention・連続バッチング・FlashAttention の恩恵をまとめて受けられます。その上で、GQA採用モデルの選定、KV/重みの量子化、最大コンテキスト長の現実的な設定を詰めるのが費用対効果の高い順序です。
まとめ:KV キャッシュを軸に推論を捉える
LLM 推論のコストとレイテンシは、突き詰めると 「KV キャッシュをいかに小さく、いかに効率よく扱うか」 に集約されます。
| 論点 | 実態 | そこから言えること |
|---|---|---|
| なぜキャッシュするか | 過去トークンのK/Vは不変だから再利用できる | Decodeを O(n) 相当に落とす。代償はメモリ |
| なぜメモリを食うか | KVは系列長×バッチに線形で増える | 最大同時数・最大文長はKVが置けるかで決まる |
| どう圧縮するか | GQA/MQAでKVヘッド共有、量子化でバイト削減 | 品質とメモリのトレードオフを選ぶ |
| どう効率化するか | PagedAttentionで断片化解消+プレフィックス共有 | 連続バッチングと併せスループット最大化 |
この見立てを持つと、「コンテキストを伸ばすと急に重い」「同時接続を増やすと OOM する」「最初の1文字が遅い」といった現象が、Prefill / Decode と KV メモリの言葉で説明でき、打ち手の優先順位もつけられます。プロンプト設計で入力長を抑える工夫は プロンプトエンジニアリング や トークン化 と、長文を外部知識で補う設計は ファインチューニングと RAG と合わせて読むと、推論コストの全体像が線でつながります。
AI/機械学習 Article
KV キャッシュと LLM 推論の最適化を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
LLM
比較で見る軸
難易度: advanced / カテゴリ: AI/機械学習 / タグ数: 5
導入後に効く点
KV キャッシュ量は おおむね 2×層数×KVヘッド数×head_dim×系列長×バッチ×精度バイト数 で決まり、長文・大バッチで急増。MQA/GQA はKVヘッドを共有してこれを数分の一〜十数分の一に圧縮する。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- AI/機械学習
- タグ数
- 5
判断チェックリスト
- 自社の用途が「LLM / 推論最適化」に近いか確認する。
- 強みである「自己回帰生成では過去トークンの Key/Value を毎ステップ再計算するのが無駄。これをキャッシュし1トークンあたりの計算量を O(n²) から O(n) 相当へ落とすのが KV キャッシュ。速さの代償に GPU メモリを消費する。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。