LLMおよびビジョンモデル向けのJev風Pythonラッパー(トークンログプロブを使用)

TL;DR – このラッパーの機能と重要性

著者は、Jevスタイルのリクエスト(状態、質問、オプションの画像添付)を任意のChat Completionエンドポイントに送信し、モデルに1トークン(選択肢を表す文字)だけを出力させ、すべての候補トークンのログ確率を読み戻す、最小限のPythonラッパーを構築しました。この手法により、生成型LLMを、テキストのみのモデルとビジョン拡張モデルの両方で動作する、安価で低遅延の分類器に変換できます。


核となるアイデア – ログプロブを使用して生成を分類に変換する

  • プロンプト形式 – ラッパーは、状態、質問、および[A]、[B]、…とラベル付けされた一連の選択肢をリストし、最後に*「最適な選択肢の文字のみで答えてください。」*という指示で終わるプロンプトを構築します。例:
    状態:
    このウェブカメラのフレームを検査してください。目に見えて存在するものだけを判断してください。
    
    質問:人が見えますか?
    選択肢:
    [A] true
    [B] false
    
    最適な選択肢の文字のみで答えてください。
    
  • APIパラメータ – リクエストには以下が含まれます:
    {
      "max_completion_tokens": 1,
      "logprobs": true,
      "top_logprobs": 20,
      "temperature": 0
    }
    
    max_completion_tokensを1に設定すると、モデルは単一のトークンのみを出力するよう強制され、応答が小さくなり、遅延が低くなります。
  • ログプロブの抽出 – APIは、上位N個のトークン候補とそのログ確率を返します。各選択肢の文字をトークンにマッピングすることで、ラッパーはこれらのログプロブを選択肢に対する正規化された確率分布に変換します。
  • 結果の解釈 – 二値質問(noulタイプ)の場合、ラッパーはtrueの確率を返します。多肢選択質問の場合、最も可能性の高い選択肢と完全な確率テーブルを返します。順序スケールの場合、期待値を計算します。

Jevをビジョンに拡張 – attachmentsフィールド

  • 元のJev仕様は、テキスト/JSONのstateのみをサポートしています。著者は、ファイルパスまたはbase64エンコードされたデータURLを含めることができるattachments配列を追加しました。
  • リクエストがOpenAIエンドポイントに送信されると、各画像はtype: "input_image"要素として追加されます。llama.cppの場合は、type: "image_url"として追加されます。
  • この例では、OpenCVでライブウェブカメラフレームをキャプチャし、JPEGとしてエンコードし、データURLにラップして、各リクエストの前にattachmentsに配置します。

エンドツーエンドのPython例(約150行)

このスクリプトは、ループ内で3つのステップを実行します:

  1. /dev/video0からフレームをキャプチャします。
  2. フレームをbase64 JPEGにエンコードし、data["attachments"]を設定します。
  3. 選択したバックエンド(ローカルのllama.cppサーバーまたはOpenAI)にリクエストを送信します(バックグラウンドスレッドを使用)。
  4. 最新の回答と測定されたFPSを含むテーブルを出力します。

主要な関数:

  • score(data, url, model) – 各質問のプロンプトを構築し、リクエストを送信し、ログプロブを正規化し、構造化された回答辞書を返します。
  • main – CLI引数(urlとmodel)を解析し、ウェブカメラを起動し、非同期スコアリングを調整します。

このスクリプトは意図的に自己完結型です。唯一の外部依存関係は、ウェブカメラアクセス用のopencv-pythonです。他のすべてのライブラリはPython標準ライブラリの一部です。


著者によって報告されたパフォーマンス数値

バックエンド モデル ハードウェア FPS(フレーム/秒)
llama.cpp(ローカル) Gemma‑4‑12B‑QAT(GGUF) RTX 3090 ≈ 1 fps(フレームあたり3つの質問)
OpenAI gpt‑6‑luna クラウド ≈ 0.2 fps

著者は、OpenAIの実行が遅い理由の一部は、各フレームが質問ごとに新しいHTTP接続を作成するためであり、これは最適化できると述べています。


ローカルllama.cppサーバーのセットアップ手順

# 1. 7 GBのGemma‑4‑12Bモデルとそのマルチモーダルプロジェクター(約175 MB)をダウンロードします
mkdir -p ~/models/gemma-4-12b/
cd ~/models/gemma-4-12b/
curl -fL -C - -o gemma-4-12b-it-qat-q4_0.gguf \
  https://huggingface.co/google/gemma-4-12B-it-qat-q4_0-gguf/resolve/main/gemma-4-12b-it-qat-q4_0.gguf
