TT Lab
ブログ

한국어 English 日本語 中文

はじめる
ブログ

ブログ

構造化出力: トークンごとに「使えない文字」をマスクするコスト

状態: ドラフト (2026-10-27)、実測値。 8GBクラスのノートPC用GPU(Ada世代)1基、vLLM 0.31.0(xgrammar 0.2.7, llguidance 1.7.6)、Qwen3-Reranker-0.6B を使用して 2026-10-27 に測定しました。公式ドキュメントは 0.32.0 基軸で確認し、構造化出力のドキュメントは 0.31.0 と同様でした。 2026-10-27 追加。 対話用の Qwen3-0.6B・Qwen3-4B(AWQ)と vLLM 0.32.0(xgrammar 0.2.8)を用いて正解のある抽出タスクを測定した結果を 3-1 節に追加しました。0.31.0 の数値はそのまま残しています。 作成規則: 実測データを基本とし、測定できなかったものは「未測定」、確認できなかったものは「未確認」と表記します。

目次

1. 原理: 次のトークン候補から文法外のものを除外する

モデルはステップごとにボキャブラリ全体(このモデルでは 151,669 個)に対するスコアを算出し、その中から 1 つを選択します。構造化出力は、選択の直前に 1 つのステップを挟み込みます。これまでの生成テキストを文法エンジン(有限オートマトンやプッシュダウン・オートマトン)に入力して「今出現可能なトークン」の集合を求め、それ以外のトークンのスコアを負の無限大でマスクします。この集合をビットマスクに変換し、GPU のスコアテンソルに一度に適用します。

