Fusic Tech Blog

Fusion of Society, IT and Culture

簡単に作れるTTSモデル:ESPnetを用いたつくよみちゃんTTSモデル作成
2021/09/10

簡単に作れるTTSモデル:ESPnetを用いたつくよみちゃんTTSモデル作成

前回「複雑な音声処理タスクを一気通貫で実装できる! ESPnetの概念と特徴について」記事で、ESPnetの特徴を説明し、その再現性・利用性に関して述べました。 今回、実際にESPnetを用いて、TTSモデルを作成してみましたが、そのプロセスについて説明していと思います。

目的

  • 数少ないデータセットを使用し(100個、総合で約15分)
  • ESPnet TTSファインチューニングモデルを作成

学習方法

1.「テキスト→音響特徴」の変換を行う、音響モデルのファインチューニング

Pre-trained モデル:JSUTコーパスで学習した、Tacotron2モデル

2.「音響情報→音声ファイル」の変換を行う、Vocoderのファインチューニング

Pre-trained モデル:JSUTコーパスで学習した、PWG(Parallel WaveGAN)モデル

結果

つくよみちゃんコーパスでファインチューニングしたTTSモデルを貼っておきます。

ESPnet TTS Recipeの流れ

まず、ファインチューニングの仕方を説明する前に、基本的なESPnet TTS Recipeについて説明します。
前回の記事で説明したように、Recipeはステージというタスクを順番に定義したものを指しますが、TTSのRecipeは次のような流れを持ちます。

  1. ステージ①:データダウンロード及び前処理
    データセットをダウンロードし、Kaldiスタイルデータ形式作成などの前処理を行います。
  2. ステージ②:特徴抽出
    使用する音響モデルに従い、必要なデータ特徴を抽出します。
  3. ステージ③:発話フィルタリング
    短い発話や長い発話を除去します。
  4. ステージ④:Tokenリスト作成
    テキスト情報のTokenizationを行います。日本語の場合は、主に

    • jacovを使用した「テキストNormalizing」や、
    • OpenJTalkを活用した「文書→音素単位」の分解を行います。
  5. ステージ⑤:TTSモデルの統計量計算
    ESPnetでは動的Batchを使用しており、各データのサイズを参考し、学習Batchサイズを調節します。このステージでは

    • 学習データの統計量を計算し、
    • データのシェイプ情報や音響特徴量の平均及び分散を抽出します。
  6. ステージ⑥:TTSモデルの学習
    「テキスト→音響特徴」の変換を行う音響モデルの学習を行います。ESPnetでは、Tacotron2やFastSpeechなどのTTSモデルを支援しています。
  7. ステージ⑦:Vocoderを用いたデコード
    Vocoderで「音響特徴→音響ファイル」の変換を行います。ここでは、

    • 既に学習されているVocoderも使えますが、
    • 新たに学習したVocoderを使うことで、音質を高めることができます。

上記Recipeの詳しい内容についてはESPnet Githubで確認できます。
今回は「新たなデータセット」でTTSモデルをファインチューニングしますので、いくつかの工夫が必要です。

つくよみちゃんデータセットを用いたTTSモデル

新たなデータセットを使ったファインチューニングRecipeを作成するために、次の3つタスクを行いました。

  1. つくよみちゃんデータセットの準備(ステージ①)
  2. Pre trained TTSモデルを用いた、音響モデルのファインチューニング(ステージ⑥)
  3. Pre trained Vocoderモデルを用いた、Vocoderファインチューニング(ステージ⑦)

ステージ②から⑤までは、既存TTS Recipeをそのまま使えます。私の場合は、ステージ④のOpenJTalkオプションを変換し、Pauseラベル(「,(コンマ)」のような記号に対応)を参考した音素分析を行うようにしました。

 e.g. こ、こんにちは
  pyopenjtalk_kana
   -> [コ, 、, コ, ン, ニ, チ, ワ]
  pyopenjtalk_accent
   -> [k, 1, 0, o, 1, 0, k, 5, -4, o, 5, -4, N, 5, -3, n, 5, -2, i, 5, -2, ch, 5, -1, i, 5, -1, w, 5, 0, a, 5, 0]
  pyopenjtalk_accent_with_pause
   -> [k, 1, 0, o, 1, 0, pau, k, 5, -4, o, 5, -4, N, 5, -3, n, 5, -2, i, 5, -2, ch, 5, -1, i, 5, -1, w, 5, 0, a, 5, 0]

上の例は「こ、こんにちは」というテキストが、OpenJTalkオプションによって、どのように分解されるかを表しています。(参考リンク)この音素分析の結果によるTTSモデルの性能差を確認してみます。

  • Pauseラベルを使用しない場合
  • Pauseラベルを使用する場合

これで、文書の中のコンマなどをしっかり認識し、より人間っぽい音声合成を行うのではないかと期待します。
それでは、上記した3つのタスクについて詳しく説明します。

「ステージ①」のタスク

データセットダウンロードと前処理に関する作業を行うステージで、Recipeの「./local/data.sh」で管理されています。ダウンロードしたファイルの構成を確認し、学習に必要なファイルを取ってくる作業や、ESPnetで読み込める形式で変換する作業などが必要でした。コーパスによって、音声ファイルの形式や音質(ノイズなど)が違いますので、一番手前のかかるタスクだと思います。

  1. Download
    つくよみちゃんコーパスのダウンロードリンクを設定し、学習に使う音声ファイル(*.wav)とテキストファイル(scripts.txt)をコピーします。
  2. Trim_Silence
    各音声ファイルの前後にあるSilenceを除去するタスクです。発話中のPause(記号によるPauseや発話の自然なスキマ)ではないSilenceは、音響情報とテキスト情報のAlignを邪魔するので、TTSの性能向上のために必要な作業です。
    ESPnetでは、thresholdデシベル以下の音を感知し、発話の始まりと終わり時間を抽出することができます。(trim_silence.sh)
  3. Subset_Data
    Train/Validate/Evaluateに分けてKaldiスタイルデータを生成します。

