Mozilla TTS の後継 coqui TTS (VITS) で日本語音声合成

主に、機械学習とかよくわからないけど、とにかく [coqui-ai/TTS](https://github.com/coqui-ai/TTS) の [v0.2.0](https://github.com/coqui-ai/TTS/releases/tag/v0.2.0) (2021/08/11) で追加された VITS 実装で TTS したい方向けのメモです（筆者がそれです）。Google Colab だけで試しています。
VITS  の公式実装 [jaywalnut310/vits](https://github.com/jaywalnut310/vits) を使ったメモは「[VITS を使った音声変換で特定話者のゆっくり化を試す](https://qiita.com/tset-tset-tset/items/93489427776f33f9d0fc)」に書いています。

# 0. coqui TTS って何

2021/03 に設立された [coqui.ai](https://coqui.ai/) が [mozilla/TTS](https://github.com/mozilla/TTS) をフォークしてメンテしている OSS です。記事「[:frog: Coqui.ai is here](https://discourse.mozilla.org/t/coqui-ai-is-here/77027)」には [mozilla/TTS](https://github.com/mozilla/TTS) メインメンテナーだった [erogol](https://twitter.com/erogol) さんが [mozilla/TTS](https://github.com/mozilla/TTS) はもうメンテせず、[coqui-ai/TTS](https://github.com/coqui-ai/TTS) に注力していく旨のコメントもあります。[python モジュールの `TTS`](https://pypi.org/project/TTS/) も [coqui-ai/TTS](https://github.com/coqui-ai/TTS) 実装に変わっており、今から [mozilla/TTS](https://github.com/mozilla/TTS) を使うのであれば [coqui-ai/TTS](https://github.com/coqui-ai/TTS) の方が良さそうです。

## 0.1. coqui.ai 設立前後のできごと
- 2020/08/11
    - 「[Mozillaが大規模リストラ。「すべてが無料だった古いモデルには結論が出た」として今後は新たなビジネスモデルを模索すると － Publickey](https://www.publickey1.jp/blog/20/mozilla.html)」
        - 原文: 「[Changing World, Changing Mozilla](https://blog.mozilla.org/en/mozilla/changing-world-changing-mozilla/)」
- 2021/03/15
    - [mozilla/TTS](https://github.com/mozilla/TTS) から [coqui-ai/TTS](https://github.com/coqui-ai/TTS)、[mozilla/DeepSpeech](https://github.com/mozilla/DeepSpeech) から [coqui-ai/STT](https://github.com/coqui-ai/STT) がフォーク
        - 原文: 「[:frog: Coqui.ai is here](https://discourse.mozilla.org/t/coqui-ai-is-here/77027)」
- 2021/04/12
    - Mozilla が DeepSpeech の開発を終了し、助成プログラムを発表
        - VentureBeat の記事: 「[Mozilla winds down DeepSpeech development, announces grant program | VentureBeat](https://venturebeat.com/2021/04/12/mozilla-winds-down-deepspeech-development-announces-grant-program/)」
        - ソース: 「[DeepSpeech update, grant and playbook - DeepSpeech - Mozilla Discourse](https://discourse.mozilla.org/t/deepspeech-update-grant-and-playbook/78569)」
    - [Mozillaが米NVIDIAと提携、音声データセット構築プロジェクト「Common Voice」を前進 | OSDN Magazine](https://mag.osdn.jp/21/04/14/122200)
        - 原文:「[Mozilla partners with NVIDIA to democratize and diversify voice technology](https://blog.mozilla.org/en/mozilla/mozilla-partners-with-nvidia-to-democratize-and-diversify-voice-technology/)」

といった時系列を見る感じ、Mozilla がメンテできなくなったソースをメンテしてる OSS、という印象です。


# 1. データの準備

「[Mozilla TTS (Tacotron2) を使って日本語音声合成](https://qiita.com/tset-tset-tset/items/7b388b0536fcc774b2ad)」のデータ準備と同様に準備しておきます。以前に作ったものを流用しているため、音声認識結果を [mecab-ipadic-neologd](https://github.com/neologd/mecab-ipadic-neologd) でカナ化 → [uconv](https://man7.org/linux/man-pages/man1/uconv.1.html) でローマ字化しただけのデータを利用しています。

# 2. データを使った学習

[Mozilla/TTS](https://github.com/mozilla/TTS) との主な違いとして、[coqui-ai/TTS](https://github.com/coqui-ai/TTS) では config management に [coqui-ai/coqpit](https://github.com/coqui-ai/coqpit) という、[facebookresearch/hydra](https://github.com/facebookresearch/hydra) の [coqui.ai](https://coqui.ai/) 版みたいのを利用しているので、学習時の設定変更などが簡単にできるようです(training スクリプトの引数に指定するとそれを読み込むなど)。

```shell
#!git clone https://github.com/coqui-ai/TTS 
!git clone https://github.com/coqui-ai/TTS -b v0.2.0
!cd TTS/; pip install -e .[all]
```

最新もしくはバージョンを指定したインストールの後に、train スクリプトを実行します。
以下のようなデータがある場合

- /content/hoge/
    - wavs/
        - 0001.wav
        - 0002.wav
        - ...
    - metadata.csv

```shell:VITSを使った学習
!mkdir -p /content/drive/MyDrive/coqui-vits/

# 初回
!python TTS/recipes/ljspeech/vits_tts/train_vits.py \
    --coqpit.output_path /content/drive/MyDrive/coqui-vits/ \
    --coqpit.datasets.0.path /content/hoge/ \
    --coqpit.datasets.0.meta_file_train metadata.csv \
    --coqpit.use_phonemes false \
    --coqpit.text_cleaner basic_cleaners

# 継続用
!python TTS/recipes/ljspeech/vits_tts/train_vits.py \
    --continue_path /content/drive/MyDrive/coqui-vits/vits_ljspeech-* \
    --coqpit.output_path /content/drive/MyDrive/coqui-vits/ \
    --coqpit.datasets.0.path /content/hoge/ \
    --coqpit.datasets.0.meta_file_train metadata.csv \
    --coqpit.use_phonemes false \
    --coqpit.text_cleaner basic_cleaners
```

上記のように [train_vits.py](https://github.com/coqui-ai/TTS/blob/v0.2.0/recipes/ljspeech/vits_tts/train_vits.py) をほぼそのまま使うだけですが、自分のデータで学習するために coqpit 引数でいくつか設定を上書きしています。`meta_file_val` を指定していないですが、内部的に `meta_file_train` をうまい具合に分割するようです。
上記はテキストにローマ字を利用しているケースですが、[coqui-ai/TTS](https://github.com/coqui-ai/TTS) 内で音素変換を行う場合、`use_phonemes` `text_cleaner` は別の指定になると思います。(変換を行う際は「[VITS を使った音声変換で特定話者のゆっくり化を試す](https://qiita.com/tset-tset-tset/items/93489427776f33f9d0fc#111-g2p-%E9%9F%B3%E7%B4%A0%E5%A4%89%E6%8F%9B%E5%87%A6%E7%90%86)」の音素変換処理部も参照ください)

![coqui.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/693216/bebaf782-328a-0967-9d90-691b3a2ca83b.png)

なお [v0.2.0](https://github.com/coqui-ai/TTS/releases/tag/v0.2.0) で試していると、学習途中 10k steps 弱で `RuntimeError: min(): Expected reduction dim to be specified for input.numel() == 0. Specify the reduction dim with the 'dim' argument.` が出る症状に悩まされました。テキストが短いデータをデータ上含めないようにして(自動で省かれるはずですが、怪しいため)試したら少し学習が進むようになりました。それでも学習を進めていくと `RuntimeError:  > Detected NaN loss - {'loss_gen': tensor(nan, device='cuda:0', grad_fn=<MulBackward0>), ...}.` が出るようになったのでデータによってはチューニングなりが必要そうです。

```shell:Tacotron2-DDCでの学習
!mkdir -p /content/drive/MyDrive/coqui-tacotron2-DDC/

!python TTS/TTS/bin/compute_statistics.py \
    TTS/recipes/ljspeech/tacotron2-DDC/tacotron2-DDC.json \
    /content/hoge/scale_stats.npy \
    --data_path /content/hoge/wavs/

!python TTS/TTS/bin/train_tts.py
    --config_path /content/TTS/recipes/ljspeech/tacotron2-DDC/tacotron2-DDC.json \
    --coqpit.output_path /content/drive/MyDrive/coqui-tacotron2-DDC/ \
    --coqpit.datasets.0.path /content/hoge/ \
    --coqpit.audio.stats_path /content/hoge/scale_stats.npy
```

今回とあまり関係ないですが、同様の構成を Tacotron2 DDC モデルで学習する場合は [tacotron2-DDC/run.sh](https://github.com/coqui-ai/TTS/blob/v0.2.0/recipes/ljspeech/tacotron2-DDC/run.sh) を参考に上記のような感じになりそうです。

# 3. 音声合成

```shell:VITSモデルを使った音声合成
 !python TTS/TTS/bin/synthesize.py \ 
     --text "souiukotomo arundanatte omoimashita." \
     --model_path /content/drive/MyDrive/coqui-vits/vits_ljspeech-*/best_model.pth.tar \
     --config_path /content/drive/MyDrive/coqui-vits/vits_ljspeech-*/config.json \
     --out_path output.wav
```

音声合成は、他のモデル同様 [synthesize.py](https://github.com/coqui-ai/TTS/blob/v0.2.0/TTS/bin/synthesize.py) を呼ぶだけで、後は [E2E モデルかどうか自動判別してくれる](https://github.com/coqui-ai/TTS/blob/v0.2.0/TTS/tts/utils/synthesis.py#L278-L279)ので簡単でした。ただ音声を聴いてみた感じ、同じデータを公式実装 [jaywalnut310/vits](https://github.com/jaywalnut310/vits) で学習させた音声が 90 点だとすると、30 点ぐらいの品質でした(割と学習が短め、30k steps ぐらいで確認したという点を差し引いたとしても)。原因がパラメータにあるのか実装にあるのかはよくわかっていません。

# 4. まとめ

[coqui-ai/TTS](https://github.com/coqui-ai/TTS) [v0.2.0](https://github.com/coqui-ai/TTS/releases/tag/v0.2.0) の時点では、VITS モデルの学習を行い合成音声なりを作る際は、若干チューニングが必要そうでした。何も考えず学習するのであれば公式実装 [jaywalnut310/vits](https://github.com/jaywalnut310/vits) を利用した方が品質的に楽そうです。
ただ [coqui-ai/TTS](https://github.com/coqui-ai/TTS) は継続的にリリースがあるので、今後 [jaywalnut310/vits](https://github.com/jaywalnut310/vits) 同等になる可能性もありそうです。将来的には [ONNX でのエクスポート](https://github.com/coqui-ai/TTS/issues/378)など他にはない機能もロードマップにあるので、今後も試していきたいです。