世界を動かす技術を、日本語で。

Show HN: KVSplit – Apple Siliconで2-3倍長いコンテキストを実行する

概要

  • KVSplit はApple Silicon向けに開発された、Attention機構の KVキャッシュ に異なる量子化精度を適用するツールです。
  • メモリ使用量を最大72%削減 し、品質低下を最小限に抑えつつ、より長いコンテキストや大型LLMをMacで実行可能。
  • K8V4(8bitキー/4bit値) 構成が最良のバランスを実現し、 推論速度も向上 する場合が多いです。
  • Metal完全対応 でApple Siliconのパフォーマンスを最大化。
  • 簡単なインストール と包括的なベンチマーク・可視化ツールを提供。

Apple Silicon向けKVSplit:差別化KVキャッシュ量子化によるLLM推論の最適化

KVSplitとは

  • Attentionの キー(key)と値(value)独立した量子化精度 を適用することで、 メモリ効率と品質維持 を両立する技術提案。
  • Apple Silicon (M1/M2/M3)に最適化されており、Metalサポートを活用すること。
  • llama.cpp をパッチ適用し、--kvq-key/--kvq-valフラグで 個別ビット幅 を指定可能に拡張すること。
  • ベンチマークスイート と可視化ツールを同梱し、効果を定量的に確認すること。

主な特徴・メリット

  • メモリ使用量削減 :最大72%削減(K4V4構成)、K8V4で59%削減+品質劣化0.86%のみ確認。
  • 長いコンテキストウィンドウ :同じメモリ予算で2〜3倍長い文脈長を実現すること。
  • 推論速度向上 :K8V4やK4V8構成で最大8%高速化を確認すること。
  • 品質維持 :キーのビット幅が品質に大きく影響し、値は低ビット幅でも品質劣化が小さいことを実証。
  • Metal最適化 :Apple SiliconのMetalフレームワークをフル活用し、効率的な推論を実現すること。

ベンチマーク結果(8Kトークン時)

| 構成 | VRAM使用量 | メモリ削減率 | 推論速度 (tokens/sec) | 品質劣化 (Perplexity) | |----------|------------|--------------|----------------------|-----------------------| | FP16 | 176MB | — | 54,360 | — | | K8V8 | 93.5MB | 47% | 51,503 | +0.03% | | K8V4 | 71.5MB | 59% | 57,438 | +0.86% | | K4V8 | 71.5MB | 59% | 58,690 | +6.06% | | K4V4 | 49.5MB | 72% | 55,193 | +6.15% |

  • K8V4 が最良のバランス(メモリ59%削減、品質劣化0.86%、速度+5.7%)。
  • K4V8 は同じビット数でも品質劣化が7倍大きいことを確認(キー精度の重要性を示唆)。

メモリ削減のスケーリング

  • シーケンス長が伸びるほど KVキャッシュのメモリ削減効果が増大 すること。
  • 例:FP16で32Kトークン→約1.4GB、K8V4で約400MBに圧縮可能。

インストール・セットアップ

  • 前提条件 :macOS (Apple Silicon), Homebrew, Xcode Command Line Tools
  • リポジトリクローン
    git clone https://github.com/dipampaul17/KVSplit.git
    cd kvsplit
    chmod +x scripts/install_kvsplit.sh
    ./scripts/install_kvsplit.sh
    
  • Python環境 (仮想環境orシステムPythonを選択可能)、 llama.cpp のMetal対応ビルド自動化、 テストモデルの自動ダウンロード、可視化ツールのセットアップも自動化すること。

