🎙️

RunPod H100×2でQwen3-Omniをデプロイする

に公開

はじめに

Qwen3-Omni-30B-A3B-Instructは、Alibaba Cloudが開発したネイティブomniモーダルモデルです。テキスト・画像・音声・動画を入力として受け取り、テキストと自然な音声をリアルタイムに出力できます。30Bパラメータ(MoE、アクティブ3B)という効率的なアーキテクチャで、介護向け音声対話システムのような実用的なユースケースに適したモデルです。

本記事では、クラウドGPU(RunPod H100 SXM 80GB × 2)上で、vLLM-Omni v0.18.0の公式Dockerイメージを使い、Qwen3-Omniのomniモード(テキスト+音声出力)をデプロイし、動作確認するまでの全手順を記録します。

技術スタック

コンポーネント バージョン / 構成
モデル Qwen/Qwen3-Omni-30B-A3B-Instruct
サービングフレームワーク vLLM-Omni v0.18.0(vLLM v0.18.0ベース)
Dockerイメージ vllm/vllm-omni:v0.18.0
GPU NVIDIA H100 SXM 80GB × 2
クラウド RunPod Secure Cloud

Qwen3-Omniとは

Qwen3-Omniは、Alibaba CloudのQwenチームが2025年9月にリリースしたネイティブomniモーダル基盤モデルです。「ネイティブ」という表現がポイントで、テキストモデルに音声・画像機能を後付けしたものではなく、最初からテキスト・画像・音声・動画を統合的に扱えるアーキテクチャで設計されています。

モデルアーキテクチャ

Qwen3-OmniはThinker-Talkerアーキテクチャを採用しています。

  • Thinker(思考部):テキスト・画像・音声・動画のマルチモーダル入力を理解し、推論とテキスト生成を担当。MoE(Mixture of Experts)アーキテクチャにより、30Bの総パラメータのうち約3Bのみをトークンごとにアクティベートします
  • Talker(発話部):Thinkerの出力(隠れ状態)を受け取り、8層RVQコーデックコードに変換。音声の韻律やイントネーションを制御します
  • Code2Wav:コーデックコードを24kHzの音声波形に変換するデコーダ

この分離設計により、Thinkerの推論がTalkerの音声生成に影響されず低レイテンシを実現できます。また、RAGパイプラインや安全フィルタをThinkerとTalkerの間に挿入するといった柔軟な構成も可能です。

モデルバリアント

モデル 用途
Qwen3-Omni-30B-A3B-Instruct Thinker + Talker。音声・テキスト両方の出力に対応
Qwen3-Omni-30B-A3B-Thinking Thinkerのみ。Chain-of-Thought推論に特化、テキスト出力のみ
Qwen3-Omni-30B-A3B-Captioner 音声キャプション生成に特化したファインチューニングモデル

本記事ではInstructモデル(音声出力あり)を使用します。

主な性能

  • 36のオーディオ/ビデオベンチマークのうち22でSOTA、オープンソースでは32でSOTA
  • 音声認識(ASR)・音声理解・音声対話の性能はGemini 2.5 Pro相当
  • テキスト119言語、音声入力19言語、音声出力10言語をサポート(日本語は入出力ともに対応)
  • Apache 2.0ライセンスで公開

vLLM-Omniとは

vLLM-Omniは、vLLMコミュニティが2025年11月に立ち上げた、omniモーダルモデルの推論・サービングに特化したフレームワークです。vLLM本体がテキスト生成に最適化されているのに対し、vLLM-Omniはテキスト・画像・音声・動画の入出力を含む「Any-to-Any」のマルチモーダル推論をサポートします。

vLLMとの関係

vLLM-OmniはvLLM本体を内部の依存パッケージとして利用する上位フレームワークです。「併用」するのではなく、vLLMの上にvLLM-Omniが構築されている関係です。vLLM-Omniのバージョン番号(例:v0.18.0)は、ベースとなるvLLMのバージョンに対応しています。

主な特徴

  • マルチステージパイプラインのオーケストレーション:Qwen3-OmniのThinker → Talker → Code2Wavのような複数ステージをGPU間で分離・並列実行
  • OpenAI互換API/v1/chat/completionsエンドポイントでmodalitiesパラメータを切り替えるだけでテキスト/音声を制御可能
  • Fully Disaggregated Serving:OmniConnectorによるステージ間の動的リソース配分。ステージごとに異なるGPUやノードに分散配置できる
  • プラットフォームカバレッジ:CUDA / ROCm / NPU / XPU をサポート
  • 公式Dockerイメージvllm/vllm-omni:v0.18.0(CUDA)、vllm/vllm-omni-rocm:v0.18.0(ROCm)

