以下の記事で StableTTS をつくよみちゃんコーパスで fine tuning をしてみました。
今回は fine tuning の土台になるベースモデル側を改善するために、日本語の演技音声コーパス moe-speech を使って 378 時間の継続事前学習を行い、本家モデルとの比較とつくよみちゃんでの fine tuning までやってみます。
以下がデモ動画になります
初めに
StableTTS は flow-matching と DiT を組み合わせた約 31M パラメータの軽量 TTS モデルで、単一チェックポイントで中国語・英語・日本語に対応しています。話者 ID を使わず、reference encoder が参照音声から話者性を抽出するゼロショット方式です。
本家 v1.1 のチェックポイント(中英日あわせて約 600 時間で学習)を日本語で評価したところ、以下のような印象でした。
- 発音・イントネーションはそこまで悪くない
- ただし表現力(感情の乗り)が低い
- ゼロショットの話者類似性が低く、つくよみちゃんを参照にしても「似ていると言われれば似ている」程度
本家の学習データのうち日本語は一部(数百時間規模の多言語の中の1言語)なので、日本語データを大量に追加で学習させれば表現力や韻律が改善するのではないか、というのが今回の仮説です。ゼロからの学習ではなく、本家チェックポイントを初期値にした継続事前学習で行います。
作業はフォークリポジトリで行っています。
結論
| 項目 | 結果 |
|---|---|
| 表現力 | 明確に向上(主目的を達成) |
| 長文の韻律 | 安定性が向上 |
| 読みの正確さ | 微改善(非劣化を確認) |
| ゼロショット類似性 | 378 時間でも依然弱い → fine tuning で解決 |
ゼロショットの類似性は事前学習で多少改善されましたが、事前学習後のモデルにつくよみちゃん 100 発話で 30 分 fine tuning するだけで、本人らしい声質と改善された表現力を両立できました。
開発環境
- 学習: RTX 5090 ×2
- 評価・推論: Windows 11
- Python 3.13 / PyTorch 2.8 (cu128) / uv
g2p を pyopenjtalk-plus に置き換える
本家の日本語 g2p は素の pyopenjtalk(OpenJTalk 辞書)です。フォークではこれを pyopenjtalk-plus に置き換えて、読み補正(Sudachi による同形異音語の補正や「何」の読みの ONNX 推定)を有効にしています。
# text/japanese.py labels = pyopenjtalk.extract_fullcontext(sentence, use_vanilla=False)
ポイントは学習と推論で g2p を統一することです。本家チェックポイントは素の OpenJTalk の読みで学習されているため、推論側だけ g2p を良くしても学習時の音素列と食い違ってしまいます。自前で事前学習をするなら、学習データの前処理から補正込みの g2p で通せるので、g2p 改善の効果をそのまま取り込めます(音素セットは互換なので語彙サイズは変わらず、本家チェックポイントを初期値にできます)。
前処理
StableTTS の学習は音声を事前に mel 特徴量(.pt)へ変換しておく方式です。前処理の並列数がコード内で 2 固定だったので環境変数化して、48 並列で約 20 分で完了しました(g2p 失敗は 0 件)。
PREPROCESS_WORKERS=48 python preprocess.py
学習
学習設定は以下です。モデル構成(v1.1 の 44.1kHz / 128 mel、エンコーダ 3 層・デコーダ 6 層)は変更していないので、生成されるチェックポイントは本家と互換です。
- 初期値: 本家 v1.1 の
checkpoint_0.pt(optimizer なしで配置すると重みのみロードされ epoch 0 から学習できます) - batch 32 × 2GPU(実効 64 = 本家と同じ)、lr 1e-4、cosine スケジューラ、warmup 200
- 15 epochs = 55,170 steps
NCCL_P2P_DISABLE=1 NCCL_IB_DISABLE=1 python train.py
平均 6〜7 it/s で、15 epochs が約 2.5 時間・GPU 費用約 $2.5 で終わりました。31M パラメータの軽量モデルなので、378 時間のデータでもこの程度で回ります。
ハマった点
| 症状 | 対処 |
|---|---|
| HF からのダウンロードが無進捗でハング | HF_HUB_DISABLE_XET=1 + リトライ |
| DDP 初期化で SIGSEGV | NCCL_P2P_DISABLE=1 NCCL_IB_DISABLE=1(コンシューマ GPU ホストの NCCL P2P 問題) |
| batch 64 で OOM | 長尺バケット(〜1291 フレーム)の attention がメモリを食うため batch 32 × 2GPU に変更 |
| 長尺データの 4.74% が黙って学習から除外される | バケットサンプラーの境界が 1000 フレーム上限だったため 1300 まで拡張 |
特に 4 つ目は気づきにくく、filelist の件数と実際に学習に使われる件数が合わないことから発覚しました。長さベースのバケットサンプラーを使うモデルでは、境界の外に落ちたデータはエラーにならず静かに捨てられるので、データ追加時は要注意です。
本家 v1.1 との比較
事前学習後の epoch 14 と本家チェックポイントで、同一テキスト・同一参照音声の A/B 比較をしました。参照はつくよみちゃんと、学習から除外したホールドアウト話者です。
| 項目 | 判定 | 所見 |
|---|---|---|
| 表現力 | 向上 | 感情文での演技の乗りが明確に改善 |
| 長文の韻律 | 向上 | 長文でも安定。ただしカタカナ語・アルファベットのアクセントはまだ不安定 |
| 読みの正確さ | 微改善 | 「何」の読みなど、pyopenjtalk-plus 化による非劣化を確認 |
| ゼロショット類似性 | 課題 | 多少改善 |
カタカナ語・アルファベットの弱さは g2p の未知語アクセント推定の限界で、学習データを増やすだけでは解決しにくそうです。英字→カタカナ読みのテキスト正規化を前処理に入れるのが次の改善課題です。
つくよみちゃんで fine tuning
ゼロショット類似性は事前学習では解決しなかったので、前回記事と同じくつくよみちゃんコーパス(100 発話)で fine tuning をします。今回は初期値が「日本語 378 時間で表現力を上げたモデル」になっている点が前回との違いです。
- 初期値: 事前学習後の checkpoint_14
- batch 16 × 2GPU、lr 1e-4、warmup 10
- 401 epochs(約 4,000 steps、実時間約 30 分)で loss は 2.70 → 1.44
エポック別(100 / 150 / 200 / 400)に聴き比べたところ、以下のようになりました。
| epoch | 所見 |
|---|---|
| 100 | 良好。声質はかなり本人に寄る |
| 200 | 類似性と音質のバランスが最良 → 採用 |
| 400 | 声がかすれる。100 発話に対する過学習で音質が劣化 |