지금까지: {"priority": "
문법상 다음에 올 수 있는 것: l(ow), m(edium), h(igh) 로 시작하는 토큰만
나머지 151,000여 토큰 → 점수 -inf

そのため、モデルがどれほど的外れであっても、結果は文法に違反しません。ただし、2 つのコストが生じます。

  1. 文法を生成するコスト: JSON スキーマや正規表現をトークン単位のステートマシンに変換する処理です。ボキャブラリが 15 万件あるため、トークンごとに「このトークンはこの状態で許可されるか」を事前に計算しておく処理に負荷がかかります。同じスキーマはキャッシュされます。
  2. ステップごとにマスクを生成するコスト: リクエストごとに状態を 1 つ進め、マスクを生成して GPU に送信します。

XGrammar の論文(Dong et al., MLSys 2025)によると、コンテキストに依存しない判定トークンを事前に計算しておき、文法計算を GPU 実行と並行して処理することで、従来の手法よりも最大 100 倍高速であり、サービングエンジンと組み合わせた場合のオーバーヘッドはほとんどないと報告されています(要約に基づく。条件は要約に記載なし)。vLLM は xgrammar と llguidance (guidance バックエンド) の両方を使用し、デフォルト値の auto はリクエスト内容を確認して選択します。

2. 使い方と名称の変遷

OpenAI 形式の response_format と vLLM 固有の structured_outputs の 2 つの方法があります。

目的 リクエストに含める内容
JSON スキーマ response_format: {"type": "json_schema", "json_schema": {...}} または structured_outputs: {"json": 스키마}
任意の JSON オブジェクト response_format: {"type": "json_object"}
選択肢のいずれか structured_outputs: {"choice": ["low", "medium", "high"]}
正規表現 structured_outputs: {"regex": "[A-Z]{3}-[0-9]{4}"}
文脈自由文法 (EBNF) structured_outputs: {"grammar": "..."}
タグ内のみ JSON structured_outputs: {"structural_tag": ...}(パート 5 の strict ツール呼び出しはこの方式)

バージョンアップに伴い名称が変更されました。従来の guided_json, guided_regex, guided_choice, guided_grammar やリクエストごとの guided_decoding_backend は 0.12.0 で削除され、現在は structured_outputs に統合されました。バックエンドはサーバー側で --structured-outputs-config.backend によって決定され、リクエストごとにバックエンドを選択する機能は V1 から削除されました。正規表現の文法はバックエンドごとに異なり、xgrammar と guidance は Rust 形式、lm-format-enforcer は Python の re 形式です。0.32.0 以降では、64 階層より深くネストされた JSON スキーマは 400 エラーとして拒否されます(リリースノートに基づく、未測定)。

3. 実際に測定してみる

方法

結果 1. 強制した場合の間違いは途切れたものだけであった

同時リクエスト数 16、測定 3 バッチ分(48 リクエスト)の合計です。

バリエーション auto guidance xgrammar 応答例
制約なし 0/48 0/48 0/48 No.
json_object 0/48 0/48 0/48 {"status": "no", "data": []}
json_schema 47/48 46/48 46/48 {"name": "", "priority": "high", "tokens": 0, "tags": []}
choice 48/48 48/48 48/48 low
regex 48/48 48/48 48/48 IOW-1139

結果 2. コスト: 初回リクエスト、およびトークンごとのわずかなオーバーヘッド

バリエーション バックエンド 新スキーマ初回リクエスト TTFT(同時 1、ウォームアップバッチ) 以降の TTFT 中央値 トークン間隔中央値(同時 1) トークン間隔中央値(同時 16)
制約なし xgrammar サーバー 43.6ms 14.1ms 5.9ms 6.3ms
json_schema xgrammar 445.7ms 14.2ms 6.3ms 7.4ms
json_schema guidance 974.6ms 14.3ms 6.0ms 7.3ms
json_schema auto 508.0ms 14.3ms 6.2ms 7.5ms
regex xgrammar 15.0ms 13.7ms 6.5ms 7.8ms
choice xgrammar 22.3ms 14.2ms 6.4ms 7.7ms

遭遇した問題: 初回サンプリングバッチにおける数秒の遅延

測定中、サーバーのログに次のような警告が出力されました。

WARNING [jit_monitor.py:140] Triton kernel JIT compilation during inference: _topk_topp_kernel.
This causes a latency spike; consider extending warmup to cover this shape/config.

新しいサーバーで初めて温度サンプリング(top-k・top-p)リクエストのバッチが入力されると、その場でカーネルがコンパイルされ、該当バッチの TTFT が 3.9 秒(guidance サーバー、同時 1)から 11 秒(guidance サーバー、同時 16)まで増加しました。貪欲デコーディングのみを実行している間はこの警告は発生しませんでした。コンパイルは 1 回で完了するわけでもありませんでした。別のサーバーで温度 1.0 のリクエストを同時 1, 8, 32 の順で送信したところ、並行数を変更するたびにそのバッチの処理が再び低速化しました(256 トークン生成において、同時 1 は 5.4 秒台から 1.5 秒、同時 8 は 7.4 秒台から 1.9 秒、同時 32 は 8.4 秒台から 3.2 秒)。バッチサイズごとにカーネルを新しく生成しているものとみられます。モデルの generation_config.json がデフォルトのサンプリング値(temperature 0.6, top_k 20, top_p 0.95)を上書きしているという警告も同時に出力されました。運用環境では、サーバーを起動した後に本番と同等のサンプリング設定を持つリクエストで一度ウォームアップを行ってから、トラフィックを受け付ける必要があります。0.32.0 のリリースノートには、サンプラーのウォームアップコンパイルを削減したという項目があります(未測定)。

3-1. 2026-10-27 再測定: 正解のある抽出タスク

前回のテストでは「正しい JSON であるか」のみを確認しました。今回は値が正しいかどうかも確認しました。

モデル バリエーション 即座に読み込み可能 スキーマ準拠 値が正しい 応答時間中央値
Qwen3-0.6B 制約なし 0/18 0/18 18/18 254.9ms
Qwen3-0.6B json_object 18/18 18/18 18/18 248.0ms
Qwen3-0.6B json_schema 18/18 18/18 18/18 273.5ms
Qwen3-0.6B FP8 制約なし 6/18 6/18 18/18 187.0ms
Qwen3-0.6B GPTQ 8-bit なし(強制なし) 0/18 0/18 18/18 185.6ms
上記3モデル json_object・json_schema 54/54 54/54 54/54
Qwen3-4B AWQ 4-bit なし(強制なし) 18/18 18/18 18/18 476.0ms
Qwen3-4B AWQ 4-bit json_schema 18/18 18/18 18/18 507.5ms

4. 運用上のポイント

5. 検証できなかった事項

参考

ログインするといいねできます

コメント

まだコメントはありません。

ログインするとコメントできます