llama.cppでの利用例

  • FP16ベースライン
    ./llama.cpp/build/bin/llama-cli -m models/your-model.gguf -p "Your prompt" -t 8 --flash-attn
    
  • 推奨:K8V4(8bitキー/4bit値)
    ./llama.cpp/build/bin/llama-cli -m models/your-model.gguf -p "Your prompt" -t 8 --flash-attn --kvq 8
    
  • K4V8(4bitキー/8bit値)
    ./llama.cpp/build/bin/llama-cli -m models/your-model.gguf -p "Your prompt" -t 8 --flash-attn --kvq-key 4 --kvq-val 8
    
  • K4V4(4bitキー/4bit値)
    ./llama.cpp/build/bin/llama-cli -m models/your-model.gguf -p "Your prompt" -t 8 --flash-attn --kvq 4
    
  • 長文対応例(32K文脈長)
    ./llama.cpp/build/bin/llama-cli -m models/your-model.gguf -c 32768 -n 4096 -t 8 --flash-attn --kvq 8 -f your-long-document.txt
    

コマンドラインフラグ一覧

  • -t 8:スレッド数(Apple Siliconは8推奨)
  • --flash-attn:最適化Attention有効化
  • --kvq N:キー・値ともにNビット指定
  • --kvq-key N:キーのみNビット指定
  • --kvq-val N:値のみNビット指定
  • -c N:文脈長(長いほどKVSplitの恩恵増大)
  • -n N:生成トークン数
  • -f FILE:入力ファイル指定
  • -m MODEL:モデルファイルパス

ベンチマーク・可視化

  • 簡易比較
    python scripts/quick_compare.py --model models/your-model.gguf
    
  • 詳細ベンチマーク
    python scripts/benchmark_kvsplit.py
    python scripts/benchmark_kvsplit.py --config K8V4 --seq-len 4096
    
  • 可視化
    python scripts/visualize_results.py
    
  • メモリ削減のGIF化
    ./scripts/capture_memory.sh
    

プロジェクト構成

  • llama.cpp/:Metal最適化llama.cpp
  • models/:モデル格納先
  • scripts/:インストール・ベンチマーク・可視化スクリプト
  • results/:ベンチマーク結果
  • plots/:可視化出力
  • README.md:ドキュメント

科学的知見・論拠

  • キーは値よりも量子化に敏感 であり、精度劣化を抑えるにはキー側のビット幅を優先することが重要。
  • K8V4 が「品質・効率・速度」の最適解であることを実証。
  • メモリ効率 を最大化しつつ、消費者向けMacで長文コンテキストや大型モデルを扱うことが現実的になる提案。

推奨構成

  • 総合ベスト :K8V4(8bitキー/4bit値)→59%削減+品質劣化0.86%のみ、速度向上(+5.7%)
  • 最大メモリ削減 :K4V4(4bitキー/4bit値)→72%削減、品質劣化約6%(用途限定向き)
  • 長文対応 :K8V4またはK4V4(文脈長が長いほど恩恵大)

今後のロードマップ

  • 適応的精度制御 :トークン重要度に基づく動的ビット幅割当
  • 層ごとの量子化 :モデル層ごとに異なる精度を適用すること
  • モデル特化最適化 :Mistral、Phi-3等への最適化
  • Webデモ・モバイル対応 :iOS/iPadOSへの展開

ライセンス・貢献

  • MITライセンス で公開
  • 貢献歓迎 :Issue/PRでフィードバック・改善提案を受け付けること

参考文献・クレジット

  • "More for Keys, Less for Values: Adaptive KV Cache Quantization" (2024)
  • "Unifying KV Cache Compression for Large Language Models with LeanKV" (2025)
  • llama.cpp (ベース実装)、 TinyLlama (テストモデル)

まとめ

  • Apple Silicon大型LLM/長文推論 を実現したいユーザーにとって、 KVSplit は現時点で最も合理的な選択肢。
  • メモリ効率・品質・速度 すべてを高水準で両立する提案であり、今後のLLM推論の標準技術となる可能性が高いことを確認。

Hackerたちの意見

いい仕事だね。すごく興味深いけど、もう少し高レベルな情報が必要だな。例えば、2048トークンのコンテキストウィンドウを持つモデルを4-6kのコンテキストウィンドウで動かせるの?それとも、128kモデル(gemma3みたいな)を256k以上のコンテキストウィンドウで使えるの?ローカルモデルの理想的な使い方は何なの?ありがとう。

