LLMのキャッシュとは
LLM(大規模言語モデル)のキャッシュとは、一度計算した結果や処理した情報を一時的に保存し、後続の処理で再利用する仕組みです。
LLMは文章を生成する際に大量の計算を行います。
そのため、同じ計算を何度も繰り返すと、処理時間が長くなったり、GPUの計算資源を多く消費したりします。
そこで、すでに計算した結果をキャッシュとして保存し、必要に応じて再利用することで、推論処理を効率化します。
ただし、「LLMのキャッシュ」という言葉は1つの仕組みだけを指すわけではありません。
代表的なものとしては、LLM内部の推論で使われる「KVキャッシュ」のほか、プロンプトキャッシュ、Prefix Cache、レスポンスキャッシュ、Semantic Cacheなどがあります。
LLMで使われる主なキャッシュの種類
LLM関連のシステムでは、目的に応じてさまざまなキャッシュが利用されます。
代表的なものは次のとおりです。
| キャッシュの種類 | 主な目的 | 保存・再利用するもの |
|---|---|---|
| KVキャッシュ | トークン生成の高速化 | AttentionのKeyとValue |
| プロンプトキャッシュ | 入力処理の再計算削減 | 共通するプロンプト部分の処理結果 |
| Prefix Cache | 共通する先頭部分の再利用 | Prefixに対応するKVなど |
| レスポンスキャッシュ | LLMの再実行を避ける | 生成済みの回答 |
| Semantic Cache | 類似質問への回答再利用 | 意味的に近い質問と回答 |
このうち、LLMそのものの推論効率と特に深く関係するのがKVキャッシュです。
KVキャッシュとは
Attentionで計算したKeyとValueを保存する仕組み
KVキャッシュとは、TransformerのAttentionで計算されるKeyとValueを保存し、次のトークン生成で再利用する仕組みです。
TransformerのAttentionでは、各トークンから主に以下の3種類のベクトルを作ります。
- Query(Q)
- Key(K)
- Value(V)
そして、QueryとKeyの関係を計算し、どの情報をどの程度参照するかを決めます。
代表的なScaled Dot-Product Attentionは、次のように表されます。
Attention(Q, K, V) = softmax(QKᵀ / √dₖ) V
ここで重要なのが、自己回帰型LLMでは、過去トークンに対応するKeyとValueを後続の生成でも再利用できるという点です。
一度計算したKとVを保存しておけば、次のトークンを生成するたびに、過去トークンのK/Vを最初から計算し直す必要がありません。
これがKVキャッシュの基本的な仕組みです。
KVキャッシュを使わない場合
過去トークンの計算を繰り返す必要がある
例えば、LLMが次のように文章を生成するとします。
「私は」
↓
「私は今日」
↓
「私は今日学校」
↓
「私は今日学校に」
KVキャッシュを利用しない場合、新しいトークンを生成するたびに、それまでの系列をモデルに通し直し、過去トークンに対応するK/Vを再計算する必要があります。
文章が長くなるほど、同じ計算を何度も繰り返すことになります。
そのため、生成トークン数が増えるほど処理が非効率になりやすくなります。
KVキャッシュを使う場合
過去のK/Vを再利用できる
KVキャッシュを利用すると、すでに生成済みのトークンに対応するKeyとValueを保存できます。
例えば、
「私は今日学校に」
まで処理した時点で、そのトークン列に対応するK/Vがキャッシュされています。
次のトークンを生成するときは、過去のK/Vを再計算するのではなく、キャッシュから読み出して利用します。
一方で、新しく追加されたトークンについては、各Transformer層の計算やQ/K/Vの生成が必要です。
また、新しいQueryは、通常は過去に蓄積されたKeyやValueを参照してAttentionを計算します。
そのため、KVキャッシュを利用したからといって、過去トークンに関する処理が完全になくなるわけではありません。
正確には、
過去トークンのK/Vを再生成する処理を省略できる
という仕組みです。
なぜQueryではなくKeyとValueをキャッシュするのか
Queryは生成ステップごとに新しく必要になる
文章生成では、新しいトークンを生成するたびに、そのトークンに対応するQueryを計算します。
一方、過去トークンのKeyとValueは、一度計算してしまえば後続の生成でも利用できます。
そのため、過去のK/Vを保存しておき、新しいQueryからそれらを参照する形になります。
これが「KV Cache」と呼ばれる理由です。
LLM推論におけるPrefillとDecode
LLMの推論処理は、大きく「Prefill」と「Decode」の2段階に分けて考えると理解しやすくなります。
Prefillとは
Prefillは、最初に入力されたプロンプト全体を処理する段階です。
例えば、
「LLMのキャッシュについて詳しく教えてください。」
という文章を入力した場合、この入力トークン列をTransformerに通します。
この処理で、各層におけるKeyとValueなどが計算され、KVキャッシュとして保存されます。
一般的に、長いプロンプトほどPrefill処理に必要な計算量も増えます。
Decodeとは
Decodeは、Prefillの後に回答を1トークンずつ生成する段階です。
例えば、
「LLM」
↓
「LLMの」
↓
「LLMのキャッシュ」
というように、順番にトークンを追加していきます。
このとき、すでに処理済みのトークンのK/VはKVキャッシュから再利用できます。
そのため、過去トークンについて毎回すべての計算をやり直す必要がなくなります。
プロンプトキャッシュとは
共通する入力部分の処理結果を再利用する
プロンプトキャッシュとは、以前処理したプロンプトの全部または一部を再利用する仕組みです。
例えば、複数のリクエストで毎回、次のような長いシステムプロンプトを使うとします。
「あなたはカスタマーサポートAIです。
以下のルールに従って回答してください。
ルール1……
ルール2……
ルール3……」
この共通部分を毎回最初から処理すると、同じ計算が繰り返されます。
そこで、共通部分に対応する処理結果を保存し、次回以降のリクエストで再利用します。
これにより、入力処理のレイテンシや計算コストを削減できる場合があります。
Prefix Cacheとは
共通する先頭部分を再利用する
Prefix Cacheは、複数のリクエストで共通する先頭部分、つまりPrefixを再利用する仕組みです。
例えば、
「あなたは法律専門AIです。契約書について説明してください」
「あなたは法律専門AIです。著作権について説明してください」
「あなたは法律専門AIです。商標について説明してください」
という3つのリクエストがあるとします。
「あなたは法律専門AIです。」という部分は共通しています。
その共通部分に対応するKVなどをキャッシュしておけば、後続リクエストで再利用できます。
ただし、プロンプトキャッシュとPrefix Cacheの呼び方や実装上の区別は、LLMサービスや推論エンジンによって異なります。
そのため、両者を完全に別の技術として考えるより、
共通入力部分の計算結果を再利用する仕組みの一種
として理解した方が実態に近い場合があります。
レスポンスキャッシュとは
生成済みの回答そのものを保存する
レスポンスキャッシュは、LLMが生成した最終的な回答を保存し、同じ条件のリクエストが来たときに再利用する仕組みです。
例えば、
「日本の首都はどこですか?」
という質問に対して、
「日本の首都は東京です。」
という回答を生成したとします。
同じ質問が再び来た場合、LLMを再度実行せず、保存しておいた回答を返すことができます。
レスポンスキャッシュは、KVキャッシュのようなモデル内部の仕組みではなく、WebアプリケーションやAPIサーバーなどアプリケーション側で実装されることが一般的です。
Semantic Cacheとは
意味が似ている質問にも過去の回答を利用する
Semantic Cacheとは、完全に同じ文字列だけでなく、意味的に似ている質問に対しても過去の回答を再利用する仕組みです。
例えば、
「東京の人口は何人ですか?」
と、
「東京都には何人くらい住んでいますか?」
という質問は、表現は異なりますが意味は近いと考えられます。
Semantic Cacheでは、Embeddingモデルなどを利用して文章をベクトル化し、質問同士の意味的な類似度を計算します。
類似度が一定以上であれば、以前の回答を再利用します。
Semantic Cacheには誤判定のリスクもある
意味が似ていても、必ずしも同じ回答を返してよいとは限りません。
例えば、
「現在のWindows 11の最新バージョンは?」
と、
「2024年時点のWindows 11の最新バージョンは?」
は意味的にはかなり近くても、求める時点が異なります。
そのため、実際のSemantic Cacheでは、類似度だけでなく、日時、ユーザー属性、権限、データの更新日時なども考慮することがあります。
LLMでキャッシュを使うメリット
トークン生成を高速化できる
KVキャッシュの大きなメリットは、Decode処理を効率化できることです。
過去トークンのK/Vを再計算しなくてよいため、自己回帰生成の重複計算を大幅に削減できます。
特に長文生成では、KVキャッシュの有無が生成速度に大きく影響します。
不要な計算を削減できる
キャッシュを利用すると、同じプロンプトや同じトークン列に対する処理を何度も実行する必要がなくなります。
そのため、GPUが行う演算量を減らせます。
ただし、KVキャッシュを利用すると、保存したK/Vをメモリから読み出す処理が必要になります。
特にDecodeでは、計算性能よりGPUのメモリ帯域がボトルネックになることもあります。
そのため、
「キャッシュを使えばGPU負荷が単純にすべて軽くなる」
というより、
演算量を削減する代わりに、メモリ容量やメモリ帯域が重要になる
と理解する方が正確です。
レスポンス時間を短縮できる
LLMサービスでは、ユーザーが質問してから回答が表示されるまでの時間がUXに大きく影響します。
KVキャッシュ、プロンプトキャッシュ、Prefix Cacheなどを適切に利用すれば、不要な処理を削減できるため、レスポンス時間の短縮につながります。
特に、長いシステムプロンプトや会話履歴を扱うサービスでは効果が大きくなる場合があります。
運用コストを削減できる
LLMの推論にはGPUなどの計算資源が必要です。
キャッシュによって重複処理を減らせれば、同じ計算資源でより多くのリクエストを処理しやすくなります。
また、LLM APIによっては、キャッシュされた入力トークンに対して通常とは異なる料金体系が設定される場合もあります。
そのため、共通プロンプトをうまく設計することで、APIコストを抑えられるケースもあります。
スループット向上につながる
キャッシュによって不要な計算を減らすと、単位時間あたりに処理できるトークン数やリクエスト数を増やせる可能性があります。
ただし、必ずスループットが向上するとは限りません。
KVキャッシュが大きくなると、GPUメモリを圧迫し、同時に処理できるリクエスト数が減る場合もあります。
そのため、実際のLLMサーバーでは、計算量だけでなく、KVキャッシュ容量と同時実行数のバランスも重要です。
LLMキャッシュのデメリット
KVキャッシュがGPUメモリを消費する
KVキャッシュの代表的なデメリットは、メモリ使用量が増えることです。
KVキャッシュの大きさは、主に以下の要素によって変わります。
- コンテキスト長
- Transformerのレイヤー数
- KVヘッド数
- ヘッド次元
- データ型
- バッチサイズ
- 同時セッション数
特に長いコンテキストを扱う場合、KVキャッシュが大量のGPUメモリを消費することがあります。
そのため、LLM推論ではモデル本体のパラメータだけでなく、KVキャッシュ用のメモリも考慮する必要があります。
コンテキストが長くなるほど容量が増えやすい
一般的なフルAttentionモデルでは、保存するトークン数が増えるほどKVキャッシュも大きくなります。
そのため、数千トークンより数十万トークンを扱う方が、より多くのメモリが必要になります。
ただし、すべてのモデルで無制限に増え続けるわけではありません。
Sliding Window Attentionのように、一定範囲の過去トークンだけを保持する仕組みでは、キャッシュサイズに上限を設けられる場合があります。
キャッシュ管理が複雑になる
大規模LLMサービスでは、多数のユーザーが同時に生成処理を行います。
ユーザーごとに入力長や出力長が異なるため、KVキャッシュのサイズも異なります。
これによって、GPUメモリが細かく分割され、効率が悪化することがあります。
この問題を改善する代表的な技術の1つがPagedAttentionです。
PagedAttentionとは
KVキャッシュのメモリ管理を効率化する
PagedAttentionは、KVキャッシュをページ単位で管理することで、GPUメモリの利用効率を改善する技術です。
従来の単純なメモリ確保では、生成長を見越して余分な領域を確保したり、メモリ断片化が発生したりすることがあります。
PagedAttentionでは、必要な容量を小さなページ単位で柔軟に割り当てます。
そのため、
KVキャッシュそのものを小さくする技術というより、KVキャッシュを効率的に配置・管理する技術
と考えると分かりやすいでしょう。
KVキャッシュ量子化とは
低ビット化してメモリ使用量を減らす
KVキャッシュ量子化とは、KeyとValueを低精度なデータ型で保存し、メモリ使用量を削減する方法です。
例えば、より低いビット数でK/Vを保持できれば、長いコンテキストでも必要なメモリ容量を抑えられます。
一方で、量子化や逆量子化に追加処理が必要になるため、必ずしも生成速度が向上するとは限りません。
つまり、
メモリ容量を削減できる代わりに、処理時間とのトレードオフが発生する可能性がある
という点に注意が必要です。
MQA・GQAとKVキャッシュの関係
KVヘッド数を減らしてメモリを削減できる
LLMでは、Attention構造そのものを工夫することでKVキャッシュ量を抑える方法もあります。
代表的なのが、Multi-Query Attention(MQA)とGrouped-Query Attention(GQA)です。
MQAとは
MQAでは、複数のQueryヘッドが同じKeyとValueを共有します。
通常のMulti-Head AttentionよりK/Vヘッド数を大幅に減らせるため、KVキャッシュ容量を削減できます。
GQAとは
GQAでは、複数のQueryヘッドをグループ化し、グループごとにKeyとValueを共有します。
MHAとMQAの中間的な構造と考えることができます。
GQAもKVヘッド数を減らせるため、KVキャッシュのメモリ効率を改善できます。
RAGでもキャッシュは重要
LLM以外の処理もキャッシュできる
RAG(Retrieval-Augmented Generation)では、一般的に次のような処理を行います。
ユーザーの質問
↓
Embedding生成
↓
ベクトル検索
↓
関連文書の取得
↓
プロンプト作成
↓
LLMによる回答生成
この各段階でキャッシュを活用できます。
例えば、
- 質問Embedding
- 検索結果
- 取得文書
- 共通プロンプト
- LLM回答
などをキャッシュできます。
なお、文書側のEmbeddingは、毎回計算してキャッシュするのではなく、あらかじめ計算してベクトルデータベースへ保存しておく設計が一般的です。
RAGでは、LLM部分だけでなく、システム全体のどこにキャッシュを配置するかがパフォーマンスやコストに大きく影響します。
キャッシュとAIの記憶は別物
KVキャッシュは長期記憶ではない
LLMのキャッシュについて理解するとき、注意したいのが「AIの記憶」との違いです。
KVキャッシュは、主に推論処理を効率化するために一時的な計算状態を保存する仕組みです。
そのため、KVキャッシュに情報が保存されたからといって、LLMが新しい知識を学習したわけではありません。
モデルパラメータそのものが更新されるわけでもありません。
チャットAIが過去の会話を参照できる場合には、
- 過去の会話履歴を再度プロンプトへ入力する
- 外部データベースから情報を取得する
- 専用のメモリ機能を利用する
といった別の仕組みが使われることがあります。
したがって、
キャッシュ=AIの長期記憶
と考えるのは正確ではありません。
LLMのキャッシュを簡単に整理すると
LLMのキャッシュとは、
以前に計算した結果や生成した情報を保存し、同じ処理を繰り返さないようにする仕組み
です。
特にLLM本体の推論では、過去トークンのKeyとValueを保存するKVキャッシュが重要です。
KVキャッシュを利用すると、生成済みトークンのK/Vを毎回再計算する必要がなくなるため、自己回帰生成を大幅に効率化できます。
一方、LLMアプリケーション全体では、プロンプトキャッシュやレスポンスキャッシュ、Semantic Cacheなども活用できます。
それぞれの役割を簡単に整理すると、以下のようになります。
KVキャッシュ
生成中に過去トークンのK/V再計算を省く
プロンプトキャッシュ・Prefix Cache
複数リクエストで共通する入力部分の計算結果を再利用する
レスポンスキャッシュ
同じ条件のリクエストではLLMによる生成そのものを省略する
Semantic Cache
意味的に類似したリクエストに過去の回答を再利用する
LLMの高速化や低コスト化を考えるうえでは、単純に高性能なGPUを利用するだけでなく、こうしたキャッシュをどのように設計・管理するかも非常に重要です。
特に長いコンテキストや多数の同時ユーザーを扱うLLMサービスでは、KVキャッシュのサイズ、GPUメモリ容量、メモリ帯域、同時実行数などを総合的に考える必要があります。
以上、LLMのキャッシュとは何か、仕組みやメリットについてでした。
最後までお読みいただき、ありがとうございました。
