infodeliverailab/qwen3-omni-jp-vllm
Qwen3-Omni-30B-A3B 日本語 ASR + 話者分離 (vLLM サーバ版)
Qwen3-Omni-30B-A3B-Instruct をそのままの重みで日本語音声書き起こしに使う「レシピ」。 モデル重みは Qwen 公式そのまま (Qwen/Qwen3-Omni-30B-A3B-Instruct) を使い、 本レポは セットアップ + サーバ実装 だけを配布します。
- base: Qwen/Qwen3-Omni-30B-A3B-Instruct
- runtime: vLLM (fused MoE kernel + PagedAttention + CUDA graph)
- 測定環境: NVIDIA A100 80GB, bf16
- 速度実測: 30 秒の電話音声を 約 4 秒 で完全書き起こし (標準 transformers 実装比 約 6 倍)
- 実効計算は 3B クラス: 総パラメータは 30B だが MoE (top-k=8 / 128 experts) なので、 1 トークンあたり実際に走る expert は 8 個 = 推論時にアクティブになるパラメータは約 3B。 30B 全体をロードするだけの VRAM は要るが、演算量は 3B の dense モデルと同等水準。
性能
Qwen3-Omni-30B-A3B-Instruct そのままの重みで:
- 電話音声 (mulaw 8kHz、証券会社の営業電話) — 話者ラベル
[spk_0][spk_1]を正しく振り分け、 固有名詞 (顧客名、会社名、支店名) や数値 (銘柄コード等) までほぼ正確に起こす。 Whisper + pyannote などの従来型 (ASR + diarization を別段でつなぐ) パイプラインを圧倒する精度。 これは Qwen3-Omni がマルチモーダル LLM として音響 + 言語文脈を一体で扱える強みで、 固有名詞・数字・話者切替の判定が全て言語モデル側の推論として動くため。 - 話者分離 — 追加学習なし。1 パスで「誰が」「何を」を出力。話者数が可変でも自動対応。
- 企業名 / 商品名 / 銘柄コード — 日本語 LLM の中でも最上位の認識精度。
- 現代の潮流: 音響エンジン + LM のカスケードは (数年前まで最強だった) Whisper 系が代表格だが、 マルチモーダル LLM (Qwen3-Omni、Gemini 2 系、GPT-4o 音声系など) が 精度・柔軟性ともに世代交代 している状況。本レポはその実運用サンプル。
なぜ transformers そのままだと遅いのか
Qwen3-Omni は Mixture-of-Experts (MoE) モデルで、内部の各層に 128 個の「専門家 (expert)」 FFN があり、トークンごとに router がその中から動的に 8 個選んで使う。 標準の transformers ライブラリでの計算は、この一連の処理を大量の小さなカーネルに分けて実行する:
router で 8 個を選ぶ → 選ばれた expert に token を振り分ける → 各 expert ごとに FFN 計算
→ 重み α を掛ける → 結果を全部足し合わせるこれを バッチサイズ 1 (通常の推論) で回すと、GPU への「命令発行」オーバーヘッドが積み重なり、 実際の計算より待ち時間の方が支配的になる。48 層 × 30 個以上のカーネル起動が毎トークンで 走るので、GPU がほぼ「待機」の状態になってしまう。
vLLM が何をしているか
vLLM の MoE 実装は、上の一連の処理を 1 つの融合カーネル にまとめてある:
router → top-K → dispatch → expert FFN 群 → weighted sum ← 全部これを 1 発の GPU カーネルで加えて:
- PagedAttention — KV cache を効率よく管理
- CUDA Graph capture — カーネル起動オーバーヘッドを事実上ゼロに
- FlashAttention 内蔵
- thinker-only mode — 音声合成用の Talker/Code2Wav は読み込まない
これで A100 80GB 上、111 秒の音声 (200 トークン生成) を 2.1 秒 (95 tok/s) で処理。 transformers 標準の同条件が 14 秒 (14.4 tok/s) なので 約 6.5 倍。
ファイル構成
qwen3-omni-jp-vllm/
├── README.md (このファイル)
├── setup.sh (venv + 依存 install + モデル DL、1 発で deploy)
├── server.py (FastAPI + vLLM のブラウザ UI サーバ、port 8080)
└── example_transcribe.py (サーバ無しで 1 音声を書き起こす CLI)使い方
準備 (1 回だけ)
必要なもの: NVIDIA GPU (VRAM 70GB 以上、A100 80GB を推奨)、Python 3.10 以上、 インターネット接続 (モデル約 65GB を Hugging Face からダウンロード)。
git clone https://huggingface.co/infodeliverailab/qwen3-omni-jp-vllm
cd qwen3-omni-jp-vllm
bash setup.shsetup.sh は以下を自動でやります。所要時間は回線速度次第で 10〜30 分程度:
- Python の仮想環境を作る (venv)
- vLLM 等の必要ライブラリを
pip install - モデル (Qwen3-Omni-30B-A3B-Instruct、約 65GB) を
Qwen3-Omni-30B-A3B-Instruct/フォルダにダウンロード
ブラウザで音声をアップロードして書き起こす
source venv/bin/activate
python server.pyこれで `http://<サーバの IP>:8080/` にブラウザからアクセスできます。 音声ファイル (wav / opus / mp3 / m4a) をドラッグ&ドロップ、または「クリックで選択」で アップロードして「書き起こし実行」を押すだけ。数秒で結果が表示されます。
- 30 秒の音声で ~4 秒
- 3 分の音声で ~20-30 秒 (中身の密度による)
- 話者ごとに
[spk_0][spk_1]タグが自動で入り、色分けされます - 履歴 (直近 20 件) は同じページに残ります
長い音声 (5 分以上) を扱う場合は「max_tokens」を大きめ (1500-2000) にしてください。
コマンドラインで 1 ファイルだけ書き起こす
サーバを立てず、単発で結果を見たい場合:
source venv/bin/activate
python example_transcribe.py --audio your.wav結果が標準出力に出ます。出力例:
[spk_0] お電話ありがとうございます。◯◯証券△△店の宮沢でございます。
[spk_1] すいません、稲見です。お世話になります。
[spk_0] お世話になります。佐藤さんは?
...モデルフォルダの場所を変えたい
デフォルトはこのレポと同じ場所 (./Qwen3-Omni-30B-A3B-Instruct/) に置きます。別の場所 (容量の大きいディスクなど) に置きたい場合:
# setup 時
MODEL_DIR=/mnt/big-disk/qwen3-omni bash setup.sh
# サーバ起動時
MODEL_DIR=/mnt/big-disk/qwen3-omni python server.py
# CLI
python example_transcribe.py --audio your.wav --model_dir /mnt/big-disk/qwen3-omniトラブルシューティング
- モデル DL が途中で止まる — 再度
bash setup.shを実行するとレジューム - VRAM 不足エラー (OOM) — A100 80GB / H100 が推奨、A6000 48GB 以下だと厳しい
- `Engine core initialization failed` — 既に別の vLLM プロセスが VRAM を掴んでいる可能性。
nvidia-smiで確認、必要ならpkill -9 -f "python.*server"してから再起動 - UI で "書き起こしが途中で切れる" —
max_tokensを大きくして再実行 (デフォルト 800) - 初回リクエストが遅い — vLLM は最初の 1 回で CUDA graph を capture するため。2 回目以降は本来の速度が出ます
統計的に不要な expert を事前排除する余地 (実験的知見)
このモデルの routing 分布を calibration データ (電話 3 件 + YT 3 件で複数音声を通し、 decode 側 token 全体で softmax 分布を平均) で測ると、多くの層で「router がどの expert を選ぶかがほぼ均等」であることが分かった。具体的には、48 層のうち下記 27 層は router の候補集合を「層平均 softmax の上位 64 個」に絞ってしまっても 書き起こし品質が baseline とほぼ変わらない:
- T1 (
KL(token_softmax || layer_mean) < 0.16、7 層): layers 7, 16, 18, 19, 30, 31, 43 - T2 (
KL < 0.19、20 層): layers 6, 8, 9, 12, 13, 14, 15, 17, 20, 21, 24, 26, 27, 28, 29, 32, 33, 44, 45, 46
これら 27 層について router に「上位 64 個以外は選ぶな」という mask を掛けた 6 音声比較実験でも、話者ラベル / 固有名詞 / 数値まで含めて baseline と実質同等の 出力が得られた。残り 64 個の expert は実質的に不要であり、この 27 層に関しては 128 → 64 の dense 化 (KRAFTON MoE-to-dense の block-concat trick) の余地がある。
一方、T1+T2+T3 (kl < 0.22、38 層) に拡げると YT モノローグ音声で数字ズレ (「PER 9.29 → PR 9.29」など) が出始めるため、現状の安全域は T1+T2 の 27 層まで。
現在の vLLM 実装では weight-level の削減までは行っていない (mask だけでは router を通る点は同じで、速度改善には dense concat が必要)。この統計的知見は、 将来モデル自体を smaller dense に置き換える形で更なる速度改善を狙う場合の 根拠になる (別途研究中)。
ライセンス
- モデル重み: Qwen 公式ライセンス に従います
- 本レポの
setup.sh/server.py/example_transcribe.py: Apache-2.0
