ChainerでWaveNetによる音声合成のチュートリアルを書いてみた
Introduction
このチュートリアルではWaveNetを使ったボコーダーにより人の音声を合成します。実行できるチュートリアルは最下部にあります。
ボコーダーとは、音声をパラメータ化した入力を元に音声を合成することです。例えば、ロボットボイスは、人の音声からその発話の特徴量をパラメータとして取得し、それを元にロボットの音色の音声を合成しています。そのため、音声合成で一般的なText-to-Speech(TTS)のように文字列を入力とするのではなく、音声を入力として音声合成します。
WaveNetは、生の音声波形を生成するDeep Neural Networkです。 上記の画像のように、音声波形はあるサンプリングレートで毎時刻の値で波形が保存されていますが、それをWaveNetは生成します。また、今回はボコーダーということで、ランダムに音声を生成するのではなく、入力した音声と似た発話の音声を合成できるようになっています。どいういうことかというと、音声をパラメータ化してWaveNetの入力として使うことで、生成される音声に条件付けできるようになっています。
このチュートリアルでわかることは以下の通りです。
- WaveNetの概要
- Chainerを使ったWaveNetによる音声合成の実装
- 音声データの前処理方法
- 独自前処理を含んだDatasetの定義方法
- WaveNetの実装方法
WaveNetとは
WaveNet[1]は、van den Oordらによって2016年に提案された手法で、生の音声波形を生成するDeep Neural Networkです。同じくvan den Oordらによって提案されたPixcelCNN[2]は、数千ものの画素数の画像を精緻に生成できる手法ですが、これを音声に応用したものです。音声は以下のような特有の特性を持ちますが、PixcelCNNを応用したWaveNetは当時のstate of the artな精度を発揮しています。
- 音声は、最低でも毎秒16,000以上のサンプルを含む
- 音声は、長短含め様々な時間スケールで重要な特徴を有する
- 音声は、2次元ではなく、1次元の時系列データである
PixcelCNNと同じように、WaveNetは音声波形全体の生成確率を、それぞれの音声サンプルの生成確率の積としてモデリングしています。
\begin{align*} p({\bf x}) = \prod_{t=1}^T p(x_t| x_1, ..., x_{t-1}) \end{align*}
ただ、単にPixcelCNNを適用しただけではなく、WaveNetを提案する上で以下のような工夫もされています。
- 単なるCausal Convolutionではなく、Dilated Causal Convolutionを使っていること
- 回帰問題としてではなく、識別問題としてLoss関数を計算し、最適化すること
- Residual層とスキップコネクションを使っていること
- 他の補助的な入力を使って、生成される音声に条件を付けられること
Dilated Causal Convolution
WaveNetの大きな違いの1つに、単なるCausal Convolutionではなく、Dilated Causal Convolutionを使っていることが挙げられます。そもそも、Causal Convolutionは以下のようなネットワークで、これを使うことでの生成を行うのに過去のデータにのみ依存するようにできます。
[1]より
ただ、上記のネットワークだと出力に影響を与える入力は5(=#layers + filter size -1)しかありません。そのため、音声のような長期の関係性があるデータを生成するためには隠れ層を相当な数増やさなければなりません。
その解決策として、下記のようなDilated Causal Convolutionを使用します。
[1]より
Dilated Causal Convolutionはフィルタをあてるときに、入力をいくつかスキップします。その結果、出力に影響を与える入力を効果的に過去までさかのぼって使うことができます。
識別問題として最適化
実数連続値を出力とする問題の場合、通常は回帰問題として解くことが多いですが、WaveNetでは識別問題として解いています。 なぜなら、識別問題で使用されるsoftmax分布はその分布の形に一切の仮定がないため任意の分布をモデルでき、結果として精度が良い場合があるからです。
ただ、音声の場合各サンプルが16bitで表現されることが多く、そのままだと16bit=65,536クラスの識別問題として解かなければなりません。 さすがにこの数の識別問題は解けそうもないため、以下のようなµ-law変換[3]を用いて8bit=256に量子化します。
\begin{align*} F(x)=sgn(x) \frac{\log(1+\mu |x|)}{\log(1+\mu)} \end{align*}
これなら画像程度の量子化数になるので扱いやすい問題になります。
Residual層とスキップコネクション
WaveNet全体としては、以下のようなネットワークを使用しています。
スキップコネクションを使うことで収束を高速化します。
Conditional WaveNet
各Residual Blockの入力に補助的な特徴量を追加することができます。
[4]より
すべてのResidual Blockに同じ値を入力する場合、global conditioningといいます。例えば、発話者のidを入力することで、その発話者独特の音声を生成することができます。また、Residual Blockに時系列データを入力する場合、local conditioningと言います。今回のチュートリアルでは発話者のスペクトログラムを入力することで、発話者の細かい音色や内容を生成することができます。
Chainerを使ったWaveNetによる音声合成の実装
以下のように、Colaboratoryで実装の説明を行っています。ブラウザさえあれば今すぐにGPU環境でチュートリアルを動かすことが可能なので、お試しください。「Show on Colaboratory」のリンクをを押すとColaboratoryで開くことができます。
Synthesize Human Speech with WaveNet — Chainer Colab Notebook 0.0 ドキュメント