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 SDKFunASRNanoStreamingVLLMPython アプリで chunk 単位の逐次テキストが必要な場合。
WebSocket リアルタイムサービスserve_realtime_ws.pyVAD 分割、話者ラベル付きの本番リアルタイムクライアント。

インストール

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 GBKV cache に余裕を持たせるため 16 GB 以上。
CUDA11.812.x
GPU 数1tensor 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 CLIpython 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 テキスト。
FinalizationSTOP残り音声を 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),
])

性能

シナリオPyTorch baselinevLLM高速化
オフライン、5.6 秒音声0.89 秒0.30 秒3x
オフライン、2-GPU tensor parallel0.89 秒~0.20 秒4.5x
Batch 16 files~16x serial cost~4x4x
Batch 32 files~32x serial cost~5x6x
WebSocket RTF0.1560.0782x

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_size1vLLM tensor parallel に使う GPU 数。
gpu_memory_utilization0.8vLLM KV cache に使用する GPU メモリ比率。
max_model_len4096vLLM の最大 sequence length。

generate()

パラメータデフォルト説明
inputs-音声パス、パスリスト、numpy array、tensor、wav.scp、JSONL。
languageNone言語ヒント。例: "中文""English""日本語"
hotwordsNoneASR prompt に入れるホットワード一覧。
itnTrue逆テキスト正規化を適用。
max_new_tokens512サンプルごとの最大生成 token 数。
temperature0.0デフォルトは greedy decoding。
repetition_penalty1.0vLLM 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 からクライアントを開いてください。