DeepLearning でアニメキャラのボイロを作った話 【モデル作成編 その1】

はじめに

データ収集編のつづきです。
次は用意したデータを用いて音声合成を行う Deep Learning モデルを作成する作業になります。 今回使用したモデルは Nvidia の Tacotron2 + Waveglow です。
このモデルは、例えば JSUT のデータをダウンロードしてデータの前処理をして Readme に従いモデルを作るだけで十分良いものができるのでパッと音声合成を試してみたい人にもオススメです。
(残念ながら他の手法を試したことがないので比較とかはできません。例えば、Mozilla が作ってたりもしてるようです。。今のデファクトスタンダードはどれなんでしょうか。)

音声合成モデル概要

Tacotron2 + Waveglow による音声合成の流れはこんな感じです。

f:id:tosaka-m:20200306205512p:plain:w400
音声合成モデル概要図

この図にある Tacotron2 と Waveglow の部分を用意したデータを使って学習することで音声合成モデルを作ります。 ここでモデル作成に必要なデータは図の テキストデータ波形データ です。

今回私が組んだモデルはちょっと変えてこんな感じにしてます。

f:id:tosaka-m:20200306205559p:plain:w500
音声合成モデル概要図その2

違うのは F0 カテゴリデータを入力に与えたのと、Denoising を追加したくらいです。
前処理ではテキストを MeCab, Jaconv でカタカナ直したあとローマ字に直すことをしました (juliusのマッピングを使用しました が一般的なやり方とかは知らないです。誰か教えてください。)

Tacotron2, WaveGlow, Denoising, F0 カテゴリデータ の部分を順にコメントしていきます。

Tacotron2

Tacotron2 については解説記事を書いてくださっている方がいましたので、こちら をご覧ください。(ありがたい...)
Tacotron2 がやることは、「テキスト → MelSpectrogram」 の変換です。Mel Spectrogram が何かについてはこの記事とか参考になりますかね。

Tacotron2 の学習自体はだいたいはもとのコードそのままで実行しております。
学習時の初期値には Nvidia の公開しているモデル を使用して Fine tuning しました。
他にも細かいハイパーパラメータの設定とかありますが、細かい上に特に違いとかも出なかった気がするので省略します。

余談ですが、Tacotron2 の学習にはどれくらいデータが必要なのか? とういう問いの一つの答えとして、学習データ量と精度の関係性について結果を載せているこの論文のプロジェクトページ が参考になるかなと思います。
モデル自体は Nvidia の Tacotron2 でなく Waveglow でも使っていないですが、類似モデルの話ではあるので Tacotron2 + Waveglow でもそこまで変わらないのではと思っております。

私が使用したデータは計1時間くらい(ただしノイズ多め)ですが、印象としてはここの Data requirements of the baseline Tacotron の 1 shard (24min) ~ 3 shard (72min ちょい) くらいの結果に近そうかなと勝手に思っています。
ここのプロジェクトページの結果をみると、そこそこのモデルなら30分くらいあれば良さそうですし、質を求めるなら 10 時間分くらいは欲しくなりますね...データの足りなさはより良いモデルを使ったり色々工夫したりで埋められる部分はあるかと思いますが。

WaveGlow

Waveglow は音声特徴量から波形データを生成するモデルです。このようなモデルは Vocoder と呼ばれ、 他に有名なものとしては、WaveNet とか World とかがあります。私は詳しくないですが、他にも色々あるようです。
NEUTORINO は 製作者 Tweet を拝見する限り NFS ライクなモデルを使っているっぽいんですかね?

こちらも学習時の初期値には Nvidia の公開しているモデル を使用して Fine tuning しました。

色々試した訳ことがある訳ではないのであれなんですが、Waveglow は自分のデータで学習させてみると iteration が増えるにつれノイズが乗るようになったり、ノイズが乗っていない iteration のモデルを使っても多少音質が劣化してしまったりと結構微妙かなと思ったり思わなかったりしています。
学習がちょっと難しいかもしれません。一応学習なしよりはよくなります。
時間が無限にあれば他の Vocoder も色々試して聴き比べてみたいです。

Denoising

Tacotro2 で生成した Mel Spectrogram をちょっと綺麗にすることをします。
作業初期であまりデータを作っていなかった時のちょっと悪いモデルだと結構効果がありましたが、データを増やした後 Tacotron2 のみである程度精度が出るようになるとあまり効果はなくなったかもしれません。
こんな感じに変わります。