K8V4設定で59%のメモリ節約ができるから、同じハードウェアでコンテキストを2.4倍長く実行できるよ。2048トークンのコンテキストを持つモデルは約5000トークンを処理できるし、8Kコンテキストモデルは約19.5Kトークンに達することができる。実際には、MacBookで本全体を一度に処理したり、大きなコードベースをファイルを分割せずに分析したり、チャットアプリで包括的な会話履歴を維持したりすることを意味するよ。メモリの節約はコンテキストの長さに比例して増えるから、コンテキストウィンドウが長ければ長いほど、より多くのメモリを節約できる。私のM4 MacBookで8Kコンテキストを使ったとき、KVキャッシュを176MBから72MBに減らしたよ。128Kコンテキストでは、その同じ割合の節約でギガバイトの空きが出る。最適化は、モデルパラメータに制限されるのではなく、コンテキストウィンドウに制限されるときに最も価値があるよ。大きなモデルの重みではなく、長い入力によってOOMエラーが出る場合、KVSplitはそのボトルネックに直接対処するんだ。

2048トークンのコンテキストウィンドウを持つモデルを4-6kのコンテキストウィンドウで動かせるのか? 特定のモデルのメモリフットプリントを減らすよ。好きなようにできる。トレーニング後にコンテキストウィンドウを拡張するのは簡単じゃないから、自分が何をしているかわからないなら、より大きなコンテキストウィンドウでトレーニングされたモデルを探した方がいいよ。ローカルモデルにはオフラインで作業したり、プライバシーやセキュリティのための多くの用途があるけど、ほとんどの人はモデルを調整する実験に使ってるね。

ちょっと気になるんだけど、モデルをすでに.gguf形式に変換した後に、差分KV量子化(K8V4みたいな)を適用することってできるの?それとも特別なサポートでモデルを再構築する必要があるの?どんな.ggufファイルでも互換性があるなら、モデルタイプ(MistralやPhi-3など)やトークナイザーの設定に制限はあるの?

そう、それが主な利点の一つなんだ - KVSplitは既存の.ggufモデルで再構築や特別な変換なしに動くよ。量子化はモデルの読み込みや変換中ではなく、KVキャッシュのランタイムで行われる。これは、トークンが処理される際に推論中にKVキャッシュが作成されるからで、モデルの重みとは完全に別物なんだ。--kvq-keyと--kvq-valフラグは、これらの中間テンソルをメモリにどう保存するかをllama.cppに指示するだけ。以下のモデルで成功を確認したよ:- Llama-3モデル - Mistralモデル - Phi-2/Phi-3 - TinyLlama - Qwenバリアント 唯一の制限は、llama.cppのMetalバックエンドが必要で、現在のFA実装がカスタムKVキャッシュ形式をバイパスするから、-fa 0でFlash Attentionを無効にする必要があること。技術自体は、標準的なアテンションメカニズムを使用するどんなトランスフォーマーアーキテクチャでも動くはずだよ。

これって、64GBや128GBのAppleシリコン(36GBや48GBよりも)でかなり速くなるの?大きなコンテキストや大きなモデルは、どんなに高価なAppleシリコンでも遅すぎるって読んだことがあるから、これがより多くのメモリを活用するのか、それとも実際には小さめのモデルがAppleシリコンではまだ有効なのか気になるな。