curl -fL -C - -o mmproj-gemma-4-12b-it-qat-q4_0.gguf \
  https://huggingface.co/google/gemma-4-12B-it-qat-q4_0-gguf/resolve/main/mmproj-gemma-4-12b-it-qat-q4_0.gguf

# 2. CUDA‑86 llama.cppバイナリをインストールします
curl -fL -o llama.zst \
  https://huggingface.co/buckets/ggml-org/install.sh/resolve/b11160/x86_64/linux/cuda/86/llama-app.zst
mkdir -p ~/bin/
zstd -d llama.zst -o ~/bin/llama
chmod +x ~/bin/llama

# 3. ポート8060でサーバーを起動します
~/bin/llama serve --models-dir ~/models/ --port 8060

サーバーが起動したら、ラッパーを実行します:

uv run webcam.py http://localhost:8060/v1 gemma-4-12b
# またはOpenAIを使用します(最初にOPENAI_API_KEYを設定してください)
uv run webcam.py https://api.openai.com/v1 gpt-6-luna

コミュニティの反応(厳選されたHNコメント)

@TeMPOraL – 「これは、スタートレックのような周囲認識を実現する方法です。マルチモーダルコンテキストから意図を推測し、自動的に行動します。」 このコメントは、単純な分類を超えたリアルタイムの意図検出にこのようなラッパーを使用するという、より広いビジョンを強調しています。

@prathje – 「これは、JSONレスポンスを使った文法ベースのデコードに過ぎないのでは?もしそうなら、Jevはその上にキャッシュレイヤーを追加したものに過ぎません。」 著者の実装は確かに決定論的なプロンプトとトークンレベルのロジットに依存しており、これは文法制約付きデコードに似ていますが、繰り返される状態プレフィックスに対する軽量なキャッシュ戦略を追加しています。

@frabcus – 「RLHFでトレーニングされた通常のLLMは、ネットワークの早い段階で決定を下す可能性があるため、Jevスタイルのログプロブラッパーは、キャリブレーションされた決定のために特別にトレーニングされたモデルよりも精度が低くなる可能性があります。」 これは潜在的な制限を指摘しています:バニラモデルの確率キャリブレーションは、下流の決定には最適ではない可能性があります。

@arcticbull – 「Jevに似ていますが、数桁高価で遅いです。」 このコメントは、汎用LLMの柔軟性と専用ビジョン分類器の効率性の間のトレードオフを反映しています。

@CROON_tv – 「テールレイテンシの数値を見たいです。私たちのリアルタイム音声エンドポイントでは、Jevは同じプロンプトのプレーンなLLMよりも速く、ためらいも少なかったです。」 レイテンシはインタラクティブアプリケーションにとって重要な指標であり、ラッパーの単一トークンアプローチはテールレイテンシを低く抑えるのに役立ちます。

@czl_my – 「OpenAI互換の任意のエンドポイントで動作するJevラッパーを作成しました:https://github.com/zhulinchng/jevper。」 外部実装は、このアイデアが注目を集めていることを示しています。


制限事項と未解決の質問

  • 確率キャリブレーション – バニラモデルからの生のログプロブは、特に稀なトークンに対して適切にキャリブレーションされていない可能性があります。ユーザーは温度スケーリングや事後キャリブレーションが必要になる場合があります。
  • 欠落トークンの処理 – ラッパーは、上位Nリストに存在しない選択肢のトークンをゼロ確率として扱いますが、欠落した質量が1e‑6を超える場合はエラーを発生させます。この安全チェックにより、静かな誤ランキングを防ぎます。
  • スケーラビリティ – 質問ごとに個別のリクエストを送信すると(デモではフレームあたり3つ)、ネットワークオーバーヘッドが増加します。複数の質問を単一のプロンプトにバッチ処理すると、スループットが向上する可能性があります。
  • ビジョンモデルの選択 – Gemma‑4‑12Bは動作しますが、専用のマルチモーダルモデル(例:CLIP、Florence)は、より高いFPSとより優れた視覚的精度を達成する可能性があります。

まとめ

紹介されたラッパーは、トークンレベルのログ確率を利用することで、任意のChat Completion API(ビジョン拡張バックエンドを含む)を高速で決定論的な分類器に変換する、実用的で言語モデルに依存しない方法を示しています。そのシンプルさ(単一のPython関数)と画像添付機能により、リアルタイムのマルチモーダル決定システムの有用な構成要素となりますが、確率キャリブレーションと質問ごとのリクエストオーバーヘッドに関する通常の注意点があります。

Sources