AI・機械学習
テキスト音声合成モデルを50ミリ秒未満で応答させる方法
How We Made a Text-to-Speech Model Respond in Sub-50 ms (nari-labs.com)
要約
Nari Labsは、Qwen3-TTS 1.7B CustomVoiceの実装により、単一のNVIDIA H100 SXM上で毎秒10リクエスト(RPS)と95パーセンタイルで50ミリ秒未満の初回音声生成時間(TTFA)を達成し、リアルタイム再生を維持することに成功しました。このシステムは、他の主要な実装と比較して大幅に低遅延かつ低コストであり、その実装とベンチマーク結果はオープンソースとして公開されています。
全文翻訳
TL;DR
当社のQwen3-TTS 1.7B CustomVoice実装は、単一のNVIDIA H100 SXM上で毎秒10リクエスト(RPS)と95パーセンタイルで50ミリ秒未満の初回音声生成時間(TTFA)を達成し、リアルタイム再生を維持します。× ポアソンオープンループトラフィック下で、当社の実装、vLLM-Omni、SGLang-Omni△、VoxServe、M*の5つの実装を比較します。低遅延ストリーミングのために各実装をチューニングした後、当社の実装だけが95パーセンタイルで50ミリ秒未満のTTFAを達成しました。10 RPSで95パーセンタイルTTFAを50ミリ秒未満に維持し、20 RPSでも100ミリ秒未満に抑えます。当社のシステムは、10 RPSで毎秒約630文字を生成します。1× H100 SXMインスタンスあたり1時間あたり4.29ドルというコストで、これはフル稼働時で100万文字あたり約2ドルに相当します1。比較として、ElevenLabs V3は100万文字あたり100ドル、Cartesia Sonic 3.5は100万文字あたり49ドルで、TTFAはより高くなります。実装とベンチマークをオープンソース化します。手法については以下で説明します。
「リアルタイム」TTSの定義
まず、リアルタイムTTSサーバーが達成すべきことについて議論しましょう。これは4つの部分からなる問題だと考えています:低い可聴TTFA:リクエスト発行から最初の可聴サンプルまでの時間が短いこと。アンダーランゼロ:再生が開始されたら、クライアントはバッファリングされたオーディオを使い果たしてはなりません。容量:RPSが増加しても、1と2が維持されること。不正な形式の出力なし:音声は聞き取れる必要があります。私たちは、最も人気のあるTTSモデルの1つであり、許可ライセンスを持つQwen3-TTS CustomVoice 1.7Bを選択します。上記の定義に基づき、高RPSを単一のNVIDIA H100 SXMで維持しながら、低い95パーセンタイル可聴TTFAとアンダーランゼロを目標とします。すべてのベンチマークは、Fireworks AIのLLMベンチマークに従い、ポアソンオープンループトラフィック下で5分間実行されます。各エンジンは、単一のHTTPリクエストで完全なテキストを受け取り、オーディオ出力はストリーミングのままです。可聴TTFAを検出し、受信したPCMから再生を再構築し、Deepgram STTを使用して完了したオーディオを評価します。
他のエンジンはどのようにパフォーマンスを発揮するか?
以下の表は、各エンジンの1 RPSでのアップストリーム/デフォルトの結果を示しています。この実行では互換性のためにのみ変更を加えています。
エンジン | 95パーセンタイル可聴TTFA | 95パーセンタイル先行無音 | アンダーランが発生したリクエスト
vLLM-Omni | 277.883 ms | 90 ms | 100%
SGLang-Omni△ | 1,140.69 ms | 80 ms | 0%
VoxServe | 315.064 ms | 30 ms | 0%
M* | 1,159.956 ms | 90 ms | 0%
これらのデフォルトには大幅な改善の余地があります。各サービングエンジンを、それぞれの遅延、連続性、品質、および容量の要件に合わせてチューニングします。
1. 先行無音の削除
モデルから返される最初のPCMには、最初の持続的な音声の前に数十ミリ秒の無音が含まれることがあります。このギャップはTTFAを以下のように遅延させます:
動的なトリムを追加します。これは、短いRMSウィンドウから持続的な音声を検出し、開始前のサンプルを削除し、残りのオーディオを通常どおりストリーミングします。この変更はTTFAを約80ms改善しますが、モデル推論自体を高速化するわけではありません。
2. フレーム蓄積のチューニング
デコードしてオーディオチャンクを解放する前に収集するコーデックフレームの数もチューニングします。小さい初期チャンクはTTFAを削減しますが、再生ヘッドルームを少なくし、デコーダーの作業をより頻繁に発生させます。大きいチャンクはバッチ処理が容易で、連続再生をより安全にしますが、最初の可聴出力を遅延させます。したがって、有用な構成は小さいチャンクから始まり、後続の出力のためにチャンクサイズを増やします。正確なノブはエンジンによって異なります:vLLM-Omniはcodec_chunk_framesやcodec_chunk_rampなどの設定を公開しており、他のエンジンは同等のチャンクまたはストライド制御を提供します。これらの値を繰り返しテストして、低い95パーセンタイルTTFA、アンダーランゼロ、および負荷増加時の安定した動作に最も適した構成を見つけます。
チューニング後の既存サービングエンジンのパフォーマンス
以下の表は、先行無音およびフレーム蓄積チューニング後の各エンジンの選択されたアンダーランゼロプロファイルを示しています。
エンジン | 95パーセンタイルTTFA (1 RPS) | 95パーセンタイルTTFA (6 RPS)
vLLM-Omni | 56.815 ms | 93.451 ms
SGLang-Omni△ | 120.879 ms | 273.700 ms
VoxServe | 49.3 ms | 363.2 ms
M* | 104.035 ms | 179.501 ms
VoxServeは1 RPSで50ミリ秒未満の95パーセンタイルTTFAに達しますが、他の3つのエンジンは達しません。約6 RPSになると、すべてのエンジンが95パーセンタイルTTFAで約100ミリ秒以上になります2。
Qwen3-TTSの最適化方法
まず、Qwen3-TTSのアーキテクチャを理解する必要があります。これは、階層的なマルチコードブック生成を行う3つの部分からなるモデルです。Talkerは各オーディオフレームの最初のコードブックトークンを予測し、Code Predictorは残りの15個のコードブックトークンを生成し、causal Codecはコードブックトークンを波形サンプルに変換します。各モジュールには独自の計算プロファイル、バッチ処理動作、および遅延要件があります。各モジュールを個別に最適化するのではなく、より広範な質問に焦点を当てます:サービングシステムはこれらの異種タスクをどのように調整すべきか?
1. 3つのモジュールを1つのスケジューラに統合する
ほとんどのQwen3-TTSサービング実装は2つのステージに分かれています:TalkerとCode Predictorは一緒に実行され、Codecは別々に実行されます。この分離により、トークン生成と波形デコードがリクエスト間でオーバーラップできるようになります。私たちはこれをさらに進めます。Talker、Code Predictor、およびCodecを3つの独立してスケジューリング可能なタスクとして公開します。鍵となるのは、単にそれらを部分に分割することではなく、すべてを1つのスケジューラによって管理される共有スケジューリングサーフェスに統合することです。この設計はM*(arXiv)から着想を得ています。このセットアップにより、スケジューラはTalkerを実行するか、Code Predictorを進めるか、再生デッドラインに近づいているCodecジョブを優先するかを決定できます。また、同じモジュールを待っているリクエストをバッチ処理することもできます。固定の実行順序に従うのではなく、緊急度に応じて作業を並べ替えることができます。TalkerとCode Predictorを組み合わせると、中間境界がなくなるため、より効率的になるように見えるかもしれません。しかし、結合された操作は、より緊急性の高いCode PredictorまたはCodecジョブをブロックする、プリエンプト不可能な作業単位になる可能性があります。モジュールを分離しておくことで、より短い作業単位が作成され、スケジューラはリクエストをインターリーブする機会が増えます。
2. 音声ストリーミングのニーズに合わせてスケジューリングする
音声ストリーミングには、緊急度に関する2つの異なる概念があります。最初のオーディオチャンクが到着する前は、ミリ秒単位でTTFAが増加するため、このパスを優先する必要があります。しかし、再生が開始されたら、目標が変わります:次のチャンクは、現在のオーディオが再生を終了する前に到着する必要があります。それより早く生成しても、ユーザーには目に見えるメリットはありません。したがって、最初のオーディオを生成していないリクエストを優先し、確立されたストリームは再生デッドラインに近づくまで緊急になりません。すべての緊急リクエストを単独で実行すると、バッチ処理の効率が低下します。代わりに、スケジューラは緊急リクエストをアンカーとして選択し、互換性のある作業で残りのバッチを埋めます。これにより、クリティカルなリクエストがデッドラインを満たすのに役立つと同時に、GPUを効果的に使用できます。このポリシーは、3つのモジュールすべてがスケジューリングサーフェスを共有しているため、スケジューラがリクエストと進めるパイプラインステージの両方を選択できるため、特にうまく機能します。
3. Code Predictorの規則的な構造を利用する
Code Predictorは自己回帰型トランスフォーマーですが、その実行は異常に規則的です。フレームごとに固定数のステップ(15)を実行して、残りのオーディオコードブックを埋めます。その固定構造を利用してKVキャッシュを事前割り当てし、フレーム生成ループ全体を単一のCUDAグラフとしてキャプチャします。また、その短く境界のあるコンテキストに特化したTritonアテンションカーネルを使用します。ホスト駆動のシーケンスを固定GPUプログラムに置き換えることで、遅延を削減し、実行システムを簡素化します。
4. キャッシュされた状態を中心にCodecを再構築する
Qwen3-TTS Codecは、トランスフォーマーとCNNで構成されています。次のオーディオチャンクを生成するには、トランスフォーマーのコンテキストと、前のチャンクからの畳み込み状態の両方が必要です。ナイーブな実装では、発話が長くなるにつれて古いオーディオを繰り返しデコードしながら、更新ごとにフレーム履歴全体を再処理します。これを回避するために、状態キャッシュベースのCodecを使用します。各リクエストは、次のチャンクに必要なトランスフォーマーコンテキストと畳み込み状態を保持します。増分デコードは、このキャッシュされた状態を再利用し、新しいものだけを処理します。