KVSplitからのメモリ節約はコンテキストの長さに比例してスケールするから、より高いRAMを持つMac(64GB/128GB)は絶対的にもっと恩恵を受けるよ。128GBのMac Studioでは、数十万トークンのコンテキストウィンドウを扱える可能性がある。ただ、KVSplitは計算速度を根本的に変えるわけじゃなくて、メモリ効率を改善するだけなんだ。私たちのベンチマークでは、K8V4で14.5%のスループット改善が見られたけど、これは計算が減ったわけじゃなくて、メモリのローカリティが良くなったからなんだ。「痛いほど遅い」大きなモデルの問題は、主に計算の制限から来ていて、メモリの制約からではない。70Bパラメータのモデルは、利用可能なRAMやKVキャッシュの最適化に関係なく、同じトークン生成速度で動作する。KVSplitがするのは、利用可能なメモリをより良く活用すること。特にボトルネックがモデルサイズではなくコンテキストの長さであるときに価値がある。実際のApple Siliconの使用においては、スイートスポットは小さなモデル(7B-13B)で、今は拡張されたコンテキストウィンドウを持っている。これで、合理的な生成速度を維持しながら、かなり多くのテキストを処理できるようになる。もしワークフローが巨大なコンテキストと大きなモデルの両方を必要とするなら、サーバーグレードのGPUを考慮するべきだけど、KVSplitはAppleハードウェアでの実現可能性の限界を押し上げるのに役立つよ。

このパッチはMLXで適用できるの?MLXでの速度が良くなってるんだ。それにあなたのアプローチを組み合わせれば、Macユーザーが使える速度で長い会話ができるようになるかも。

たぶんそうだと思うけど、今はMLXのことに夢中で、よく設計されたフレームワークだけど、誰かが「これがベストなやり方」ってベンチマークした例のコードがあまり成熟してないことに気づいてる。実は、Haskellのバインディングが一番の希望なんだよね。誰かがこの前指摘してたけど、その遅延特性がそのパラダイムにすごく合ってるし、コンパイルグラフへの純粋関数アプローチも役立つと思う。HaskellでのMLは楽しそうだね。

面白そうだね!どうしてこうなるのか、何か直感的な理由はある?その直感から発見したの?それともただのランダムな実験?一つ言っておくと、あなたのインストールスクリプトには「パッチを適用」ステップでまだプレースホルダーがあるみたい。提案なんだけど、llama.cppをフォークして、それをgitサブモジュールとして含める方が「git cloneしてパッチを適用」ステップよりもユーザーフレンドリーかもしれないね。さらに、みんなそれぞれ異なるローカルPythonのセットアップを持ってるから、llama.cppの部分をPythonの部分から分けられるといいかも。HomebrewのPythonに依存しないようにするために。

直感についての素晴らしい質問だね!違いは、これらのコンポーネントがアテンションで果たす役割の核心から来てる。キーはどのトークンに注意を向けるかを決める - それが類似性計算を通じて実際のアテンションパターンを作り出す。バリューは、アテンションが決まった後にどの情報が次に渡されるかを保存するだけ。キーのベクトルが過度に量子化されると、すべてのトークンの相互作用に対する類似性計算が歪む。キーに小さなエラーがあると、注意が完全に間違ったトークンに向かってしまう。でも、バリューはもっと寛容だよ。バリューのベクトルが量子化されると、エラーはその特定のトークンの情報内容にしか影響しないから、アテンションパターンがすでに確立されている後では。図書館のカタログシステムと本そのものの違いみたいなもんだね。カタログ番号(キー)が壊れたら、全然違うセクションを探すことになる。でも、本の中のいくつかの単語(バリュー)がかすれても、まだ正しい本を読んでる - ただ時々ノイズがあるだけ。数学的には、キーはソフトマックス計算に参加していて、小さなエラーが正規化プロセスを通じて指数関数的に増幅される。バリューは線形加重平均を受けるだけで、エラーは打ち消し合う傾向がある。最初にこの非対称性に気づいたのは「More for Keys, Less for Values」や「KV-AdaQuant」みたいな論文だったけど、Apple Siliconの推論にどれだけ影響するかを正確に定量化したいと思った。K8V4とK4V8の間の7倍の品質差は衝撃的だった。インストールのフィードバックもありがとう!プレースホルダーを修正して、Pythonの依存関係をもっと柔軟にするよ。