サポートモデル(2026年4月時点)

Qwen3-Omni / Qwen2.5-Omni / Qwen3-TTS / Bagel / MiMo-Audio / GLM-Image / Voxtral TTS / Fish Speech S2 Proなど、omniモーダルモデルと生成モデルを幅広くサポートしています。

なぜQwen公式Dockerではなくvllm-omniなのか

Qwen公式のDockerイメージ(qwenllm/qwen3-omni)も存在しますが、本記事ではvLLM-Omni公式イメージを選択しました。

比較項目 qwenllm/qwen3-omni vllm/vllm-omni:v0.18.0
vLLMバージョン v0.13.0相当(古い) v0.18.0(最新安定版)
CUDA 12.4 12.9
最終更新 約6ヶ月前 数週間前
主な用途 デモ・プロトタイピング(Gradio UI同梱) 本番APIサーブ
H100×2での検証 明記なし 公式検証済み
音声サーブ安定性 初期実装 大幅改善済み

本番環境での利用やファインチューニング後のモデルデプロイを見据えると、APIサーバーとして設計されたvLLM-Omni公式イメージのほうが適しています。

RunPodとは

RunPodは、AI/MLワークロードに特化したGPUクラウドプラットフォームです。AWS、GCP、Azureなどのハイパースケーラーと比較して、GPU単価が大幅に安く、Docker Podベースの柔軟なデプロイが特徴です。

RunPodの特徴

  • Docker Pod ネイティブ:任意のDockerイメージをPodとして直接起動可能。vllm/vllm-omni:v0.18.0のようなカスタムイメージを指定するだけでデプロイできる
  • 秒単位課金:使った分だけ支払い。短時間の検証でもコストを抑えられる
  • GPUの豊富な選択肢:RTX 4090からH100 SXM、H200、B200まで幅広くラインナップ
  • 2種類のクラウド
    • Community Cloud:第三者ホストのGPUをマーケットプレイス形式で利用。安価だがリライアビリティにばらつきあり
    • Secure Cloud:RunPod管理下のデディケート環境。SOC2準拠で、機密データを扱うプロジェクトに適切
  • エグレス料金無料:データ転送費用がかからない(AWSなどでは大きなコスト要因になる部分)

価格(2026年4月時点)

H100 SXM 80GBのオンデマンド価格は$2.69/hr/GPU。本記事のH100×2構成では$5.38/hrとなり、同等構成のAWS(約$7.86/hr)やAzure(約$13.96/hr)と比較して大幅に安価です。

本記事でSecure Cloudを選択した理由

介護施設の音声データを扱うプロジェクトでの利用を想定しているため、SOC2準拠のSecure Cloudを選択しました。Community Cloudはコストが若干低いものの、GPUが第三者ホスト上で稼働するため、機密性の高いデータを扱うユースケースには不向きです。

RunPodの環境構築

アカウント設定(事前準備)

  1. SSH公開鍵の登録:Settings → SSH Public Keysに公開鍵を貼り付け。VSCode Remote-SSHやscpでのファイル転送に必要
  2. Hugging Faceトークンの取得:Qwen3-Omniのモデルウェイトをダウンロードするために必要

Pod作成時の設定