ESPnetのライブラリーで基本的な作業は処理できますが、データセットによって追加タスクが求められると思います。 実際に、私の場合は次のタスクも必要でした。

wavファイルの読み込み問題

trim_silence.shでは、waveモジュールを用いて、音声ファイルの読み込みを行いますが、つくよみちゃんコーパスの読み込みの祭、エラーが発生しました。音声ファイルの形式に問題がありましたので、次のような形式変換を行いました。

  for wav in onlyfiles:
     data, sr = sf.read(f'{path_temp}/{wav}', dtype='float32')     ## Read original *.wav file with soundfile module

     filepath = f'{path_temp}_new/{wav}'                           ## Write converted *.wav file
     _format = "WAV"
     subtype = 'PCM_16'
     sf.write(filepath, data, sr, format=_format, subtype=subtype)

今回は、上記の作業をJupyter Notebookで行いましたが、この作業をシェルスクリプトで作成し、ステージ①に追加するとより完成度の高いRecipeになると思います。

「ステージ⑥」のタスク

音響モデルをファインチューニングするステージで、学習済みの音響モデルが必要です。ESPnetで提供するPre-trainedモデルを利用することもできますが、私は次の条件で学習したモデルをファインチューニングしました。

  • データセット:JSUTコーパス
  • 音素分析:pyopenjtalk_accent_with_pause
  • 学習モデル:Tacotron2

ファインチューニングするためには、Pre-trainedモデルの学習情報が必要です。ステージ④と⑤から生成された「Tokenリストとデータ統計量」は、新しいデータセットの情報で、Pre-trainedモデルの学習には使用できません。従い、下の2つのファイルをPre-trainedモデルの情報に変更します。

  • token.txt:Tokenリスト
  • feats_stats.npz:データ統計量

上の設定が終わったら、ESPnetのTTS Recipeを使い、簡単にファインチューニングできます。次のコードを実行することで、新たなデータセットが、Pre-trainedモデルに相応しい情報に変換され、ファインチューニングが行われます。

  ./run.sh --stage 6 --ngpu 1 --lang jp \
     --g2p pyopenjtalk_accent_with_pause \
     --train_config conf/tuning/finetune_tacotron2.yaml \
     --train_args "--init_param <path of pre-trained.pth> \
     --tag finetune_jsut_pretrained_tacotron2

「ステージ⑦」のタスク

Vocoderモデルで生成された音響情報を音声ファイルにDocodeするステージです。 TTSのファインチューニングを通じ、「テキスト→音響情報」変換ができるようになりましたが、Vocoderの性能によってその音質が大きく変わるので、Vocoderのファインチューニングも行いました。

  • Vocoder Fine Tune前の声

Pre-trained VocoderはParallel WaveGAN(PWG) Githubから、次の条件で学習されたモデルを使用しました。

  • JSUTコーパス
  • Parallel WaveGAN

上記のPWG学習モデルも、Kaldiスタイルデータ・Recipe構造を使っていたので、今回前処理したデータセットをそのまま使えました。前回ESPnet紹介記事で説明したように、Kalidスタイル・Recipe構造を活用することで、多様な音声処理プロセスが簡単に実装できます。

  # TTSタスクで生成したデータセットのsymlinkを作る。
  mkdir dump data
  ln -s /<recipe_name>/dump/raw dump/
  ln -s /<recipe_name>/dump/raw/train data/train
  ln -s /<recipe_name>/dump/raw/dev data/dev
  ln -s /<recipe_name>/dump/raw/eval1 data/eval

  # PWGのパラメータを設定する。
  # conf/parallel_wavegan.v1.yaml

  # PWG Recipeのstage1で、ファインチューニングする。
  ./run.sh --stage 1 --conf conf/parallel_wavegan.v1.yaml

ファインチューニングの結果

つくよみちゃんのデータセットを使用して、ESPnetのTTSモデルを作って見ました。ESPnetのお陰で、簡単にファインチューニングできたと思います。

  • オリジナルのつくよみちゃん声
  • TTSで生成したつくよみちゃん声

上の音声ファイルは学習に使用したつくよみちゃんの声と、TTSモデルで生成した声です。オリジナル音声に比べると、少しノイズが乗せていますが、「100個という数少ないデータセット(約15分)」を考えると良い結果だと思います。
ぜひ皆さんもつくよみちゃんTTSモデルを使って、自分のセリフで音声合成を試してみてください。

まとめ

新たなデータセットを使ってTTSモデルをファインチューニングしてみました。ESPnetを使うことで、複雑な音声処理プロセスが簡単に実装できました。ステージ①のデータセットの用意が上手くできれば、TTSモデルだけでなく、様々な音声処理モデルが簡単に作成できると思います。

また、データセットや学習時間を増やしたり、ノイズを除去した音声ファイルを使用したりすることで、より良いTTSモデルが得られると思います。公開したつくよみちゃんTTSモデルは、これからも更新していく予定なので、楽しみにしてください!



使用したデータセットは、「つくよみちゃんコーパス」の100個の発話データであり、データの数が少なかったので、ファインチューニングを通じモデルを構成しました。

  • TTSの学習には、フリー素材キャラクター「つくよみちゃん」が無料公開している音声データを使用しています。
  • つくよみちゃんコーパス(CV.夢前黎)
    https://tyc.rei-yumesaki.net/material/corpus/

Han Beomseok

Han Beomseok

Python, AI Engineering, Natural Language