MiraEcho
記事一覧

音声モデルはもう LLM である ── GPU の見積もり方が通用しなくなった

約 12 分
  • TTS
  • ASR
  • 推論基盤
  • キャパシティ

かつて音声と言語モデルは別々の技芸だった。ASR はエンコーダに CTC かアテンションデコーダを載せたもの、TTS は音響モデルとボコーダの組み合わせ。配信の仕方も普通のニューラルネットと同じで、入力長は決まっていて、計算量は固定、レイテンシは FLOPs とバッチサイズから見積もれた。

いま現場で動いているものは、もうそれではない。Whisper 系の ASR、CosyVoice、Fish Speech、VoxCPM、そして音声を入れて音声を出す omni 系──どれも KV キャッシュを抱えて離散的な音声トークンを一歩ずつ予測する自己回帰 Transformer だ。末端のボコーダは計算量の勘定では端数でしかない。高いのは全部デコードである。

モデリング面の恩恵はさんざん語られてきた。数秒の参照音声から声を写し取れる、長文でも韻律が崩れない、ひとつのアーキテクチャで多言語をまかなえる。あまり語られないのは運用面の帰結のほうで、しかも当番の携帯を鳴らすのはそちらだ。あなたが動かしているのはもう音声サービスではなく、たまたま音声を吐く LLM 推論サービスである。 前者について知っていることの多くは、後者には当てはまらない。

RTF はもうモデルの属性ではない

リアルタイムファクタ(音声 1 秒を作るのに何秒の計算がかかるか)は、どのベンダーも公表し、どの購入側も見積もりの土台にする数字だ。RTF 0.15 はきれいな容量単位に見える。1 枚のカードが実時間の約 7 倍で音声を作るのだから、余裕をみて同時 6〜7 ストリーム、といった具合に。

この計算は RTF が定数であることを前提にしている。continuous batching で配信される自己回帰モデルにとって、それは「そのカードに他に誰が乗っているか」の関数だ。

デコードはメモリ帯域律速である。1 ストリームだけ流しているとき、トークンを 1 個吐くために重み行列を丸ごとメモリから引きずり出していて、GPU はほとんど遊んでいる。公表される単一ストリームの RTF があれほど良く見えるのはそのためだし、2 本目を足すのがほぼタダなのも同じ理由だ。8 本まとめても 1 ステップの所要時間は 1 本のときとさして変わらないので、合計スループットはほぼ線形に伸び、ストリームあたりの RTF はほとんど動かない。キャパシティ設計がいちばん気持ちよく感じる区間である。

そして、そこで終わる。あるバッチサイズを超えるとステップ時間がバッチとともに伸び始め、カード上のすべてのストリームで RTF が同時に悪化する。すでに走っていて、さっきまで問題なかったものも含めてだ。曲線はなだらかに壁へ近づくのではない。平らで、そして急に平らでなくなる。その膝の位置はモデルサイズ、系列長、KV キャッシュに割ける残りメモリで決まる。

つまりベンダーの数字を正直に書き直すとこうなる。空のカードで、同時実行数 1 のとき RTF 0.15。モデルの体格はわかる。1 枚に何人乗るかはわからない。

飽和はどんな音がするか

ここが音声とチャットが本当に分かれる地点であり、LLM 配信のやり方をそのまま持ち込めない理由でもある。

テキストの LLM エンドポイントが膝を越えても、起きるのは全員のトークンが少し遅く届くことだけだ。UI は流れ続けるし、答えは最後まで返る。ほとんどのユーザーの認識は「今日は少し重い」で止まる。劣化は比例的で、耐えられる。

音声の下には硬い閾値がある。再生がいったん始まったら、クライアントは 1 秒につき 1 秒ぴったりの速度で音声を消費していく。生成はその前を走り続けなければならず、遅れればバッファは干上がる。線を越えたときに得られるのは「少し遅い音声」ではなく、単語の途中で入る途切れだ。聞き手はそれを「混んでいる製品」ではなく「壊れた製品」として受け取る。チャットのユーザーなら気づきもしない 20% の減速が、実時間の 1.3 倍で走っていたストリームを 1.0 倍未満に押し下げる。

しかも被害は 1 人では済まない。continuous batching では、限界で入ってきた 1 本が同じバッチのすべてに課税する。膝を越えさせた当のユーザーは無事で、いま文の途中にいる全員が壊れる音を聞く。新しい負荷が既存セッションの後ろに並ぶのではなく既存セッションを傷つけるというこの逆転が、音声配信とテキスト配信の運用上いちばん重要な違いだ。

本当に必要なキャパシティ指標

だからスループットは狙う先として間違っている。設計の土台にすべき数字はこうだ。チャンクあたりの生成速度が p99 で見てなお実時間再生を上回り、かつ余裕がある最大同時実行数。

