coqui-ai/ttsでvocoderの学習

こちらの続きとしてvocoderの学習と、生成したmelspectrogramをvocoderに通して音声生成してみる
https://jsapachehtml.hatenablog.com/entry/2021/06/16/094213

学習

前回書いた通りmultiband-melganを試してみる。データセットもJSUTで同じくLJSpeechと同様の形式にしたものを使う。

なお、コードはv0.0.13のtag時点のものをcheckoutした。理由としてはなるべく最新の状態に近いものを利用したかったが、v0.0.14以降だとconfigの実装が変更されていて少し設定方法を直す必要があって手間だったためその直前を使った。

configの設定はこちら

DATAROOT = '/content/ljspeech_structure_22050'
DATAROOT_DRIVE ='/mydrive/machine-learning/tts/data/jsut_ver1.1_ljspeech_structure'

CONFIG = load_config('TTS/vocoder/configs/multiband_melgan_config.json') 

CONFIG['data_path'] = f"{DATAROOT}/wavs/"
CONFIG['audio']['stats_path'] = None
CONFIG['output_path'] = f"{DATAROOT_DRIVE}/output"
CONFIG['num_loader_workers'] =  2
CONFIG['num_val_loader_workers'] = 1
CONFIG['test_sentences_file'] = f"{DATAROOT_DRIVE}/test_sentences_file"
CONFIG['print_step'] = 1000
CONFIG['save_step'] = 5000

# デフォルトのconfigには存在しないがこちらの2つの設定がないとエラーになるので追加
CONFIG['use_l1_spec_loss'] = False
CONFIG['diff_samples_for_G_and_D'] = False

注意点は200K stepを超えないとdiscriminatorの学習が始まらない設定がデフォルトになっていること。

学習に用いたnotebookはこちら
https://github.com/y-kamiya/machine-learning-samples/blob/master/scripts/tts/TTS_jsut_multiband_melgan_example.ipynb

学習にかかる時間

batch_size=64(デフォルトのconfigの設定そのまま)で学習を行った際の例

200Kまで(discriminatorなし)

   --> TRAIN PERFORMACE -- EPOCH TIME: 50.83 sec -- GLOBAL_STEP: 133591
     | > avg_G_stft_loss_mg: 0.63728
     | > avg_G_stft_loss_sc: 0.21645
     | > avg_G_subband_stft_loss_mg: 0.58217
     | > avg_G_subband_stft_loss_sc: 0.22271
     | > avg_G_loss: 0.82930
     | > avg_G_gen_loss: 0.82930
     | > avg_G_adv_loss: 0.00000
     | > avg_loader_time: 0.59188
     | > avg_step_time: 0.41982

evaluationも含めて1 epoch分にかかる時間は130sec程度

200K以降(discriminatorあり)

--> TRAIN PERFORMACE -- EPOCH TIME: 75.39 sec -- GLOBAL_STEP: 425051
     | > avg_G_stft_loss_mg: 0.80059
     | > avg_G_stft_loss_sc: 0.36194
     | > avg_G_subband_stft_loss_mg: 0.72644
     | > avg_G_subband_stft_loss_sc: 0.37061
     | > avg_G_mse_fake_loss: 0.31279
     | > avg_G_loss: 1.91177
     | > avg_G_gen_loss: 1.12979
     | > avg_G_adv_loss: 0.78198
     | > avg_D_mse_gan_loss: 0.44839
     | > avg_D_mse_gan_real_loss: 0.13879
     | > avg_D_mse_gan_fake_loss: 0.13842
     | > avg_D_loss: 0.44839
     | > avg_loader_time: 0.80281
     | > avg_step_time: 0.62103

evaluationも含めて1 epoch分にかかる時間は190sec程度

データのロードにかかる時間が計算にかかる時間より長い。これは教師データとなる音声データからmelspectrogramを生成しているためかも?と思ったのだが、cacheの設定はONになっているため、そうであれば一度ロードした後は速くなるはずなので違った模様。

実行時間の見方について注意だが、 EPOCH TIME: 75.39 sec というのは計算時間(step_time)のみの合計を表している。今回のデータセットだと1 epochあたり120 stepだったので以下のような時間配分になっていることがわかる(200K以降のデータを例にして)

  • 学習の計算時間: 0.621 * 120 := 75 sec
  • 学習のデータロード時間: 0.803 * 120 := 96 sec
  • 評価にかかる時間: 190 - 75 - 96 = 19 sec

学習/評価用のデータ数はこちらの設定で決まっており、デフォルトは10となっている
https://github.com/coqui-ai/TTS/blob/v0.0.13/TTS/vocoder/configs/multiband_melgan_config.json#L143

そのためかなり少ないにも関わらず10%程度の時間を使ってる。評価を数epoch毎にするようなオプションは用意されていなかったので今回はやっていないが、メインのスクリプトをちょっと修正すれば可能なのでちゃんと学習させる or 繰り返しやる場合は入れると良さそう。

