画像エンコード結果の再利用(実験機能)

September 21, 2026 · View on GitHub

LFM2.5-VLの画像エンコーダー+projectorが出力したembeddingを、llama.cpp内部で再利用します。モデルへの画像・state・質問の入力処理(decoder prefill)は質問ごとに実行し、KVや再帰状態は共有しません。PNG/JPEGの読み込み・リサイズ等の前処理も質問ごとに行います。

公式b11042のソースに小さなパッチを適用した、別の実験用バイナリです。通常の./run.shや配布済みバイナリには変更を加えていません。このビルド手順はLinux/ROCm向けで、この環境ではR9700で確認しています。

このページのランチャーはLFM用です。 Sarashinaは同じLRU部品を使いますが、projectorと前処理の修正が必要なため専用のビルド・ランチャーへ分離しています。

起動

初回は下記「ビルドと再検証」の手順でビルドしてください。バイナリはリポジトリに含めません。同じポートのサーバーが動いている場合は止めてから起動します。

./run_image_cache_gpu.sh --port 18080 --backend-port 18097

別ターミナルから既存のクライアントをそのまま使えます。下の例の画像は各自でsample_pics/に配置します(画像は同梱していません)。

python3 systemone_client.py \
  --url http://127.0.0.1:18080 \
  --input examples/vision_gss_4.json \
  --image sample_pics/20260911_image_resized.png

同じ実験バイナリで無効にする場合:

JEV_IMAGE_CACHE_MIB=0 ./run_image_cache_gpu.sh --port 18080 --backend-port 18097

GPU_DEVICEの既定値はROCm0です。ROCmライブラリはROCM_LIBRARY_DIRで指定できます。未指定で既存LM Studioのvendor-v4ディレクトリが存在する場合はそれを利用します。

再利用範囲と寿命

  • LFM2のvisionのみが対象で、音声や他モデルには適用しません。
  • 正規化後の画素の完全なバイト列・形状・タイルの付加情報をキーにします。同じ画像IDを持つ別タイルの取り違えを防ぎます。
  • モデルコンテキストごとに独立したメモリ上のLRUです。画像を変更すると別のキーになり、モデル終了時に全削除します。ディスクには保存しません。
  • 既定の上限はキーとembeddingの合計128 MiB。超過時は古いエントリーから破棄します(管理オブジェクトや一時作業メモリは上限外)。JEV_IMAGE_CACHE_MIB=0..4096で変更できます。
  • 同時に来た同一画像のエンコードを重複させないよう、初回計算を含めてロックします。失敗した計算結果は保存しません。
  • 4問の初回送信では1回計算+3回再利用、同じ画像の再送信では4回とも再利用します。複数タイル画像では各タイル・サムネイルごとに同様に処理します。
  • HTTPのX-Jev-Local-State-Cache: off-imagesは引き続きdecoder側のState共有が無効であることを示します。画像embeddingのhit/missはバックエンドログのJEV_IMAGE_CACHEで確認します。

比較結果

512×286 pxのGSS画像+4問、R9700、4 slots、CTX_SIZE=32768。無効→有効→有効→無効の順で、各ブロックは初回1回+5回測定。モデルロードと入力ファイルの読み込みは時間から除き、HTTP応答完了までを測定しました。

条件時間
無効、初回リクエスト(2回)380〜383 ms
有効、未登録の画像(2回、各1 miss+3 hits)192〜212 ms
無効、初回を除く10回の中央値358 ms
有効、同じ画像の再送信10回の中央値98 ms

同じ画像の再送信では約3.66倍高速でした。初回は約1.9倍です。4問の選択結果は全条件で同じ。4並列での確率の揺れは最大約0.0247(2.47ポイント)あり、無効時にも揺れを確認しています。1ワーカーの追加比較ではキャッシュ有無で全候補確率の差は0でした。

これは1画像・4問の測定であり、サイズや質問数に応じて効果は変わります。元の質問の選択肢順による偏り・誤答の問題を改善する機能ではありません。

測定要約は画像評価の記録にまとめています。生データのresults/は各環境で生成するGit対象外のフォルダで、過去の測定ファイルは配布しません。

ビルドと再検証

Linux x86_64、GCC(g++、C++17)、CMake 3.14以上、Ninja、利用可能なAMD ROCm環境が必要です。通常の推論と異なり、コンパイルを行います。ROCmプラグインは公式b11042ランタイムのlibggml-hip.soを利用します。C++ ABIの一致のためGCC/libstdc++を使ってください。CCCXXCMAKENINJAで各実行ファイルを指定できます。依存ツールやOSパッケージは自動インストールしません。

./setup.sh --model vision --backend rocm
python3 scripts/build_image_cache.py --jobs 8

# LM Studio同梱ROCmライブラリを利用する場合の例。
# システムのROCmライブラリで動く環境ではLD_LIBRARY_PATHの指定は不要です。
LD_LIBRARY_PATH="$HOME/.lmstudio/extensions/backends/vendor/linux-llama-rocm-vendor-v4${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" \
  python3 -m tools.benchmark_image_cache \
    --image sample_pics/20260911_image_resized.png

setup.sh --model vision --backend rocmも同じROCm共有ライブラリが必要です。LM Studioのライブラリを利用する場合は、セットアップ前からLD_LIBRARY_PATHを設定してください。

比較ツールは同一の実験ビルドでキャッシュを無効・有効に切り替えます。入力は--input(Choiceのみ)、画像は--image、GPUは--deviceで指定できます。GPUの既定はROCm0で、機種名は固定しません。比較ツールは実験用ポート19080/19097を使い、終了・失敗時に自分が起動したプロセスを停止します。ポートが使用中なら停止し、--port--backend-portで変更できます。既存の測定JSONとログは上書きします。別保存先は--output、数値比較の逐次実行は--workers 1で指定します。

ソースのURLとSHA256はnative/source.jsonに固定し、変更箇所はscripts/patch_vision_cache.pynative/vision_embedding_cache.hで管理しています。ビルド成果物のハッシュは.cache/vision-build-gcc/jev-build.jsonに保存します。ネイティブ単体テストは同時ミスの集約、キーの区別、エラー処理、LRUによる破棄、上限超過、コンテキスト間の分離を確認します。

開発時の追加検証では、赤/青の別画像が混同されず、1024×1024の合成画像の4タイル+サムネイルがそれぞれ初回miss、再送時hitになることも確認しました。