FlowtronでJVSデータを学習
はじめに
FlowtronはFlow-baseのText to Speech (TTS)アルゴリズムである1。
今回は日本語の複数話者コーパスであるJVSコーパス2によってこの
Flowtronを学習してみた。
Flowtron
TTSは文章を入力として、音声を合成するタスクである。
Tacotron23などが有名である。
Flowtronは以下の特徴を持つ。
- Flow-baseの自己回帰生成モデル
- 文章からログメルスペクトルグラムを生成する
- 非文章情報をある程度制御した生成が可能
メルスペクトルグラムからの音声合成はwaveglowなど別の
アルゴリズムを用いる必要がある。
Flowtronの最大の特徴はFlowを用いることによる生成文章の制御である。 TTSは文章から音声を合成するが、本来音声とは話者の気分や体調にも依存するため、 同一話者においてもひとつの文章から複数の異なる音声が生成される可能性がある。 Flowtronにおいては、そういった非文章情報が異なる音声は特徴量空間の別の場所に射影されるように学習される。 生成時はFlowによって、その逆を行うことで非文章情報を制御した音声合成が可能となる。
以前検証した通り、Flowtronは生成時に異なるノイズを入力することで微妙にアクセントの異なる文章を生成できる。
JVSコーパス
JVSコーパスは 100の話者からなる日本語の音声コーパスである。
参考:
https://arxiv.org/abs/1908.06248
https://www.slideshare.net/ShinnosukeTakamichi/jvs-180982861
フリーでありながら、合計で30時間もの会話データが収録されている。
今回はこの中で話者間共通の100文章(parallel100)をFlowtronで学習した。
前処理
実装はNVIDIAの公式実装を一部修正したものを利用。
また、モデルの初期値として学習済みFlowtronを利用した。4
文章入力の前処理は以下の流れとなっている。
例 :
こんにちは、今日はいい天気です。
↓
konnichiwa , kyoo wa ii tenki desu .
前処理コード
import MeCab
from pykakasi import kakasi
import jaconv
kakasi = kakasi()
kakasi.setMode('K', 'a')
me = MeCab.Tagger ()
conv = kakasi.getConverter()
def convert_romaji(txt):
arrays = [i[1] if len(i[1]) > 0 else i[0] for i in [t.split("\t") for t in me.parse(txt).split("\n")] if len(i) > 2]
return " ".join([jaconv.hira2kata(a) for a in arrays if a is not None]).replace("、", ",").replace("。", ".")
def japanese_alphabet(jpn_text):
kana = convert_romaji(jpn_text)
kana = conv.do(kana)
return kana
音声側の前処理はFlowtronのデフォルトをそのまま用いている。
結果
メルスペクトルグラムから音声の合成には公開されている学習済みのwaveglowを用いた。
以降sigmaはFlowtronのノイズ分布の分散を決めるパラメータとする。
話者比較
話者ごとに共通の文章を生成して比較する。
読み上げ文章「こんにちは、今日はいい天気です。」
sigma=0.2で推論。
jvs05
jvs10
jvs21
jvs26
4話者のメルスペクトルグラム
話者を判別できる程度には異なる音声が生成できている。
元の話者音声と比較するとある程度近いがやや印象が異なった。
(英語で学習されたwaveglowを復元に利用していることが原因の可能性もある。)
ノイズの分散と非言語情報
Flowtronはノイズと文章、話者情報を入力としてメルスペクトルグラムを生成する。
sigmaパラメータはFlowtronへ入力するノイズの分散を決めるパラメータであり、
この値が小さいほど安定したクオリティの高い文章が生成される。
(GANにおけるtruncationみたいなもの)
一方で、値が大きいとクオリティが低い代わりに多様な文章が生成される。
sigma = 0.8による生成結果
jvs26(seed 1)
jvs26(seed 3)
jvs26(seed 9)
10 seed 走らせて特徴的な3サンプルを選択した。
runごとにアクセントなどの非言語情報が微妙に異なっている。
(最後の音声はかんだような発音となっている。)
1 seedは完全に生成に失敗しており、sigmaを上げると不安定となることがわかる。
(sigma=0.2の場合、10 seed間でほぼばらつきはない。)
これをうまく制御できれば多様な会話ができるシステムが作れるので
おもしろそうだと思った。
話者補間
話者情報はspaker embeddingとして特徴量空間に埋め込まれる。
この特徴量空間を異なるspeakerの間で補間することにより、
中間の音声を求めることができる。
以下は「ありがとう。」という文章をspeaker_embedinngを補間しながら8回 繰り返したものである。(simga = 0.2)
jvs48 → jvs7
jvs54 → jvs67
補間中の音声もそれほど不自然でない音声が生成できている。
(2つのスピーカーの中間として適切かは不明。)
失敗例
現状学習している文章数は話者ごとに100であり、
任意の文章を生成するためには不十分である可能性がある。
ここではうまく生成できなかった文章の例を上げる。
不要な繰り返し
一部の文章で不要な繰り返しが生じる。
結果を確認した限りではこの失敗が最も多い。
痕跡残さず個人情報盗む 新たな手口のサイバー攻撃に注意を。
jvs24
特定単語の発音ミス
話者によらず特定の単語で発音に失敗している。
本気でやれば後悔することはない。
jvs54
これらの失敗が学習データがカバーできていない発音が原因なのか、
under-fittingが原因なのかは不明である。
(英語で学習したモデルをfine-tuningしているのでそれに起因しているかもしれない)
まとめ
今回はJVSデータを使ってFlowtronを学習した。
ある程度多様性を持った音声データ生成ができており、
Flowの特性をうまく使った制御方法ができれば
感情表現などのより汎用的なTTSが行えるのではないかと思った。