設定項目
GPU H100 SXM 80GB × 2
Cloud Type Secure Cloud(SOC2準拠)
Container Image vllm/vllm-omni:v0.18.0
Container Disk 30GB以上
Volume Disk 100GB以上(マウント先: /root/.cache/huggingface
Expose HTTP Ports 8091, 7861
Expose TCP Ports 22(VSCode Remote-SSH用)
Environment Variables HF_TOKEN=hf_xxxxx
Docker Start Command bash -c "sleep infinity"(初回)

ポイント:初回はStart Commandを bash -c "sleep infinity" にします。モデルダウンロード(約60GB)に時間がかかり、サーバー起動をStart Commandに直接指定するとRunPodのヘルスチェックがタイムアウトして再起動ループに入るリスクがあるためです。

デプロイ手順

1. Pod起動後の接続

Pod起動後、Web TerminalまたはSSHで接続します。

# GPUの認識確認
nvidia-smi
# → 2x NVIDIA H100 80GB HBM3 が表示されること

2. モデルの事前ダウンロード

python3 -c "
from huggingface_hub import snapshot_download
snapshot_download('Qwen/Qwen3-Omni-30B-A3B-Instruct')
"

約60GBのモデルウェイトがVolume Diskにダウンロードされます。RunPodのネットワーク環境にもよりますが、10〜30分程度かかります。Volume Diskに保存されるため、Pod再起動時は再ダウンロード不要です。

3. vLLM-Omniサーバーの起動

vllm serve Qwen/Qwen3-Omni-30B-A3B-Instruct \
  --omni \
  --port 8091 \
  --trust-remote-code

--omni フラグにより、vLLM-Omniのマルチステージパイプラインが有効になります。デフォルトのステージ設定YAML(qwen3_omni_moe.yaml)が自動的にロードされ、以下のようにGPUが割り当てられます。

ステージ 役割 GPU メモリ使用量
Stage 0: Thinker マルチモーダル理解 + テキスト生成 GPU 0 約59.5 GiB
Stage 1: Talker テキスト埋め込み → 音声コーデック生成 GPU 1 約8.5 GiB
Stage 2: Code2Wav 音声コーデック → 波形変換 GPU 1 約0.4 GiB

初期化には約3.5分かかります。以下のログが出力されれば起動完了です。

[AsyncOmni] AsyncOmniEngine initialized in 215.40 seconds
[AsyncOmni] Initialized with 3 stages for model Qwen/Qwen3-Omni-30B-A3B-Instruct
Starting vLLM API server 0 on http://0.0.0.0:8091
Application startup complete.

起動ログの注目ポイント:

  • FlashAttention 3が自動選択されている(Using FlashAttention version 3
  • CUDA GraphがThinkerとTalkerで有効(H100ではenforce-eager不要)
  • Code2Wavステージはenforce-eagerで動作(これはデフォルト設定通り)
  • 利用可能スピーカー:aiden, chelsie, ethan

KVキャッシュに関する注意

起動ログで確認すべき重要な情報があります。

GPU KV cache size: 84,896 tokens
Maximum concurrency for 65,536 tokens per request: 1.30x

Thinkerステージ(GPU 0)はモデルウェイトだけで59.5 GiBを消費するため、KVキャッシュに使える残りメモリが限られます。65,536トークンのリクエストに対して最大並行数1.3xとタイトなので、長い入力や大きなmax_tokensを指定する場合は注意が必要です。

動作確認

ヘルスチェック

curl -v http://localhost:8091/health
< HTTP/1.1 200 OK

テキスト入力 → テキスト出力

curl http://localhost:8091/v1/chat/completions \
  -X POST -H "Content-Type: application/json" \
  -d '{
    "model": "Qwen/Qwen3-Omni-30B-A3B-Instruct",
    "messages": [{"role":"user","content":"こんにちは。日本語で簡単に自己紹介してください。"}],
    "modalities": ["text"],
    "max_tokens": 256
  }'

レスポンス:

{
  "choices": [{
    "message": {
      "role": "assistant",
      "content": "こんにちは!私はQwen-Omniです。中国のアリババグループ傘下の通義実験室が独自に開発した多モーダル超大規模言語モデルです。ユーザーの皆様に高品質なテキスト生成、画像理解、音声処理、コード生成などのサービスを提供することを目指しています。"
    },
    "finish_reason": "stop"
  }],
  "usage": {"prompt_tokens": 20, "completion_tokens": 100}
}

テキストモードでは、modalities: ["text"] のみを指定します。3ステージのうちThinkerだけが動作するため、レスポンスが高速です。

テキスト入力 → テキスト+音声出力(omniモード)

curl http://localhost:8091/v1/chat/completions \
  -X POST -H "Content-Type: application/json" \
  -d '{
    "model": "Qwen/Qwen3-Omni-30B-A3B-Instruct",
    "messages": [{"role":"user","content":"こんにちは。日本語で挨拶してください。"}],
    "modalities": ["text", "audio"],
    "audio": {"voice": "chelsie", "format": "wav"},
    "max_tokens": 128
  }' -o response.json

レスポンスには2つのchoicesが含まれます。

import json, base64

with open('response.json') as f:
    data = json.load(f)

for c in data['choices']:
    if c['message'].get('audio'):
        audio = base64.b64decode(c['message']['audio']['data'])
        with open('output.wav', 'wb') as f:
            f.write(audio)
        print(f'Audio saved: {len(audio)} bytes')
    elif c['message'].get('content'):
        print(f'Text: {c["message"]["content"]}')

結果:

Text: こんにちは!お元気ですか?何かお手伝いできることがありましたら、いつでもお知らせくださいね。
Audio saved: 367574 bytes

