vLLM 推論エンジン
vLLM を使って自己回帰型の LLM-based ASR モデルを高速化します。新しいエンジンは、オフライン一括書き起こし、SDK 形式のチャンクストリーミング、VAD・ホットワード・話者ラベル付きの本番向け WebSocket サービスを提供します。
概要
音声 frontend、encoder、adaptor、任意の CTC タイムスタンプ decoder は PyTorch で実行されます。LLM decoder は prompt embedding として vLLM に渡され、PagedAttention、continuous batching、tensor parallel による高速化を利用します。
対応モデル
| モデル系列 | vLLM 対応 | 理由 |
| FunASRNano | 対応 | Audio encoder + adaptor + Qwen3-0.6B LLM。 |
| LLMASR / LLMASRNAR | 対応 | Whisper-style audio encoder の後段に Qwen、Vicuna、LLaMA decoder を持つ構成。 |
| GLMASR | 対応 | GLM-ASR-Nano は自己回帰 LLM decoding を使用。 |
| QwenAudioWarp | 対応 | LLM-based audio generation path。 |
| Paraformer、SenseVoice、Conformer、Transformer | 非対応 | LLM 自己回帰 decoding ではないため、通常の AutoModel を使用してください。 |
3 つの入口
| モード | 入口 | 用途 |
| オフライン一括推論 | AutoModelVLLM / FunASRNanoVLLM | 大量ファイルの書き起こし、スループット重視の処理。 |
| Streaming SDK | FunASRNanoStreamingVLLM | Python アプリで chunk 単位の逐次テキストが必要な場合。 |
| WebSocket リアルタイムサービス | serve_realtime_ws.py | VAD 分割、話者ラベル付きの本番リアルタイムクライアント。 |
インストール
pip install "funasr>=1.3.0"
pip install "vllm>=0.12.0"
pip install safetensors tiktoken websockets regex
# ソースツリーの AutoModelVLLM を使う場合
cd /path/to/FunASR
pip install -e .
| リソース | 最小 | 推奨 |
| GPU メモリ | 8 GB | KV cache に余裕を持たせるため 16 GB 以上。 |
| CUDA | 11.8 | 12.x |
| GPU 数 | 1 | tensor parallel 使用時は 2 枚以上。 |
初回実行時、FunASR は model.pt から LLM 重みを抽出し、Qwen3-0.6B-vllm のような vLLM 互換ディレクトリを作成します。以後は準備済みの重みを再利用します。
オフライン一括推論
推奨の汎用 API
from funasr.auto.auto_model_vllm import AutoModelVLLM
model = AutoModelVLLM(
model="FunAudioLLM/Fun-ASR-Nano-2512",
hub="ms", # または "hf"
tensor_parallel_size=2,
gpu_memory_utilization=0.8,
)
results = model.generate(
["audio1.wav", "audio2.wav"],
language="中文",
hotwords=["张三", "北京"],
)
for item in results:
print(f"[{item['key']}] {item['text']}")
Fun-ASR-Nano 直接 API
from funasr.models.fun_asr_nano.inference_vllm import FunASRNanoVLLM
engine = FunASRNanoVLLM.from_pretrained(
model="FunAudioLLM/Fun-ASR-Nano-2512",
tensor_parallel_size=4,
)
results = engine.generate(
inputs="wav.scp",
language="中文",
hotwords=["开放时间"],
max_new_tokens=512,
)
コマンドライン
cd examples/industrial_data_pretraining/fun_asr_nano
# 単一ファイル
python demo_vllm.py --input audio.wav --language 中文
# 一括 + マルチ GPU tensor parallel
python demo_vllm.py --input wav.scp --tensor-parallel-size 4 --batch-size 32
# ホットワード + JSONL 出力
python demo_vllm.py --input audio.wav --hotwords 张三 北京 --output results.jsonl
Streaming SDK
FunASRNanoStreamingVLLM は音声を 720 ms chunk に分割し、累積音声を再エンコードして、各 chunk の prompt を vLLM にバッチ投入します。fixed/unfixed のテキスト領域を返すため、Python アプリで逐次字幕を表示する用途に向いています。
from funasr.models.fun_asr_nano.inference_vllm_streaming import FunASRNanoStreamingVLLM
engine = FunASRNanoStreamingVLLM.from_pretrained(
model="FunAudioLLM/Fun-ASR-Nano-2512",
chunk_ms=720,
rollback_chars=8,
)
for result in engine.streaming_generate("audio.wav", language="中文"):
if result["is_final"]:
print(f"Final: {result['text']}")
else:
print(f"[{result['audio_duration_ms']:.0f} ms] fixed: {result['fixed_text']}")
| 動作 | 説明 |
| Stage 1 | 最初の 10 chunk は prev_text なしで decode し、安定した prefix を探します。 |
| Stage 2 | 以後の chunk は安定 prefix を assistant context として使用します。 |
| Rollback | 末尾 rollback_chars 文字は未確定として保持し、後続 chunk で修正可能にします。 |
| 短い音声 | 最初の 1.5-3 秒は空または不安定な場合があります。これはモデル特性です。 |
WebSocket サービス
リアルタイムサービスは streaming VAD、vLLM によるセグメント decode、partial preview、反復 hallucination の抑制、ホットワード、言語ヒント、話者分離を統合します。
サービス起動
cd examples/industrial_data_pretraining/fun_asr_nano
# 単一 GPU
CUDA_VISIBLE_DEVICES=0 python serve_realtime_ws.py --port 10095 --language 中文
# マルチ GPU tensor parallel
CUDA_VISIBLE_DEVICES=0,1 python serve_realtime_ws.py \
--port 10095 \
--tensor-parallel-size 2 \
--language 中文
# 全パラメータ例
python serve_realtime_ws.py \
--port 10095 \
--model FunAudioLLM/Fun-ASR-Nano-2512 \
--hub ms \
--device cuda:0 \
--decode-interval 0.48 \
--hotword-file 热词列表 \
--language 中文 \
--dtype bf16 \
--tensor-parallel-size 1 \
--gpu-memory-utilization 0.8 \
--max-model-len 2048
クライアント
| クライアント | 使い方 |
| ブラウザ | client_mic.html を開く。マイク、ファイルアップロード、ホットワード、話者ラベルに対応。 |
| Python CLI | python client_python.py --server ws://localhost:10095 --mic |
| テストスクリプト | python client_test.py --server ws://localhost:10095 --file audio.wav |
リモート GPU サーバーでは、先にポートフォワードします: ssh -L 10095:localhost:10095 <server>
プロトコル
Client -> Server:
"START" セッション初期化
"HOTWORDS:word1,word2" ホットワード設定、任意
"LANGUAGE:中文" 言語設定、任意
[binary bytes] PCM16 16 kHz mono audio
"STOP" 最終 decode
Server -> Client:
{"event": "started"}
{"event": "hotwords_set", "hotwords": ["word1", "word2"]}
{"event": "language_set", "language": "中文"}
{"sentences": [...], "partial": "...", "is_final": false}
{"sentences": [...], "partial": "", "is_final": true}
{"event": "stopped"}
推論ロジック
| 経路 | トリガー | 出力 |
| 確定セグメント | Dynamic VAD が endpoint を検出。 | セグメント全体を vLLM で decode し、sentences にロック。 |
| Partial preview | --decode-interval、デフォルト 0.48 秒、かつ十分な新規音声。 | 後で上書きされる可能性がある一時的な partial テキスト。 |
| Finalization | STOP。 | 残り音声を flush し、進行中の発話を強制終了、話者再クラスタリング後に is_final: true を返します。 |
Dynamic VAD
Dynamic VAD は現在の発話の長さに応じて無音閾値を調整します。短い発話では長めに待って切りすぎを防ぎ、長い発話では早めに分割して ASR 品質を保ちます。
Streaming wrapper のデフォルト
| 累積発話時間 | 無音閾値 | 効果 |
| ≤ 5 秒 | 2.0 秒 | 短いターンを早く切りすぎない。 |
| 5-10 秒 | 1.5 秒 | 通常の会話分割。 |
| 10-15 秒 | 1.0 秒 | 長い発話を少しずつ締める。 |
| 15-30 秒 | 0.8 秒 | より速い分割。 |
| 30-45 秒 | 0.4 秒 | ASR セグメントが長くなりすぎるのを防ぐ。 |
| > 45 秒 | 0.1 秒 | 強制的に分割。 |
fsmn-vad ネイティブのデフォルト
| 累積発話時間 | 無音閾値 |
| ≤ 5 秒 | 800 ms |
| 5-10 秒 | 600 ms |
| 10-20 秒 | 500 ms |
| 20-30 秒 | 400 ms |
| > 30 秒 | 300 ms |
# デフォルト: dynamic_silence=True
model.generate(input="audio.wav")
# 動的閾値を無効化
model.generate(input="audio.wav", dynamic_silence=False)
# カスタム schedule: (duration_limit_ms, silence_threshold_ms)
model.generate(input="audio.wav", silence_schedule=[
(5000, 1000),
(15000, 500),
(float("inf"), 200),
])
API リファレンス
AutoModelVLLM
| パラメータ | デフォルト | 説明 |
model | - | ModelScope/Hugging Face の id、またはローカルモデルディレクトリ。 |
hub | "ms" | "ms"、"modelscope"、"hf"、"huggingface"。 |
device | "cuda:0" | PyTorch audio encoder/adaptor の実行デバイス。 |
dtype | "bf16" | "bf16"、"fp16"、"fp32"。 |
tensor_parallel_size | 1 | vLLM tensor parallel に使う GPU 数。 |
gpu_memory_utilization | 0.8 | vLLM KV cache に使用する GPU メモリ比率。 |
max_model_len | 4096 | vLLM の最大 sequence length。 |
generate()
| パラメータ | デフォルト | 説明 |
inputs | - | 音声パス、パスリスト、numpy array、tensor、wav.scp、JSONL。 |
language | None | 言語ヒント。例: "中文"、"English"、"日本語"。 |
hotwords | None | ASR prompt に入れるホットワード一覧。 |
itn | True | 逆テキスト正規化を適用。 |
max_new_tokens | 512 | サンプルごとの最大生成 token 数。 |
temperature | 0.0 | デフォルトは greedy decoding。 |
repetition_penalty | 1.0 | vLLM generation の repetition penalty。 |
戻り値: [{"key": str, "text": str, "timestamps": [...]}]。モデルに任意の CTC decoder/tokenizer が含まれる場合、タイムスタンプも返ります。
FAQ
- Q: 初回起動が遅いのはなぜですか?
- vLLM が KV cache と CUDA graph を初期化し、FunASR が
model.pt から LLM 重みを抽出する場合があります。初回は 60-90 秒程度かかることがあります。
- Q: CUDA OOM の対処は?
gpu_memory_utilization または max_model_len を下げるか、tensor_parallel_size を増やしてください。
- Q: Paraformer は vLLM を使えますか?
- いいえ。Paraformer は非自己回帰モデルであり、vLLM の KV-cache decoding の恩恵を受けません。
- Q: WebSocket と
streaming_generate() の使い分けは?
- 本番のリアルタイム ASR には VAD endpoint ベースの WebSocket サービスを使います。SDK 組み込みや chunk 単位デモには
streaming_generate() が便利です。
- Q: リモートサーバーでブラウザのマイクが使えません。
- Chrome はマイク利用に HTTPS または localhost を要求します。
ssh -L 10095:localhost:10095 <server> で転送し、localhost からクライアントを開いてください。