tensorboardでの結果

440Kくらいまで学習してみた結果がこちら eval log f:id:y-kamiya:20210722125607p:plain

train log f:id:y-kamiya:20210722142257p:plain

colabなので途中で一旦終了して若干ずれた位置から再学習が始まることがあるため注意。

eval logが大きく振れているのはデータ数を少なくしているためだと思われる。基本的に形状はeval, trainともに同じであり、値も同程度になっていることが確認できる。自分で作ったデータセットなど使う場合は学習用・評価用のデータの性質の異なる部分が多くなるはずであるため、lossの値に違いが出てくるのではと考えられる。

生成された音声がこちら

教師データ

生成された音声だけ聞くとしっかり喋っている内容もわかるしイントネーションもあっていてなかなかよい感じに聞こえる。ただ、教師データの音声に比べると明らかに雑音が多いことがわかる。

ちなみに生成された音声というのは、上記教師データとなる音声をmelspectrogramに変換後それをmelganのgeneratorに通して音声波形としたもののこと。なので仮にvocoderが理想的な関数を学習してあれば教師データと遜色のない音声が生成されるはずといえる。

上記の音声に対するmelspectrogramがこちらで、右が教師データとなる音声のmelspectrogram、中央が生成した音声のもの、左がその差分 f:id:y-kamiya:20210722144611p:plain 差分を見ると明るくなっている部分もけっこう多くまだ違いがあることがわかる。目視で右と中央を見比べると確かに中央の方が全体的に輪郭がぼやけた感じになっているように見える。ただ、低周波数帯のしっかりした帯がある部分の形状は比較的同様の形で生成できてるように見える。

公開されている学習済みモデルとの比較

元のnotebookに書かれているが、1.45M分LJSpeechで学習したモデルが公開されている。今回は勉強のためにvocoderの学習もやってみたものの以下の理由によりLJSpeechで学習済みのモデルを使えば問題なさそうなことはわかっている。

  • 1.45Mと学習させたstepが圧倒的に多い
  • melspectrogramから音声波形への変換については言語の違いによる影響がそこまで大きくない

ということで、学習済みモデルを使った場合はどの程度になるのかを確認するため、上記に貼った自前で学習した際のtensorboardの結果と同じ教師データを使ってmelspectrogramから音声波形を生成してみたのがこちら

f:id:y-kamiya:20210807220456p:plain

教師データの音声は上で載せたものと同じなので生成したものだけ貼った。教師データとほとんど遜色なく聞こえるのがわかる。

melspectrogramはカラーバーの値が小さすぎて見えないが、真ん中は黄色の部分が2.5程度、右は黄色の部分が4程度となっているので注意。そのため色は異なるもののほぼ同程度の値となっている。それが示されているのが左の差分で、自前で学習したモデルで生成した結果と比べて色のついた部分が格段に減っている(=差がない)ことがわかる。

ということで実際に使うことを考えるのであれば、日本語だからという理由だけならvocoderは自前で学習する必要はなく公開されている学習済みモデルを使った方が良さそう。

テキストからの音声生成

glow-ttsで生成したmelspectrogramをmultiband-melganに通すことで音声を生成してみる。両者とも公開されている学習済みモデルを使う場合はこちらのnotebookを実行するだけでOKなので簡単
https://colab.research.google.com/drive/1NC4eQJFvVEqD8L4Rd8CVK25_Z-ypaBHD?usp=sharing

自前のモデルを使う場合もこれを少し修正して使える。参考までに私が使ったnotebookはこちら
https://github.com/y-kamiya/machine-learning-samples/blob/master/scripts/tts/Glow_TTS_MultiBandMelGAN_example.ipynb

上記のnotebookの中ほどに教師データから変換したmelspectrogramをvocoderに通して音声を生成する処理を追加したがこの際にハマった部分をメモしておくと、GANDatasetに渡すAudioProcessorはtts用とvocoder用で適切なものを渡す必要がある。

notebookを見ればAudioProcessorはtts用とvocoder用で別のものを生成して使っているので当然なのだが、両方のモデルに対していろいろ試している中でvocoderに対する処理なのにtts用のものを指定してしまった。生成されるmelspectrogramがおかしくなるのだがここが間違っていることに気づくのに結構な時間を使ってしまうこととなった。

JSUTで0から学習したglow-ttsのモデルと公開されている学習済みのmultiband-melganでテキストから生成した音声

LJSpeechで学習済みのモデルにJSUTで100Kほど追加学習したglow-ttsのモデルと公開されている学習済みのmultiband-melganでテキストから生成した音声

どちらもたどたどしさの残るしゃべりになっているが、追加学習したものの方が若干雑音が多いかも?というくらい。なのでもしこの程度の音声でOKなのであれば学習済みモデルに対してちょっと追加学習する方が楽に済む。ただし、さらに学習を続けた場合に違いが出るのかは気になるところ。