ローカルモデルのカードはこぞって128kコンテキストを誇るようになった。デスクトップでは、あの数字はRAMが中身を読まずにサインしたリース契約だ。長い文書をどこまで押し込めるかを決めるのは重みではない——KVキャッシュだ。そしてそれは、他人の通貨で取り決めた税金のようにコンテキストに比例して膨らむ。
コンテキストウィンドウは切り替える機能ではない。サーバーが動いている1秒1秒に払い続ける、メモリのリースだ。
128kコンテキストは実際どれくらいRAMを使うのか?
算術は公開されていて、短い。Transformer Inference Arithmeticが解説するように、KVキャッシュはレイヤーごと、KVヘッドごと、トークンごとに2つのテンソル——keyとvalue——を保持する:
cache_bytes = 2 × レイヤー数 × コンテキスト × KVヘッド数 × head_dim × 要素あたりバイト数 Llama 3.1 8Bを見てみる:レイヤー32、KVヘッド8、head_dim 128——Hugging Faceのconfig.jsonからそのまま。fp16(2バイト)で131,072トークン:
2 × 32 × 131072 × 8 × 128 × 2 = 17,179,869,184 バイト ≈ 16 GiB 同じモデルのfp16の重みは約16 GiB。最大コンテキストでは、キャッシュはそれが属するモデルと同じ大きさになる。「16 GiBに収まる8Bモデル」は、num_ctxを128kに上げた瞬間、静かに32 GiBの問題に変わる。
キャッシュはなぜ窓と一緒に育つのか?
アテンションは各トークンをそれ以前の全トークンと比較しなければならず、推論は自己回帰的だ——何も再計算されず、すべて覚えている。生成を速くする仕掛けはそれだけだ:各トークンのkeyとvalueは一度だけ計算して保存される。トークンあたりの保管コストは一定、ゆえに総量はコンテキストに対して線形だ。128kは「大きい数字」ではない。1k窓のキャッシュの128倍を、セッションの間じゅう確保し続けるということだ。
Grouped-query attention(GQA)はモデル側の割引だ:KVヘッドが少なく、キャッシュが細い。Qwen2.5 7Bはレイヤー28に対してKVヘッドわずか4(configはこちら)。同じ128k窓でもfp16で約7 GiB——実質的な救済で、こうしたモデルが非力なマシンで好意的に感じられる理由のひとつだ。
| モデル (fp16) | レイヤー × KVヘッド | KVキャッシュ @ 128k | 重み | キャッシュ対重み |
|---|---|---|---|---|
| Llama 3.1 8B | 32 × 8 | 約16 GiB | 約16 GiB | 約100% |
| Qwen2.5 7B | 28 × 4 | 約7 GiB | 約15 GiB | 約47% |
自分で計算してみてほしい——検証はこれで全部だ:
def kv_cache_gib(layers, kv_heads, head_dim, ctx, bytes_per=2):
return 2 * layers * ctx * kv_heads * head_dim * bytes_per / 2**30
print(kv_cache_gib(32, 8, 128, 131_072)) # Llama 3.1 8B -> 16.0
print(kv_cache_gib(28, 4, 128, 131_072)) # Qwen2.5 7B -> 7.0 マーケティングの数字が黙っていること
この主張の正直な帳簿がここにある——曲がるところも含めて:
- prefillは2枚目の請求書だ。 最初の応答トークンの前に、プロンプト全体を一気に処理する。100kトークンのプロンプトなら、最初の1文字を見る前に100kトークンを噛まなければならない。3060ではミリ秒ではなく分だ。
- sliding-windowモデルはこの計算を壊す——あなたの有利に。 Mistral系アーキテクチャはレイヤーごとに固定窓へアテンションを制限するので、キャッシュは育ち続けない。単純な式はそれらを過大評価する。この稿はフルアテンションのtransformerの話で、つまり大半の人が動かしているものの話だ。
- KV量子化は現実だが部分的だ。 llama.cppはKVをq8_0で保持でき、キャッシュを約半分に削れる。品質リスクはある。16 GiBの半分でも8 GiBだ。
- スペック表はKVヘッド数を滅多に書かない。 config.jsonを掘る羽目になる——そのこと自体が、あの数字がどう読まれることを想定しているかを物語る。
以上のどれも、長いコンテキストを無用にするものではない。窓を埋め尽くすのが「4節を読め」と言う高価な方法だからこそ、RAGが存在する。ただ、メモリの請求書を印刷せずに「128k」だけ印刷するカードは、最高速度だけ売って燃料タンクに触れない車の売り方だ。
誰も128,000トークンを読まない。それでもRAMは全額払っている。
処方箋は退屈だ:ワークロードが実際に使う分だけコンテキストを設定すること。32k窓なら128kキャッシュの4分の1で済み、一人の開発者が本当に送るプロンプトはほぼ全覆盖できる。これはローカルLLMのRAM算術と同じ帳簿の規律だ。モデルサイズは予算の半分に過ぎず、量子化レベルは重みを細くするが、キャッシュの計算には触れない。
キャッシュがコンテキストの本当の価格なら、なぜカードは窓を売って請求書を売らないのか?そして告白タイム:最後のトークンまで実際に埋めた最大のコンテキストは?その出力はギガバイトに見合ったか?コメントは開いている。シリーズ次回は、負けた側の立場を取る。
FAQ
128kコンテキストウィンドウはどれくらいRAMを使う?
Llama 3.1 8Bのfp16では、KVキャッシュだけで131,072トークン時点でおよそ16 GiB——モデルの重みとほぼ同等。式:2 × レイヤー数 × コンテキスト × KVヘッド数 × head_dim × バイト数。
長いコンテキストはなぜ多くのメモリを使うのか?
キャッシュ上の各トークンは、アテンション層ごとにkeyとvalueのテンソルを保持する。コンテキストを倍にすればキャッシュも倍。窓を埋めなくても確保自体は存在する。
ローカルLLMでKVキャッシュのメモリを減らすには?
実際に使う分だけコンテキストを設定し、grouped-query attentionのモデル(KVヘッドが少ない)を選び、ランタイムが対応していればKVキャッシュをq8に量子化するか、sliding-windowやハイブリッド構成を使う。
— mrsaynothing
— mrsaynothing
意見は公開前にロードテスト済み。たぶん。
次の主張をメールで受け取る
投稿ごとに1通。同意も、解体も自由。
これは何?git stash:1ファイルだけ退避させ、他はそのままにする
記事を楽しんでいただけましたか?私の本業はこのような構築です。 採用のご相談