音声ファイルの検証

import wave
with wave.open('output.wav', 'rb') as w:
    print(f'Channels: {w.getnchannels()}')      # 1(モノラル)
    print(f'Sample rate: {w.getframerate()} Hz') # 24000 Hz
    print(f'Duration: {w.getnframes() / w.getframerate():.2f} sec')  # 7.66 sec

24kHzモノラルのWAVファイルが生成され、実際に再生すると日本語の自然な音声が確認できました。chelsie スピーカーはやや中国語ネイティブ寄りのアクセントがありますが、日本語として十分聞き取れる品質です。

APIフォーマットまとめ

音声入力

{
  "messages": [{
    "role": "user",
    "content": [
      {"type": "audio_url", "audio_url": {"url": "https://example.com/audio.wav"}},
      {"type": "text", "text": "この音声を書き起こしてください。"}
    ]
  }],
  "modalities": ["text"]
}

base64エンコードによるインライン送信も可能です(data:audio/mp3;base64,...)。対応フォーマットはMP3、WAV、OGG、FLAC、M4Aです。

音声出力の制御

パラメータ 説明
modalities: ["text"] テキストのみ出力(高速)
modalities: ["text", "audio"] テキスト+音声出力(3ステージ)
audio.voice スピーカー選択(aiden, chelsie, ethan
audio.format 出力フォーマット(wav

レスポンス構造(音声出力あり)

テキストとオーディオが別々のchoicesとして返されます。

{
  "choices": [
    {"index": 0, "message": {"content": "テキスト応答"}},
    {"index": 1, "message": {"audio": {"data": "<base64>", "format": "wav"}}}
  ]
}

2回目以降の起動

モデルがVolume Diskにキャッシュされた後は、PodのDocker Start Commandを以下に変更するとワンコマンドで起動できます。

--model Qwen/Qwen3-Omni-30B-A3B-Instruct --port 8091 --trust-remote-code

vLLM-Omni Dockerイメージのデフォルトentrypointが vllm serve --omni なので、上記の引数がそのまま渡されます。

コスト

RunPodでのコスト

項目
H100 SXM × 2 オンデマンド 約 $5.38/hr
初期化時間 約3.5分
月間コスト(24/7稼働) 約 $3,874
月間コスト(開発用 160h/月) 約 $860

メモリ使用状況

GPU 用途 モデル KVキャッシュ
GPU 0 Thinker 59.5 GiB 7.8 GiB
GPU 1 Talker + Code2Wav 8.9 GiB 37.3 GiB

GPU 0(Thinker)のメモリがタイトなので、長いコンテキストや高並行リクエストが必要な場合は、TP=2でThinkerを2GPU分散するか、モデルの量子化を検討する必要があります。

トラブルシューティング

Dockerイメージのダウンロードが進まない

Container Diskの容量不足が原因のことがあります。30GB以上に設定してください。Community Cloudではホストによってレジストリへの接続速度にばらつきがあるため、Secure Cloudを推奨します。

VSCode Remote-SSHで接続できない(PTYエラー)

RunPodのBasic SSH(ssh.runpod.io経由)はPTYをサポートしていません。Pod作成時にExpose TCP Portsに 22 を追加し、Public IP経由で接続する必要があります。

~/.ssh/config
Host runpod-qwen
    HostName {PUBLIC_IP}
    User root
    Port {EXTERNAL_PORT}
    IdentityFile ~/.ssh/id_ed25519
    StrictHostKeyChecking no

まとめ

RunPod H100 × 2上でvLLM-Omni v0.18.0を使い、Qwen3-Omniのフルomniモード(テキスト+音声入出力)のデプロイに成功しました。

  • vLLM-Omniの公式Dockerイメージ(vllm/vllm-omni:v0.18.0)により、ソースビルド不要で3ステージパイプライン(Thinker/Talker/Code2Wav)が動作
  • OpenAI互換APIで modalities パラメータを切り替えるだけでテキストモードとomniモードを選択可能
  • 日本語のテキスト応答・音声出力ともに実用的な品質で動作確認済み
  • H100 × 2の構成はvLLM-Omni公式の検証構成と一致しており、安定した動作が期待できる

介護向け音声対話AIのように、日本語での音声入出力が必要なプロジェクトにとって、Qwen3-Omni + vLLM-Omniの組み合わせは有力な選択肢です。今後はファインチューニングによる日本語音声品質の改善や、DGX Sparkへの移植を検討していきます。

株式会社Quixotiksテックブログ

Discussion