NVIDIA/tacotron2 で日本語の音声合成を試す (2) - JSUTで学習
前回、「Japanese Single Speaker Speech Dataset」を使った音声合成を行いました。次は「つくよみちゃんコーパス」といきたいところですが、「つくよみちゃんコーパス」のサンプル数は100個と少なく、そのままだとうまく学習できなさそうなので、
(1) 英語を学習(済) (The LJ Speech Dataset, 13100個)
↓
(2) 日本語を学習 (JSUT, 7696個)
↓
(3) つくよみちゃんの声を学習(つくよみちゃんコーパス, 100個)
という転移学習な作戦をたててみました。(うまくいくか不明)
今回は「JSUT」で日本語の学習に挑戦してみたいと思います。
前回
1. 音素表記
「Japanese Single Speaker Speech Dataset」にはデータセット内に「音素表記」があったのでそれを利用しましたが、「JSUT」にはないので、日本語の文章から「音素表記」に変換します。
今回は、「pyopenjtalk」を使って音素表記への変換を行います。
「pyopenjtalk」の使い方は、次のとおりです。
(1) 「Google Colab」でメニュー「編集→ノートブック」で「GPU」を選択。
(2) 以下のコマンドで「pyopenjtalk」をインストール。
# pyopenjtalkのインストール
!mkdir tools && cd tools && git clone https://github.com/r9y9/hts_engine_API.git
!mkdir -p tools/hts_engine_API/src/build && cd tools/hts_engine_API/src/build && \
cmake -DCMAKE_INSTALL_PREFIX=../.. .. && make -j && make install
!cd tools && git clone https://github.com/r9y9/open_jtalk.git
!mkdir -p tools/open_jtalk/src/build && cd tools/open_jtalk/src/build && \
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON \
-DHTS_ENGINE_LIB=../../../hts_engine_API/lib \
-DHTS_ENGINE_INCLUDE_DIR=../../../hts_engine_API/include .. && \
make install
!cp tools/open_jtalk/src/build/*.so* /usr/lib64-nvidia
!cd tools && git clone https://github.com/r9y9/pyopenjtalk.git
!cd tools/pyopenjtalk && pip install .
(3) pyopenjtalk.g2p()で音素表記に変換。
import pyopenjtalk
text = "システィナ礼拝堂は、1473年に、バティカン宮殿内に建立された、壮大な礼拝堂です、"
phones = pyopenjtalk.g2p(text, kana=False)
print(phones)
sh I s u t i n a r e e h a i d o o w a pau s e N y o N hy a k u n a n a j u u s a N n e N n i pau b a t I k a N ky u u d e N n a i n i k o N ry u u s a r e t a pau s o o d a i n a r e e h a i d o o d e s U
(4) 「NVIDIA/tacotron2」用の音素表記に変換。
「pyopenjtalk」は、「、」「。」「!」「?」を以下のように変換します。
「、」→「pau」(文末では<削除>)
「。」→ <削除> (文末以外は「pau」)
「!」→ <削除>
「?」→ <削除>
そこで、次のような前処理を行いました。
import pyopenjtalk
text = "システィナ礼拝堂は、1473年に、バティカン宮殿内に建立された、壮大な礼拝堂です、"
phones = pyopenjtalk.g2p(text, kana=False)
#phones = phones.replace('pau',',')
#phones = phones.replace(' ','')
#phones = phones + '.'
print(phones)
shIsutinareehaidoowa,seNyoNhyakunanajuusaNneNni,batIkaNkyuudeNnainikoNryuusareta,soodainareehaidoodesU.
また、文末に終端記号があった方が収束が早いとの情報があったので、音素表記の最後に「.」を付けることにしました。
2. データセットの準備
今回は「JSUT」を利用します。
・jsut_ver1.1
・basic5000 - 常用漢字の音読み・訓読みを全てカバー
・transcript_utf8.txt - wavファイル名とセリフの一覧
・wav - wavファイルを保持するフォルダ
・BASIC5000_XXXX.wav - wavファイル
:
・utparaphrase512 - 文の一部を読み替えたもの
・transcript_utf8.txt - wavファイル名とセリフの一覧
・wav - wavファイルを保持するフォルダ
・UT-PARAPHRASE-XXXXX-XXXXX.wav - wavファイル
:
・onomatopee300 - 日本語オノマトペ
・transcript_utf8.txt - wavファイル名とセリフの一覧
・wav - wavファイルを保持するフォルダ
・ONOMATOPEE300_XXX.wav - wavファイル
:
・countersuffix26 - 助数詞
・transcript_utf8.txt - wavファイル名とセリフの一覧
・wav - wavファイルを保持するフォルダ
・COUNTERSUFFIX26_XX.wav - wavファイル
:
・loanword128 - 外来語由来の動詞・名詞 (e.g., ググる)
・transcript_utf8.txt - wavファイル名とセリフの一覧
・wav - wavファイルを保持するフォルダ
・LOANWORD128_XXX.wav - wavファイル
:
・voiceactress100 - 声優統計コーパス (プロ女性声優のフリーコーパス) とのパラ音声
・transcript_utf8.txt - wavファイル名とセリフの一覧
・wav - wavファイルを保持するフォルダ
・VOICEACTRESS100_XXXX.wav - wavファイル
:
・travel1000 - 旅行ドメインのフレーズ
・transcript_utf8.txt - wavファイル名とセリフの一覧
・wav - wavファイルを保持するフォルダ
・TRAVEL1000_XXXX.wav - wavファイル
:
・precedent130 - 判例文
・transcript_utf8.txt - wavファイル名とセリフの一覧
・wav - wavファイルを保持するフォルダ
・PRECEDENT130_XXX.wav - wavファイル
:
・repeat500 - 繰り返し発話された音声 (100文 * 5回)
・transcript_utf8.txt - wavファイル名とセリフの一覧
・wav - wavファイルを保持するフォルダ
・REPEAT500_XXXX_XXX.wav - wavファイル
:
2-1. transcript.txt
「JSUT」の「transcript.txt」の書式は、次のとおりです。
BASIC5000_0001:水をマレーシアから買わなくてはならないのです。
BASIC5000_0002:木曜日、停戦会談は、何の進展もないまま終了しました。
:
これを「NVIDIA/tacotron2」用に変換します。
wav/BASIC5000_0001.wav|mizuomareeshiakarakawanakUtewanaranainodesU.
wav/BASIC5000_0002.wav|mokuyoobi,teeseNkaidaNwa,naninoshiNteNmonaimamashuuryooshimashIta.
:
(1) 9個の「transcript_utf8.txt」を1個にまとめる。
import os
# パス
in_paths = [
'jsut_ver1.1/basic5000/',
'jsut_ver1.1/countersuffix26/',
'jsut_ver1.1/loanword128/',
'jsut_ver1.1/onomatopee300/',
'jsut_ver1.1/precedent130/',
'jsut_ver1.1/repeat500/',
'jsut_ver1.1/travel1000/',
'jsut_ver1.1/utparaphrase512/',
'jsut_ver1.1/voiceactress100/']
out_path = 'filelists/'
# 出力フォルダの準備
os.makedirs(out_path, exist_ok=True)
# transcriptの変換
with open(out_path+'transcript_utf8.txt', 'w') as wf:
for in_path in in_paths:
with open(in_path+'transcript_utf8.txt', 'r') as rf:
wf.write(rf.read())
(2) 「Google Colab」でメニュー「編集→ノートブック」で「GPU」を選択。
(3) 以下のコマンドで「pyopenjtalk」をインストール。
# pyopenjtalkのインストール
!mkdir tools && cd tools && git clone https://github.com/r9y9/hts_engine_API.git
!mkdir -p tools/hts_engine_API/src/build && cd tools/hts_engine_API/src/build && \
cmake -DCMAKE_INSTALL_PREFIX=../.. .. && make -j && make install
!cd tools && git clone https://github.com/r9y9/open_jtalk.git
!mkdir -p tools/open_jtalk/src/build && cd tools/open_jtalk/src/build && \
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON \
-DHTS_ENGINE_LIB=../../../hts_engine_API/lib \
-DHTS_ENGINE_INCLUDE_DIR=../../../hts_engine_API/include .. && \
make install
!cp tools/open_jtalk/src/build/*.so* /usr/lib64-nvidia
!cd tools && git clone https://github.com/r9y9/pyopenjtalk.git
!cd tools/pyopenjtalk && pip install .
(4) 「Google Colab」で「wavファイルのパス」「セパレータ(|)」「音素表記」を変換。
「in_path」に入力ファイル、「out_path」の出力ファイルのパスを指定してください。
import os
import pyopenjtalk
# transcript.txtの変換
in_path = 'filelists/transcript_utf8.txt'
out_path = 'filelists/transcript.txt'
output = []
with open(in_path) as f:
lines = f.readlines()
for line in lines:
strs = line.split(':')
strs[1] = pyopenjtalk.g2p(strs[1], kana=False)
strs[1] = strs[1].replace('pau',',')
strs[1] = strs[1].replace(' ','')
strs[1] = strs[1] + '.'
output.append('wav/'+strs[0]+'.wav|'+strs[1]+'\n')
with open(out_path, 'w') as f:
f.writelines(output)
2-2. wav
「JSUT」のwavのサンプリングレートは48KHzです。これを「NVIDIA/tacotron2」用の22KHzに変換します。
変換スクリプトは、次のとおりです。
$ pip install librosa==0.8.0
$ pip install pysoundfile==0.9.0.post1
import os
import librosa
import soundfile as sf
# パス
in_paths = [
'jsut_ver1.1/basic5000/',
'jsut_ver1.1/countersuffix26/',
'jsut_ver1.1/loanword128/',
'jsut_ver1.1/onomatopee300/',
'jsut_ver1.1/precedent130/',
'jsut_ver1.1/repeat500/',
'jsut_ver1.1/travel1000/',
'jsut_ver1.1/utparaphrase512/',
'jsut_ver1.1/voiceactress100/']
out_path = 'wav/'
# 出力フォルダの準備
os.makedirs(out_path, exist_ok=True)
# wavの変換の関数
def convert(in_path):
filenames = os.listdir(in_path+'wav/')
for filename in filenames:
print(in_path+'wav/'+filename)
y, sr = librosa.core.load(in_path+'wav/'+filename, sr=22050, mono=True)
sf.write(out_path+'wav/'+filename, y, sr, subtype="PCM_16")
# wavの変換
for in_path in in_paths:
convert(in_path)
3. 学習
今回も「NVIDIA/tacotron2」を利用して、「Google Colab」で学習します。前回と同様にインストールした後、データセットを配置します。
(1) データセットの配置。
・work
・tacotron2
・filelists
・transcript.txt ←★ここに配置
・wav ←★ここに配置
・BASIC5000_XXXX.wav
:
(2) データセットを学習データと検証データに分割
# 学習データと検証データの分割
!head -n 7000 filelists/transcript.txt > filelists/transcript_train.txt
!tail -n 690 filelists/transcript.txt > filelists/transcript_val.txt
(3) 「hparams.py」の編集。
前回と同じだとメモリオーバーになってしまったので、バッチサイズを減らしました。
:
epochs=100,
:
training_files='filelists/transcript_train.txt',
validation_files='filelists/transcript_val.txt',
text_cleaners=['basic_cleaners'],
:
batch_size=16,
:
(4) 学習の実行。
!python train.py --output_directory=outdir --log_directory=logdir -c tacotron2_statedict.pt --warm_start
4. 推論
前回と同様に推論します。ただし、入力の音素表記は、「pyopenjtalk」で生成します。
text = "honnjituwaseitennnari."
待ちきれず3000ステップで確認したところ、「JSUT」の声で「本日は晴天なり」を言ってくれました。
◎ JSUT 13000ステップ