Monthly Hacker's Blog

プログラミングや機械学習の記事を中心に書きます。

VQ-VAEの追試で得たWaveNetのノウハウをまとめてみた。

できること

この記事では、次のことができるようになります。

  • VQ-VAEを使った声質変換
  • WaveNetを使った音声合成を学習させる際のノウハウ

はじめに

こんにちは、dhgrsです。先日こんなツイートをしてしまい、これはイキりDNN音声合成erになってしまったと反省していました。


ちなみに、件の学会では、WaveNet関連の発表で(私も含め)「何か知見がありましたら教えてください」祭りでした。(真実かどうかはこの記事を通して考えを述べますが)膨大な計算資源が必要と言われるWaveNetなので、なかなか手を出せない方が多く、情報が集まらないのではないかと感じました。そこで、この記事では初めてWaveNetを扱う方(深層学習に詳しくない音声合成研究者/音声合成に詳しくない深層学習研究者)に向けて私がVQ-VAEの追試で得たWaneNetの知見を公開します。なお、mixture of logisticsやParallel WaveNetはまだ追試ができていないため、WIPとします。

「膨大な計算資源が必要」に対する回答

Noだから、みなさんどんどんWaveNetを試そう、と言いたいのですが、正直Noとは言い切れない、くらいの肌感覚です。例として、VQ-VAEをTitan Xで丸2日ほど(160k iteration)学習させた音源です。上から順に入力音声、同話者で復元(reconstruction)、別話者で復元(voice conversion)です。


音質がかなり悪いですが、ロスはまだまだ下がり途中なので、時間を掛ければもっと良くなります。とはいえ、2日掛けてこの程度です。

しかし、これがWaveNetの学習速度かというとそういうわけでもありません。試しにメルスペクトログラムを入力とするWaveNetを学習させてみたところ、同じネットワークのパラメータでも1日で十分な品質を得られました。VQ-VAEだと入力となる量子化ベクトルも学習対象であるため、時間が掛かるようです。

また、Parallel WaveNetの論文では1000k iteration学習したとあります。しかもバッチサイズ32と書いてあります。これはサンプリング周波数24kHzだからということもあると思いますが、このレベルになってくると計算資源を用意するのはかなり困難かと思います。

改めて結論としては、サンプリング周波数などプロダクトレベルの品質を求める場合の計算量は膨大で、追試するのも難しい、一方で通常のWaveNetであれば個人でも追試できるレベルで、VQ-VAEなど組み合わせる手法によっては個人だと少々苦労するくらいです。

それでは、WaveNetのTIPSを紹介していきます。

前処理

色々と試行錯誤をしていたなかで、最も重要だったのが前処理でした。やはり機械学習は、前処理に始まり、前処理に終わる、ということですね。前処理は3種類行いました。

  • 8bit mu-law変換
  • 無音区間の除去
  • 振幅の正規化

順に説明していきます。まず8bit mu-law変換です。これは論文にも書いてある通りで、特別なことをしていません。単純に8bitにすると、いわゆるファミコン音楽の音になってしまうので、mu-law変換という変換をしてから8bitにします。こうすると、逆mu-law変換した際にまあまあな品質を維持できます。

続いて、無音区間の除去です。これは非常に重要です。多くの音声合成用データセットでは、発音の前後に無音区間が結構長く含まれています。そのため、無音区間を除去せずランダムに切り抜いてくると、学習に用いるデータの多くの部分が無音区間になってしまいます。特にVQ-VAEですと、量子化ベクトルの大部分が無音区間の僅かな差分を表現するために使われるせいか、散々な品質になりました。ちなみに、今回試したVCTKやCMU ARCTICといったデータセットでは、librosaのeffects.trimでtop_db=20くらいで結構きれいに無音区間を除去できます。逆にデフォルトのtop_db=60だと無音区間がかなり残ります。

最後に、振幅の正規化です。データセット内に振幅が1/-1になるデータが少ないと、学習が難しいだろうと思い、正規化しておきました。mixture of logisticsの場合はやらなくても問題ないかと思います。試していませんが、8bit mu-law変換で正規化しなくても問題ないかもしれません。

ネットワーク構造

WaveNetで1番信頼がおける実装は、Magenta実装かと思います。WaveNetを発明したGoogleがやっているプロジェクトですので、参考になります。また、最近だと山本りゅういちさん(@r9y9)が実装されたpytorch実装はとても読みやすいコードで、初めてWaveNetを使う方はまず見てみることをオススメします。さらに、このツイートで紹介されているスライドでも、WaveNetの詳細が紹介されています。ResNetなどのメジャーなネットワークと違い、公式実装が公開されていないため、詳細は不明な点がいくつかありますが、Parallel WaveNetの論文ではこれまでに公開された論文と比べてかなり細かくパラメータが記載されていたので、参考になるかと思います。