測るのに半日もかからない。

  1. 代表的なワークロードを選ぶ。実際の文長、実際の参照音声、本番で使っている声。系列長のロングテールはここで効く。KV キャッシュがそれに比例して伸びるからだ。
  2. 同時実行数を 1、2、4、8、16 と上げていく。各段で定常状態に入るまで維持する。
  3. 各段で、全ストリームのチャンク到着時刻を記録し、実時間 1 秒あたり何秒の音声が届いたかをストリームごとに、平均ではなく p99 で出す。
  4. p99 が 1.2 倍を割る同時実行数を見つける。それが上限だ。1.0 倍で取ってはいけない。あれは崖の縁で、ネットワークの揺らぎにも、いつもより長い一文にも、GC の停止にも場所を残しておく必要がある。

結果について、たいてい二つ驚かれる。ひとつは、スループットから逆算した容量よりはっきり小さくなること。スループットが飽和するのは音質が崩れたずっと後だからだ。もうひとつは、その同時実行数では GPU の使用率が半分程度にとどまり、ダッシュボード上は無駄に見えること。それが「途切れさせない」ことの値札である。使用率を上げろと言われたとき、あなたが断っているのはこの取引だ。

優雅な劣化より、断ること

系として当然の帰結として、上限に達したら次のストリームは断る。受け入れて全員で遅くなる、をやらない。

これはテキスト配信の直感と正反対だ。あちらではもう 1 リクエスト受けて痛みを分散するのがたいてい正しい。だがストリーミング音声では、痛みの分散とは「断られたセッション 1 本」を「聞いてわかるほど壊れたセッション 10 本」に両替する行為である。接続時に断られるのは回復可能だ。リトライもできるし、代替の声に落とすことも、待ち順を示すこともできる。一文の 9 秒目で途切れるのは回復不能で、しかもユーザーが覚えているのはそちらだ。

実装としては、推論エンジン内部のスケジューラに任せきりにせず、モデルの前に本物のキューと明示的な同時実行上限を置くことになる。そして見るべき信号を間違えないこと。continuous batching では、音質が崩れていく間もエンジンのキュー深度はゼロのままである。何も並んでいないからだ。全部受け入れて、全部が遅くなっている。キュー深度はユーザーが苦情を言い始める瞬間まで健康に見える。電話が鳴る前に動く指標は、p99 のチャンク到着間隔のほうだ。

同じ根から出てくる小さな罠が二つ

同居は期待どおりには動かない。 LLM 推論エンジンは起動時に VRAM の大部分を KV キャッシュとして先に確保するのが普通なので、1 枚のカードにチャットモデルと TTS を載せるのは交渉ではない。先に起動したほうが要求どおり取り、後発は起動に失敗するか延々スラッシングする。地味な話だが、これでチームは 1 日を失う。しかもたいてい、その 1 日はローンチ当日だ。リアルタイム音声には専用のカードを与えるか、メモリ比率を明示的に固定したうえで、必要になる前にその組み合わせを試しておくこと。

ASR も同じ性質を受け継いだ。 自己回帰デコーダを通すストリーミング認識にも、バッチ依存のステップ時間はそのままある。加えて TTS にはないつまみがひとつ増える。各チャンクが出力前にどれだけ右文脈を見るか、だ。文脈が多いほど精度は上がり、遅延も増える。そしてこの設定は負荷と干渉する。遅れて届いたチャンクは、すでに遅延予算を使い切っているからだ。パイプラインが ASR → LLM → TTS なら、三段は同じ故障モードを共有することになり、エンドツーエンドの予算は独立に測った三つの平均の和ではなくなる。自分の膝にいちばん近い段が全体を支配する。

まとめ

音声モデルのトークン化は確かな品質をもたらし、同時に運用上の問いを「モデルは十分速いか」から「このカードは音が壊れる前に何ストリーム抱えられるか」へ移した。別の問いであり、答えも別だ。

モデルカードの RTF から見積もるのはやめる。同時実行数を段階的に上げながら p99 のチャンク配送速度を測り、実時間を余裕をもって上回る位置に上限を置き、そこで受付を止める。あとは出てきた使用率を受け入れる。もう一方の選択肢は、グラフの上では効率的に見え、耳の中では壊れている GPU だからだ。

この曲線を自分で抱えたくないなら、ホスティングされたエンドポイントはおおむねそのためにある。MiraEcho のストリーミング合成は無料で始められるし、同時実行の上限を正直に保つのはこちらの仕事だ。

MiraEcho で作る

ストリーミング TTS、リアルタイム ASR、音声クローンを 1 つの API キーで。無料で始められます。

無料で始める