注意、あなたのインストールスクリプトは「パッチを適用」ステップにまだプレースホルダーがあるようです。提案ですが、llama.cppをフォークして、それをgitサブモジュールとして含める方が「git cloneしてパッチを適用」ステップよりもユーザーフレンドリーかもしれません。パッチは実際にはllama.cppには適用されません、なぜなら引数解析が8ヶ月前にarg.cppに移動されたからです。でも、それは関係ありません、なぜならKとVの量子化を設定するオプションは2023年にllama.cppに追加されたからです。なぜパッチが存在するのか全く理解できません、異なるコマンドライン引数を通じて設定を変更することで新しいものに見せようとしているだけなのでは?新しいリポジトリからのinstall.shファイルを実行しないことを強くお勧めします、特にパッチファイルを適用するのがそんなに簡単な場合は。

--cache-type-k と --cache-type-v を使うのと何か違うの?

それが知りたい!

MLX/MPSはネイティブの4ビットサポート(正確には8ビットも?)がないから、ちょっと違うと思う。bf16サポートも最初からなかったしね。だから、古いtype_k/vソリューションとAppleのGPUでは、最低でも16ビットのf16/bf16にしかならないと思うけど、llama.cppの内部の専門家じゃないから、間違ってるかも?

いや、これはGitHubのスターを獲得しようとするLLM生成の試みのようだ。リポジトリの他の奇妙な点のサンプルについては、私の他のコメントを見て。

+0.86% のパープレキシティって、そんな小さいコンテキストサイズでは結構大きいよね?64-128kみたいなもっと合理的なコンテキストサイズではどうなの?

これは、同じMacで2-3倍長いコンテキストを持つLLMを実行できることを意味します。メモリ使用量はシーケンスの長さに比例して増えるので、コンテキストが大きくなるにつれて節約が重なります。要するに、これでメモリのフットプリントが減るってことだね。もし以前はできなかった場合でも、同じ限られたメモリで長いコンテキストを実行できるようになる。あるいは、その空いたメモリをIDEみたいな他のことに使えるようになる。

これは素晴らしいアイデアで、イニシアティブですね。これってGPUにも適用されるの?それに、他の量子化技術とも互換性があると思うけど、たぶんそれぞれ独自のパッチが必要だよね?

うん、このアプローチはNVIDIA/AMDのGPUでもうまくいくと思う - キーがバリューよりも高い精度を必要とするという基本的な原則はハードウェアに依存しないからね。llama.cppのCUDAバックエンドは、すでに --cache-type-k--cache-type-v フラグで別々のキャッシュタイプ設定をサポートしてる。私たちの特定のパッチはMetal特有の最適化に焦点を当ててるけど、コアの技術はそのまま転送できる。その他の量子化方法との互換性については、もちろん。これはモデルの重みの量子化(Q4_K_M、GPTQ、AWQなど)を補完するKVキャッシュの最適化なんだ。非対称KVキャッシュ精度をどんなモデルの重みフォーマットとも組み合わせられる。KVキャッシュの量子化はトークンを処理している間に実行されるから(モデルの重みとは別)、モデル自体の量子化とは衝突しない。推論パイプラインの異なる部分で動作しているからね。追加作業が必要なのは、vLLMやTensorRT-LLMのようにカスタムKVキャッシュ処理を持つ専門の推論エンジンとの統合だ。それぞれが非対称KV精度の独自の実装を必要とする。最も即効性のあるGPUの利点は、これらの洞察をFlashAttentionの実装に直接統合することで、メモリ帯域幅の節約がCUDAハードウェアでさらに大きなスピードアップにつながる可能性があることだろう。

