Jev を 25 行の Python で実装する – ミニマルなローカル分類器の仕組み
すぐにわかるポイント
25 行の Python スクリプトで、GGUF 形式の LLM を読み込み、ラベル付きの選択肢を含むプロンプトを入力し、モデルの最終トークンのロジットを確率に変換することで、Jev のコア機能を模倣できます。
スクリプトの動作内容
GGUF モデルを読み込み、ラベル付きの選択肢を含むプロンプトをフォーマットし、フォワードパスを実行し、選択トークンの最後のトークンロジットを抽出して確率に正規化します。
# /// script
# requires-python = ">=3.12"
# dependencies = ["huggingface-hub", "llama-cpp-python", "numpy"]
# ///
import numpy
from llama_cpp import Llama
model = Llama.from_pretrained(
repo_id="Qwen/Qwen3-0.6B-GGUF",
filename="Qwen3-0.6B-Q8_0.gguf",
n_ctx=512,
logits_all=True,
verbose=False,
)
labels = ["A", "B", "C"]
choices = ["正当", "スパム", "フィッシング"]
email = "給与計算部門が会社外のサインインページでパスワードの入力を求めています。"
options = "\n".join(
f"{l}. {c}" for l, c in zip(labels, choices, strict=True)
)
prompt = f"""system
選択肢を一つ選びなさい。
user
メール: {email}\n\n{options}
assistant
\n\n"""
model.eval(tokens=model.tokenize(text=prompt.encode(), add_bos=False, special=True))
logits = model.scores[model.n_tokens - 1]
token_ids = [model.tokenize(text=l.encode(), add_bos=False)[0] for l in labels]
choice_logits = numpy.asarray([logits[t] for t in token_ids])
logprobs = choice_logits - numpy.logaddexp.reduce(choice_logits)
probabilities = numpy.exp(logprobs)
for name, scores in (
("ロジット", choice_logits),
("対数確率", logprobs),
("確率", probabilities),
):
values = numpy.round(scores.astype(float), 3).tolist()
print(f"{name}:", dict(zip(choices, values, strict=True)))
スクリプトは、以下の例のように3つの辞書を出力します:
ロジット: {"正当": 26.254, "スパム": 27.262, "フィッシング": 29.614}
対数確率: {"正当": -3.482, "スパム": -2.474, "フィッシング": -0.122}
確率: {"正当": 0.031, "スパム": 0.084, "フィッシング": 0.885}
なぜこれが重要なのか
Jev の本質的な振る舞い——離散的な選択肢を含む自然言語プロンプトを、校正された確率に変換する——には、特許取得済みの API や合成データ、RL に基づく後処理学習が不要であることを示しています。 全てのパイプラインはローカルで実行され、ネットワーク遅延が発生せず、GGUF 対応モデルであれば誰でも再現可能です。
コミュニティの知見
対数確率に関する注意点
"チャットモデルをベースに使用する場合、直接対数確率を取得するのは常に不快です。なぜなら、モデルは出力として散文を生成するように訓練されているからです。…明確なシステム指示を追加するか、構造化出力を使用して、モデルが逸脱しないようにするべきです。" – sigmoid10
このコメントは、モデルが選択トークンの前に追加の散文を生成した場合、トークンレベルの確率が歪む可能性があると警告しています。明示的なシステムプロンプトを追加するか、アシスタントの出力形式を制限することで、このリスクを軽減できます。
プロンプトの順序の影響
"マスク付きアテンションのため、選択肢を本文の前に置くと、トランスフォーマーはすでに何を探すべきかを把握しており、タスクに多くのトークンを割り当てることができます。" – antirez
プロンプト内で選択肢リストを先に配置することで、モデルの分類タスクへの集中が向上する可能性があります。
構造化出力の代替案
"モデルに『A』『B』『C』だけを生成させ、確率を調べるのではなく、直接『正当』『スパム』『フィッシング』を生成させるようにする…また、確率を言葉や数値で出力させることもできます。" – sigmoid10
アシスタントの出力に JSON やプレーンテキスト形式のスキーマを適用することで、後続のパース処理が簡素化され、トークンレベルのロジットに依存する必要が減ります。
キャリブレーションに関する懸念
"確率は常に正しいわけではありません。校正された意思決定には通常、RL に基づくファインチューニング(RLCD)が必要です。" – オリジナル投稿
多くのコメントでは、ロジットのままでは確率が適切にキャリブレーションされているとは限らないと指摘されています。温度スケーリング、Brier損失によるファインチューニング、または後処理のキャリブレーション曲線などの技術が、信頼性を向上させます。
速度と精度のトレードオフ
"なぜ分類器に『推論』を含めたくないのですか?速度とコストは明らかですが、これはトレードオフではありませんか?" – brap
このスクリプトは、レイテンシを犠牲にして、あらゆる思考の連鎖(chain-of-thought)推論を放棄しています。高リスクの意思決定では、明示的な推論を含む遅いモデルの方が、より高い精度をもたらす可能性があります。
実用的な考慮事項
モデルの選択
例では Qwen/Qwen3-0.6B-Q8_0.gguf を使用しており、0.6B パラメータのモデルで、比較的低スペックなハードウェアでも動作します。量子化やより小さなモデルを使用することで、より高速な推論が可能ですが、ドメインによって精度が異なる可能性があります。
ラテントシ測定
この投稿ではベンチマークの数値が提供されていません。コミュニティメンバーは、具体的なラテントシとエラー率の数値(例:「45 問に対して 200ms 未満」)を求めています。生産環境に導入する前に、ターゲットハードウェア上でエンドツーエンドの時間を測定することが不可欠です。
エラー処理
モデルが予期しないトークンを出力した場合、パース失敗が発生する可能性があります。厳密な出力スキーマ(例:choice フィールドを持つ JSON)とリトライロジックを追加することで、不正な応答を減らすことができます。
拡張性
同じパターンは、任意の多クラス分類タスクに適用可能です。labels、choices、email を適切なドメインデータに置き換え、プロンプトを新しい文脈に合わせて調整するだけです。
代替案とオープン実装
- OpenJev – より完成度の高いオープンソースのリファレンス実装。
- openjev-sglang –
sglangランタイムと統合され、より高いスループットを実現。 - DiffusionGemma 上の OpenJev – 別のバックボーンモデルでこのアプローチを実証。
- Laya – System-Oneスタイルの意思決定に特にチューニングされたオープンウェイトモデル(SylonZero のコメント参照)。
まとめ
25 行のスクリプトは、Jev の核心的なアイデア——プロンプトベースの分類と確率抽出——が、汎用的な LLM、特許取得済みのトレーニング、最小限のコードで再現可能であることを証明しています。ただし、実務家は、キャリブレーションの限界、プロンプト設計の細部、そして生産環境でこのような軽量パイプラインに依存する前に、堅牢な出力パースの必要性を認識しておくべきです。
Sources
関連
- Dispatch
- プロジェクト
- プロジェクト
- Dispatch
- プロジェクト