データが少ない時

  • Denoising 前

  • Denoising 後

データを増やした時

  • Denoising 前

  • Denoising 後

あんまり変わらないですかね?

今回使用した Denoising の方法はこの方の結果を参考にさせていただきました。この方のどこかの解説記事で書かれていたと思いますが、やっていることとしては画像界隈の Image-to-image translation や SuperResolution と似たような感じだと思います。
この Denoising 機構を入れずに Tacotron2 単体で学習できれば理想なのですが、モデルの構造を深くしようとすると GPU メモリーに限界があったりなどどうしようも無い要素があったりします。特に Tacotron2 の学習は FineTuning 無しかつ BatchSize を小さくしすぎる (10 未満とか) と全然うまく学習しないとかあった気がするので (うろ覚え)、あまり Tacotron2 自体はいじらずに置きたかった事情もありました。

試した方法としてはこの2つです。

最初のほうでデータが少ない時は UNet + GAN を使用していましたが、これでかなり結構ノイズが減って 「おおっ」 となりました。
ただ、適切なハイパーパラメータがあまり分からず学習は安定しないことが多かったです。短い音声に適用すると逆に悪くなったりとかもありました。GAN の学習は難しいですね。

暫くしてデータを増やした後は RCAN を使用していました。
RCAN は SuperResolution 関連の論文なのですが、最近の SuperResolution は GAN でやるものとばかり勝手に思っていたので RCAN の論文を見ると L1 loss のみ学習していたのはちょっと驚きました(ので使って見ることにしました)。
GAN のチューニングのめんどくささもなさそうなのでとりあえずやってみたら、多少効果があってそこそこまぁまぁだったので最終版ではこちらにしてます。
どちらが優位かとかは軽く聴き比べたくらいだと一長一短で分からないです。

ちなみに Waveglow 自体にも予測の bias を除くことによる Denoising 機構があり上記サンプルは全てそれも使用しています。
(Waveglow の Denoising はざっくりと述べると bias = 「0 の値の mel spectrogram の Waveglow による予測値」 と定義し、audio のパワースペクトルから bias のパワースペクトル * 0.1 を除く処理です。)

F0 カテゴリデータ

入力に F0 を入れることで生成する音声の高さをコントロールできるようにしました。(あまり綺麗に変わらないですが...)
F0 が何かについては例えば wiki とか。
F0 の入力のために UI をこんな風に作ってます。

f:id:tosaka-m:20200306161229p:plain:w350
F0 入力フォーム

この UI では 5 段階で F0 を変えられるようにし、 Neural Net の入力にも 1 ~ 5 のカテゴリ値を入力として与えております。
普通 F0 は連続値なのですが、F0 の分布を 20% 点区切りの 5 つに分けることでカテゴリ化しています。

f:id:tosaka-m:20200306211834p:plain:w500
F0 カテゴリ化

F0 の値の算出には pyworld の harvest 関数を使用しました。

そもそもどうしてカテゴリ化したかというと、先に UI の構想があり、この UI で音程をコントロールしてみたいなと思ったのでそれを実現するためのモデルを考えた結果です。

F0 の値を組み込む方法はこんな感じにしました。なんでもよかったので、Tacotron2 の Encoder の部分をそのまま流用することで最小限のコードの変更で済ませてます(??)。

f:id:tosaka-m:20200306161427p:plain:w500
F0 値組み込み方法

F0 に関しては色々モデルを試したりとかは全くしてないのですが、 F0 をカテゴリ化せず直接入れた方がより細かい情報を与えられるため良いかもしれないし、 F0 を入れる NN の構造をもっと単純にする (例えば CNN, RNN を入れずに前後の関係性を無視するようにする) ほうが綺麗に音程が変わるかもしれません。

また、副次的な効果として精度も多少向上しました。
今回使用したデータは同じようなテキストでも声の高さ(キャラクターのテンション)が全然違うことが多々あります。 そのため、テキストだけでは音声の高さが判別できないので、高さ情報もいれてあげないと適切な予測ができない恐れがあります。(高いデータと低いデータで平均する過剰なスムージングがかかってしまう気がします。) そのため、F0 を入力に入れることで高い声を生成すればいいか、低い声を生成すればいいかある程度判断がつくようになったのが良い影響を与えたと思っております。

おわりに

使ったモデルと構造に関してはだいたい以上です。
まだ他に試したこととかもあるので次回は効果のあった手法とかなかった方法とか、あとは今後やりたいこととかをまとめたいと思います。