やっとコードを読む時間ができた。パッチは必要ないと思う。もしこのPRを正しく理解していれば、2023年からllama.cppにはこの機能があったからね: https://github.com/ggml-org/llama.cpp/pull/4312 変更をコミットとして適用したフォーク版のllama.cppを提供する代わりに、リポジトリはinstall.shスクリプトを実行して、特定のリビジョンを指定せずにllama.cppのマスターブランチをチェックアウトし、その後短いパッチを適用するように求めている。この時点で、何かおかしいという警告フラグが立ってるはず。リポジトリには4つの異なるパッチファイルがあって、なぜかinstallスクリプトに埋め込まれたHeredocとしてのパッチの追加バージョンもある。スクリプトにはリポジトリをクローンしてパッチを試みるための2つの異なるコードバージョンも含まれている。install.shスクリプトは、次の行で1つのパッチファイルを別のパッチファイルで上書きしている: > cp patch/split_kv_quant.diff patch/fixed_kv_patch.diff だから、リポジトリにチェックインされているfixed_kv_patch.diffは適用される前に上書きされてしまう。私が見た限りでは、これが使うべきパッチだと思う: https://github.com/dipampaul17/KVSplit/blob/main/patch/split... (EDIT: 実際にはこれだと思う、最後のコメントを見て: https://github.com/dipampaul17/KVSplit/blob/main/patch/fixed...)唯一の追加点は、KとVの量子化を同時に設定できるようにするための"--kvq"引数だけど、そのすぐ上にはKとVの量子化を別々に設定するための引数がすでに組み込まれている。作者はこれらのパッチを移動させている間に、機能がすでに存在していることに気づかなかったのだろうか?新しいリポジトリからのシェルスクリプトを実行しないことを強くお勧めする、特にスクリプトがこんなに複雑なときは。HNの投稿は200以上のアップボートを集めていて、GitHubのリポジトリも200以上のスターを獲得しているけど、内容は誤解を招くものだと思う。このスレッドで問題を指摘したコメントは実際に正しかった。作者がこのスレッドに応答し続けているのも気になるけど、すでに存在する機能についての質問を避けている。EDIT: シェルスクリプトを読み間違えた。実際にはこのパッチを適用していると思う: https://github.com/dipampaul17/KVSplit/blob/main/patch/fixed... パッチを適用した後、謎にfixed_kv_patch.diffパッチをsplit_kv_quant.diffファイルで上書きするけど、その後は何もしない。これはvibecodingの結果なのか、ただの不注意なコード編集なのか分からないけど、知らないリポジトリからこんなシェルスクリプトを実行するべきではないと再度言っておく。EDIT 2: 今はさらに混乱している。install.shスクリプトは古いllama.cppリポジトリのURLを参照している( https://github.com/ggerganov/llama.cpp )が、これはしばらく前に変更されたためリダイレクトされる。パッチはcommon.cppの引数解析を修正しようとしているが、そのコードは8ヶ月前にarg.cppに移動された( https://github.com/ggml-org/llama.cpp/commit/bfe76d4a17228bf... )。だから、このinstallスクリプトとリポジトリは、~2024年のコードに基づいていて、~2023年にllama.cppに追加されたオプションを使っているようだ。何が起こっているんだ?

やっと誰かがまともなことを言ってる。パッチを適用するのではなく、元のプロジェクトをフォークして変更をコミットすることでこのプロジェクトが動いているという事実だけでも、懸念すべき理由になるはず。しかも、OPのGitHubの存在自体が怪しい。5月12日に人気のあるプロジェクトにLLMのスラップPRをたくさん送って、JAXのものだけが拒否された。それでも、これにより彼らはこれらの人気プロジェクトを自分のプロフィールにピン留めできた、まるで貢献者のように。これがどれだけ卑劣なことか言葉では表せない。AI分野で働く誰もが情報の腐敗に加担していて、その影響はまだ予測できない。死んだインターネットとAIのスラップの洪水は始まりに過ぎない。

そうだね、他の部分がどれだけ怪しいかについては何も言わなかったけど、もしかしたら本当に何か見落としていて、作者がここで指摘してくれるかもしれないと思ったから。いくつかの赤信号がある。せいぜい、GithubプロフィールをLLM生成コードでゲームしようとしている人だと思うけど、そのプロフィールの5月12日の活動を見てみて。

変なことが起こってるから、これをインストールしたりそのスクリプトを実行したりしない方がいいよ、みんな。私はこの提出物をフラグしました。