KV Cache量子化とは?FP8でLLMの同時実行数を増やす前に測ること
LLMのmodel weightがGPUへ収まっても、長いcontextと同時requestが増えるとKV Cacheが先に限界へ達することがある。KV Cache量子化は、保存するKeyとValueのprecisionを下げ、同じGPU memoryへより多くのtokenを保持する方法である。
たとえばFP16やBF16のKVをFP8へ変えれば、要素当たりの保存量は16 bitから8 bitになる。理論上のKV領域は約半分になるが、同時実行数や速度が必ず2倍になるわけではない。scale、metadata、kernel、modelの感度、request長の分布まで含めて測る必要がある。
weight量子化とは別の場所を小さくする
weight量子化はmodel parameterをINT8、INT4、FP8などへ変換する。KV Cache量子化は、requestのPrefillとDecodeで生成されるKeyとValueを低precisionで保存する。
weightはmodel読込後にほぼ一定だが、KV Cacheは次の条件で増減する。
- layer数
- KV head数とhead dimension
- requestごとの保持token数
- 同時に実行するsequence数
- KeyとValueの二つ
- Cacheのdata type
概算は次の形で考えられる。
[ M_{KV} \approx 2 \times L \times H_{KV} \times D \times T \times B \times S ]
Lはlayer数、H_KVはKV head数、Dはhead dimension、Tは保持token数、Bは同時sequence数、Sは1要素のbyte数である。先頭の2はKeyとValueを表す。実際にはblock単位の余り、alignment、scaleなどのmetadataが加わる。
Grouped Query AttentionやMulti Query AttentionではKV head数が少ないため、同じcontext長でもCache量が小さい。model名とparameter数だけで削減効果を推測しない。
FP8は値とscaleを組み合わせる
低precisionへ変換すると表現できる値の範囲と細かさが減る。そのため元の値をscaleで調整して量子化し、Attention計算時にそのscaleを使う。
vLLMのQuantized KV Cacheは、FP8についてper-tensorとper-attention-headのscale方式を説明している。
- per-tensorはQ、K、Vごとに一つのscaleを使う
- per-attention-headはheadごとにscaleを持つ
粒度を細かくすれば分布の違いへ合わせやすいが、scaleの保存量と処理が増える。KIVI論文はKV分布を分析し、Keyはchannel単位、Valueはtoken単位で量子化する非対称な2 bit方式を提案した。KeyとValueを同じ粒度で扱うことが常に最適とは限らない。
default scaleのまま本番へ出さない
量子化scaleの決め方には、固定値、実行時のdynamic計算、calibration datasetからの事前推定などがある。
vLLMではFP8 KV Cacheをscale 1.0で始める方法もあるが、公式資料はdatasetを使ったcalibrationを推奨している。scaleが実際の分布へ合わなければ、値が表現範囲を超えてclippingされるか、使えるbitを十分に活用できない。
calibration dataは一般的な会話だけで選ばない。本番にある長文、コード、日本語、表、RAGの引用、tool結果、system promptを含める。最大値だけを追うと少数のoutlierがscaleを広げ、通常値の解像度が粗くなる場合もある。
model、revision、KV dtype、scale、attention backendを一組のartifactとして管理する。modelだけ更新して古いscaleを流用しない。
memory削減と高速化は同じではない
KV CacheをFP8へすれば、同じmemory予算で保持できるtoken数は増える。これによりwaiting requestをrunningへ移し、batchを大きくできればthroughputが上がる可能性がある。
一方、1 requestだけを処理する場合は速くならないこともある。保存時のquantize、読込時のdequantize、scaleの取得に処理が必要だからである。低precisionのままAttentionを実行できる対応kernelならdata movementを減らせるが、毎stepで高precisionへ戻す実装では変換costが残る。
確認するのは「FP8を選べるか」だけではない。
- 使用するGPUとattention backendがその形式を高速に処理できるか
- quantizeとdequantizeが融合されたkernelか
- Prefix CachingやPagedAttentionと同じblock layoutを使えるか
- CUDA graphやcompile済みshapeが無効にならないか
- tensor parallel間でscaleとlayoutが一致するか
memory使用率が下がっても、token間latencyが悪化すれば対話用途では逆効果になりうる。
品質はperplexityだけで判断しない
KV Cache量子化の誤差は、各Decode stepでAttentionが参照する過去情報へ入る。短い回答の平均accuracyが維持されても、長いcontextの特定位置を取り出す能力が落ちる可能性がある。
少なくとも次を比較する。
- 短文QAと通常会話
- 長文内の一箇所を探すretrieval
- 複数資料をまたぐ質問
- 長いコードの依存関係
- JSONやtool callの形式遵守
- RAGで引用した数値と固有名詞
- 長い生成での反復、脱線、途中の条件忘れ
- safetyと拒否挙動
評価は同じprompt、sampling parameter、seedを使い、native dtypeを基準にする。perplexity、task score、長文retrieval、形式違反率、人手評価を分ける。平均値だけでなくcontext長ごとの劣化を見る。
すべてのlayerを量子化しない選択肢
layerやattention typeによって誤差への感度は異なる。vLLMには特定layerやsliding-window attentionをKV Cache量子化から除外する設定がある。
品質問題が出たとき、すぐ全体をnative dtypeへ戻す必要はない。感度の高いlayerだけ高precisionに残すmixed構成を試せる。ただし、異なるpage sizeやlayoutが混ざるとcapacity計算とkernel選択が複雑になる。
除外layerを増やすたびに、実際の保存token数を測り直す。設定上FP8でも一部がBF16なら、単純な半減にはならない。
Prefix CacheとOffloadingへの影響
量子化はPrefix CachingやKV Cache Offloadingと併用できるが、Cache互換性を明示する必要がある。
同じtoken列でも、model revision、scale、quantization granularity、KV dtype、layoutが違えば同じblockとして再利用しない。rolling deploymentではnamespaceを分ける。
offloadでは転送byte数が減るため、CPUやSSDからGPUへ戻す時間も短くなる可能性がある。一方、保存先とGPU側で形式が違えば変換が必要になる。どの階層でquantizeし、どの階層でdequantizeするかを一つに決める。
監視するmetric
- KV Cacheの総byte数と保持可能token数
- running、waiting request数と実効concurrency
- block使用率、eviction、preemption、recompute
- Prefix Cacheのhit token数
- quantize、dequantize、Attentionの時間
- TTFT、ITL、E2E latency
- input・output token throughput
- context長別のtask scoreと形式違反率
- clippingやscale更新を観測できる場合はその頻度
- layer除外後の実効compression ratio
throughputの改善だけを見ず、同じ品質水準とlatency目標を満たすrequest数で比較する。
本番投入前の比較
native dtypeと量子化構成を同じtrafficで比較する。
- 短文中心、長文中心、混合の三つを用意する
- concurrencyを段階的に上げ、waitingが始まる点を測る
- Prefix Cacheをcoldとwarmに分ける
- context上限付近でretrieval品質を測る
- 同じGPU memory上限で最大安定concurrencyを比較する
- PagedAttentionのblock余りとpreemptionを確認する
- Offloading併用時の転送量とTTFTを測る
- model更新時にscale不一致を検知できるか確認する
- unsupported kernelへfallbackした場合の速度を測る
- canaryで品質指標とtail latencyを監視する
量子化後にOOMが減っても、長文回答の正確さやp99 ITLが悪化するなら、bit幅、scale粒度、除外layerを調整する。
KV Cache量子化はcapacityと品質の交換である
KV Cache量子化は、同じGPUへ多くのtokenを保持し、長いcontextや大きいbatchを扱う余地を作る。FP16やBF16からFP8への変更なら保存要素は半分になるが、system全体のmemory、throughput、latencyは単純に2倍改善しない。
重要なのは、本番dataに合うscaleを校正し、対応kernelを確認し、長文retrievalと形式遵守まで評価することである。weight量子化とは別のartifactとしてversion管理し、native dtypeへ戻せるcanary運用から始める。