音質で選んだTTSが、会話には遅すぎた — RTF実測2.5倍差で「設計用」と「本番用」を分けるまで
「キャプションを書くだけで声が作れる」タイプの拡散TTSを見つけた。
"落ち着いた大人の女性ナレーターの声。ゆったりとした語り口で、
温かみと信頼感があり、長い文章を丁寧に読み上げる。"
これを渡すと、その説明どおりの声で喋る。話者の音声データを1秒も用意せずに、日本語で任意の声が出せる。しかも同じキャプションと同じ乱数種からは、何度でも同じ声が出る。
対話型のアバターに使えないかと考えた。キャラクターごとに声を用意する手間が消える。
結論から言うと、会話には使えなかった。ただし捨てるには惜しかったので、役割を変えて残した。
実測: 同一GPUで2.5倍遅い
既存システムでは学習済みのTTS(Style-Bert-VITS2系)を使っていた。これと同じGPUスライスで、同じ文(7.5秒ぶん)を合成して比べた。
| エンジン | 条件 | 生成時間 | RTF |
|---|---|---|---|
| 拡散TTS 40steps | 2g.20gb | 1.9〜2.1s | 0.25〜0.28 |
| 拡散TTS 24steps | 2g.20gb | 1.4〜2.0s | 0.19〜0.27 |
| 拡散TTS 16steps | 2g.20gb | 1.1〜1.4s | 0.15〜0.19 |
| 学習済みモデル | 2g.20gb | 0.74〜0.81s | 0.115〜0.127 |
RTF(Real Time Factor)は「生成時間 ÷ 音声の長さ」。小さいほど速い。
40stepsで2.5倍遅い。ステップ数を16まで削っても1.5倍差が残る。そして16steps以下では品質が破綻する(6stepsは聴いて即NG判定になった)。
小さいGPUスライスだとさらに開く。
| エンジン | 条件 | 生成時間 | RTF |
|---|---|---|---|
| 拡散TTS 40steps | 1g.10gb | 3.4〜4.0s | 0.46〜0.54 |
| 拡散TTS 16steps | 1g.10gb | 2.0s | 0.27 |
会話では文ごとに合成して順に再生するので、最初の文が出るまでの時間が体感を決める。1〜2秒の遅延が毎ターン乗るのは厳しい。
GPUを増やしても解決しなかった
「もっと大きいGPUを割り当てれば」と考えて試算した。2g.20gb → 7g.80gb で4倍のリソースになる。
ところが実測から固定オーバーヘッドが約1.1秒あることが分かっていた。ステップ数を12まで削っても16stepsと同じ1.1〜1.4秒で下げ止まる。これはモデルのロードやテキスト処理など、GPUの並列度では縮まない部分だ。
したがって4倍のリソースを積んでも、生成時間は 1.0〜1.2秒あたりが下限になる。学習済みモデルの0.8秒には届かない。コストは数倍。採用しないと判断した。
この「固定オーバーヘッドを先に測る」のは、GPU増強の判断で毎回効く。ステップ数を極端に減らして、それ以上速くならない点を見つけると、並列度で縮まない部分の大きさが分かる。
役割を分ける
速度で負けているだけで、拡散TTSにしかできないことがある。キャプションから声を作れるという一点だ。学習済みモデルは、学習に使った話者の声しか出せない。
そこで、こう分けた。
[設計時] 拡散TTS ── キャプション+seedで声を生成 ── 学習コーパス(約200本)
↓
学習
↓
[ランタイム] 学習済みモデル ──────────────── 実際にユーザーが聞く声
拡散TTSで「こういう声」を作り、その声で200本ほど喋らせて学習素材にする。それを学習させた軽量なモデルが本番で喋る。
拡散TTSは声を作るときにしか動かないので、遅くても問題にならない。1本の声を作るのに約70分かかるが、これはバッチ処理だ。
この分け方が成立する条件
生成が決定論的であること。 同じキャプションと同じseedから必ず同じ声が出る。これが無いと、設計した声を再現できず、コーパスを作り直すたびに別の声になってしまう。
実際、この性質のおかげで設計値の台帳だけ持っていればモデルを再現できる。学習済みモデルのファイルを失っても、キャプションとseedがあれば同じ声をもう一度焼ける。実際にドライバースクリプトを失ったことがあるが、記録からキャプションとseedを復元して同一の声を作り直せた(声をガチャで引く)。
音質が学習に耐えること。 生成音声を学習素材にするので、拡散TTS側の音質がそのまま上限になる。ここは実際に学習させて聴くしかない。今回は問題なかった。
声の同一性が保てること。 200本を生成する間、同じ声であり続ける必要がある。ここは苦労した。感嘆詞から始まる文で冒頭だけ別人になったり、感情表現を強めると話者性が崩れたりする。参照音声の使い方を工夫して解決したが、これは別の話になる。
副作用: キャプションは韻律も決める
分けたことで見えてきた制約がある。キャプションは音色だけでなく、話速や抑揚も決めてしまう。
男性のカウンセラー声を作ろうとして、こういうキャプションを書いた。
穏やかで優しい男性の声。低めでゆっくり、包み込むように落ち着いて語りかける話し方。
出てきた声が「低すぎる・ゆっくりすぎる」という評価だったので、「低め」「ゆっくり」を外してみた。
穏やかで優しい男性の声。落ち着いた中くらいの高さの声で、
相手を安心させるように丁寧に語りかける、自然なテンポの話し方。
F0は136〜159Hzまで上がった(元は105〜159Hz)。狙いどおりだ。ところが話速も5.5〜6.7 → 7.3〜7.7 に跳ね上がった。
「少しゆっくりめに」と書き戻しても6.4〜7.1までしか戻らない。F0と話速がキャプションの中で連動していて、片方だけ動かせない。
5パターン試して分かったのは、この2つを分離するのはキャプションだけでは難しいということだった。声の高さを上げると、必ずテンポも上がる。
そして話速は合成時のパラメータでは変えられない(学習後に話速は変えられないで実測を書いた)。つまり、キャプションを書く時点で声の高さとテンポの組み合わせが決まり、後から調整できない。
設計時に決めきる前提で、候補を複数作って聴き比べる運用にした。1候補あたりの生成は数分なので、5パターン×12seedを回して比較表を作るのが現実的だった。
まとめ
- RTFは同一GPU・同一テキストで測る。エンジンの公称値は条件が違うので比較にならない
- 固定オーバーヘッドを先に測る。ステップ数を極端に削って下げ止まる点を見つければ、GPU増強で縮む余地が分かる
- 速度で負けても、できることが違うなら役割を分けられる。「設計時に1回」と「ランタイムに毎回」では、許容できる遅延が2桁違う
- 決定論的な生成は、それ自体が価値。設計値の台帳でモデルを再現できるようになる
- キャプションで指定できるものと、後から変えられるものを実測で確認する。今回は話速が「指定できるが変えられない」側だった
シリーズ: 拡散TTSから実用ボイスを量産する
キャプション1行から声を設計し、学習コーパスを製造して、役割別の実用ボイスを量産するまでの記録です。この記事は第1部 設計にあたります。
(この記事がシリーズの入口です) → 次: 声をガチャで引く
シリーズ全18本
1. 音質で選んだTTSが、会話には遅すぎた ← いまここ 2. 声をガチャで引く 3. 「ナレーターっぽい声」を24候補から機械に選ばせる 4. 品質ゲートを厳しくするほど、棒読みが生き残る 5. 学習後に話速は変えられない 6. 生成するたび「録音場所」が変わるTTS 7. クリップ1本の粗さが、スタイル全体を嗄れさせる 8. AIが「こんにちわー」と伸ばす癖は、どこで身についたのか 9. 「少々」が「しょも」になる — 許可文字リストが日本語を削っていた 10. ハルシネーション対策のコードが、ハルシネーションの時だけ動かなかった 11. 品質ゲートが許した「3文字」が、モデルの口癖になった 12. 治せる欠陥で候補を落としていた 13. 文字起こしでは見つからない欠陥がある 14. 70分かけた学習素材が、ネットワークの一瞬の瞬きで消えた 15. “ja” を “JP” と書いて喃語モデルができるまで 16. 登録経路が4つ、管理画面が0 17. デプロイのたびに互いの成果を消していた 18. 測れていないものを閾値で追い込むと、必ず失敗する
知見の元になったノートは 拡散TTSから実用ボイスを量産する製造パイプライン にまとめてあります。