これらの実装に対して言えるのは、層を重ねるごとに\frac{1}{\sqrt{2}}倍するなどマイナーチェンジはありますが、層の数やチャンネル数などのパラメータのほうがずっと重要で、学習速度や生成結果に強く影響するということです。まずはチャンネル数の知見を紹介します。

チャンネル数

この記事では、私のVQ-VAE実装に合わせて、residual channels/dilated channels/skip channelsの3つのパラメータについてまとめていきます。それぞれ、次の隠れ層のチャンネル数を表しています。

  • residual channels
    • 各residual blockの入力/出力チャンネル数
  • dilated channels
    • dilated convolutionの出力チャンネル数で、2つにsplitしtanh/sigmoidをそれぞれ適用したのち、要素積を取ってdilated channels/2を出力チャンネル数とする
  • skip channels
    • skip connectionで足す際のチャンネル数

1iterationあたりの計算時間に大きく影響するのはresidual channelsとdilated channelsで、この値を256や512など大きい値にすると、計算時間が長くなります。一方でskip channelsは1024など大きい値にしても、そこまで計算時間は長くなりません。定量的に測定していなくて申し訳ないです。

計算時間だけでなく、音声合成の質について。CMU ARCTICやVCTKなど比較的大規模なデータセットで学習させる際には(一般的なGPUに載るネットワークでは)ほとんど過学習しないので、チャンネル数も大きくしたほうがロスが下がる傾向にあるようです。ただし、ロスの値と合成される音声の質は必ずしも対応関係にあるわけではないようです。目安として、ロスが2.5を切るとノイズが減ってきます。ただ、それよりもロスが下がってくると、実際に合成して音を聞かないと判断できないです。

また、単位時間あたりの音声合成の質の観点でチャンネル数の大小を比較すると、(前述のロスが2.5を切った以降を比較する限り)あまり大きな差はないのではないかと思っています。ただ、VQ-VAEで言えば音素情報が維持され、話者情報が与えたものになっていることが確認できる程度の音質を確保するまで(つまりロスが2.5より大きい、だいたい3.0前後)はチャンネル数が小さいほうが早いようです。

個人的には、論文の追試などちょこっと試したいときは64/64/256で、論文を発表したり、サービスに取り入れたり質が重要なときは256/256/512が良いかと思います。

レイヤー数

Google関係者の論文ではdilationが1~512の10層を3回繰り返す構造が多く出てきます。これを例によって私のVQ-VAE実装に合わせてn_layer=10/n_loop=3と表すことにします。baiduのDeepVoice系ではn_layer=6/n_loop=4なども使われています。DeepVoice系の論文では、このあたりのパラメータを色々変えた際の違いについても言及があるので、興味がある方は読んでみるとよいでしょう。私の実装ではn_layer=10/n_loop=4としています。これはチャンネル数を小さくした分、パラメータ数を増やしたいという発想ですが、n_loop=3でもうまく学習できると思います。

ロスと音質の関係

これまでにもいくつか例を挙げてきたロスと音質の関係についてです。これは前処理によって大きく変わってきます。前処理をせず、無音が多い(つまり簡単な)データで学習をすると、ロスは2.0を切るあたりから、WaveNetらしい高音質な生成ができるようになります。(前処理なしをまともに試していないため、本当に2.0あたりか検証はしていません)前処理をした場合、ロスが2.1を切るあたりが高音質の基準の1つかと思います。

VQ-VAE特有の知見

学習を続けると、WaveNetのロスはどんどん下がっていきますが、ベクトル量子化のロスは途中から大きくなっていきます。そしてそのまま大きくなり続けると、合成された音声の音素情報がでたらめになります。本当はこの現象が起きないようなパラメ―タを見つけようと思ったのですが、無理でした。一応、途中でencoderとベクトル量子化層の更新を止めてWaveNetだけ学習するようにするとうまく学習を続けられて高音質な声質変換ができることを確認しています。

さいごに

色々と知見をまとめてみましたが、WaveNetに関しては私もまだまだ分からないことだらけですので、ぜひ読者の皆さんの持っている知識も共有してもらいたいです。ブログのコメントやtwitter、githubのissueなどお待ちしています。