spreadsheetへの貼り付け時にダブルクォーテーションが消える

spreadsheetに以下のような複数行のテキストをコピペして貼り付けるとダブルクォーテーションが消える

"example1"
"example2"

ちなみにコピーするのが一行だけだとちゃんとダブルクォーテーションも貼り付けられる

どうやらテキスト先頭にダブルクォーテーションがあるとそれと対応する後ろのダブルクォーテーションがと一緒に削除するようになっている模様。つけたままの状態にしたかったので試してみると以下のように3重で囲うと貼付け時に1重の状態になる

"""example1"""
"""example2"""

colab proを使ってみた

1ヶ月だけcolab proを使ってみたのでメモ

https://jsapachehtml.hatenablog.com/entry/2021/06/16/094213

こちらの記事で少し書いたが、学習に数日かかるものだったため再実行の頻度を下げようと思って一度使ってみることにした

colaboratoryの無料版、有料版の比較は既にweb上にたくさん情報があるので詳細な説明はそちらを見た方がよいが、今回は実際に使ってみた感想ということで自分な理に気づいた点をメモとして残しておく

まずはざっくりした感想としては、とても良いものでした、ということになる

理由としては

  • ほぼ途切れることなくgpuを利用可能
  • 使えるリソースがスケールアップ
  • notebookでつなげてるマシンに対してブラウザ上でcliによるアクセスが可能

ほぼ途切れることなくgpuを利用可能

まず強制的に途切れるまでの利用時間が12h -> 24hと2倍になる。ただ、もっと大きなメリットはそもそも途切れなくなること。最近はinteractive利用のチェックが以前より厳しくなったようで、3時間くらい経つとチェックが入る(ログイン時のロボットじゃないですよね?のやつ)

これはjavascriptで定期的にボタンを押すようにしてあっても、定期的にページのリロードをするようにしておいても変わらず確認された(私が知らないだけで回避する方法はあるのかもしれない)

colab proにした場合、私が使ってる間はこんな感じだった

  • interactive利用のための確認は一度も入らなかった
  • pcがスリープに入ってしまったことがあったが復帰したらまだ実行中だった
    • 少なくとも1時間以上は経ってた
    • 半日以上経ってたことも一度あったさすがにそのときは切れていた
  • スリープにならないようにした上でjavascriptから定期的にボタンを押すようにしておいたところ24h経つまで切れなかった
    • その後すぐに初期化して再実行したところ、同じようにそのまま24h切れなかった
    • 無料の場合、12hぶっ通しで使った後は少なくとも数時間使えなくなったはず
  • 別のnotebook(gpuモード)も開いてそちらでもgpuを使って実行したところ、2~3日目のどこかで使用上限のため~のメッセージで切れた
    • ただし、そのあと1時間くらいしたら実行できるようになった

使っていて思ったが使用上限の判定はおそらくこんな感じなのではないだろうか

  • 使用可能時間が単位時間毎にn秒回復する
    • 実際は秒単位とかで細かく見てるはずだが(gcpがそうなので)ここではわかりやすく1時間毎と考えておく
  • colab proの場合、3600 <= n <= 7200となっているため2つ以上同時に使っている場合は枯渇する
    • ただし、3600 <= n なのでまたすぐに使えるようになる
  • 無料版の場合、n < 3600のためgpu一つの利用でも枯渇し12h使った後は回復の時間が必要になる

基本的に24h毎に再実行する前提ならば、それ以外は途切れることなく実行可能であるように見えた。ただし、24h実行しっぱなしを続けたのはせいぜい3~5日間で、別の学習を始める前は開始までにある程度間があいていた。なので本当に24hを途切れることなく続けた場合はどこかで上限にひっかかるときが来るかもしれない。

使えるリソースがスケールアップ

colab proの場合は無料版に比べて以下のように使えるリソースが良くなっている

  • gpuは基本的にP100 or V100のどちらか
    • 大抵P100でたまにV100というイメージ
  • メモリとストレージは無料版の2倍分

なので大きめのデータセットを試したい場合はcolab proにすることで可能になるものがあるはず

ブラウザ上でcliによるアクセスが可能

無料版だとnotebookを実行している際は他の操作ができないため、以下のようなときに困ったことがあった

  • コンソールには出してない別のログが確認したい
  • 出力ファイルが上書きされていく実装の場合、現状のものを取り出してdriveに保存したい

colab proだと画面左下のアイコンからcliを起動できコマンドラインによる操作が別途可能だったため、上記のような際に簡単に解決できる

まとめ

上記を考えるとこれで$10であれば十分すぎると感じた

実際同じ用途をgcp上でノードを立てて行う場合、P100であればプリエンプティブの料金でも$0.43/hourなので1ヶ月動かしておけたとしてgpuだけで$0.43 x 24 x 30 = $309.6(この他にインスタンス料金などもかかる)

なので1 gpu、2 cpu、メモリとストレージ容量の制約で問題のない規模の学習を行うなら圧倒的にお得だし準備の手間もかからない。そして、おそらく学習を試してみるという程度であれば間違いなくこの規模に収まるので、ちょっと時間がかかる学習などやってみたい場合にとてもよい選択だと思った

coqui-ai/ttsで日本語音声合成を試す

いままで音声の生成はやってみたことがないため勉強のためにやってみたのでメモ。

目的としてはどういうことを行っているのか理解したいというのと、TTSを学習させてみた場合にどの程度のコストがかかってどのくらいの音声が生成できるのかというのが気になったというのもある。

TTSの学習・推論処理をまとめたrepositoryはいくつかあるが、coqui-ai/ttsを使ってみることにした
https://github.com/coqui-ai/TTS

理由としては

ちなみにcoqui-ai/ttsは、readmeに書いてあることが同じかつ開発者も同じ方なのでmozilla/ttsと同じコードベースだと思われる。mozilla/ttsはmasterの最終コミットが2021/02に対してcoquiは数日前なので後継となるrepoという扱いっぽい。

データセット

日本語の音声データということでsingle speakerでデータ量も多いJSUTを使う
https://sites.google.com/site/shinnosuketakamichi/publication/jsut

このコーパスは日本語テキストと読み上げ音声からなります.音声データは48kHzでサンプリングされ,無響室で収録されました.一人の日本語女性話者の音声を収録しました.このコーパスは,10時間の音声 を含み,以下のデータからなります.

notebookで公開されている英語学習済みのモデルで使われているデータセットがLJSpeechで、そちらは24時間分のデータなので比較すると少なめ。

ちなみにJSUTを作成された方は他にも日本語の音声データセットをいろいろ公開している
https://sites.google.com/site/shinnosuketakamichi/publication/corpus?authuser=0

データセット準備

まずJSUTをLJSpeechの形に直す。LJSpeechの形についてはこちらで説明してくれている
https://qiita.com/tset-tset-tset/items/7b388b0536fcc774b2ad#112-ljspeech-%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%83%88

こんな感じで簡単に変換できる

#!/bin/bash

# jsutのzipを展開した際のroot
ROOT_PATH=$1

mkdir -p $ROOT_PATH/wavs
find $ROOT_PATH -name '*.wav' -type f | xargs -I@ cp @ $ROOT_PATH/wavs/
find $ROOT_PATH -name transcript_utf8.txt -type f | xargs cat | sed -e 's/:/|/' > $ROOT_PATH/metadata.csv

metadataはカタカナに直したものを使うことにする。これについても同じqiitaの著者が書いてくれたこちらの処理を使わせてもらう(一番下のセルの処理)
https://github.com/tset-tset-tset/hoge/blob/8ef0a1c/JSUT.ipynb

また、変換しそこねた漢字はできるだけ除きたいので削除しつつtrain/valのデータを分ける(ただし、これでも完全に削除しきれるわけではないので注意)

実際に使ったスクリプトはこちら
https://github.com/y-kamiya/machine-learning-samples/pull/24

ちなみにvalidation用のデータ数を160件しか用意してないが本来はもっと多く用意した方がよいと思われる。ここではまずやってみるということで、なるべく学習完了までの時間を短くするために少なめの数にした。また、品質チェックはattentionの並びと生成された音声ファイルを聞いて確認するのがと書いてあったためというのもある。

モデル

以下のモデルで試す

  • Text-to-Spectrogram
    • GlowTTS
  • Vocoder
    • MultiBandMelGAN

理由はtacotron2&waveglowなどに比べて学習・推論が速いらしいためというのと、vocoderについては推論用のnotebookを見るとよくMultiBandMelGANが使われているため
https://github.com/coqui-ai/TTS/wiki/%F0%9F%90%B8-TTS-Notebooks,-Examples-and-Tutorials

英語での推論はこちらのnotebookで試せる https://colab.research.google.com/drive/1NC4eQJFvVEqD8L4Rd8CVK25_Z-ypaBHD?usp=sharing

ちなみにMultiBandMelGANの学習済みステップ数が145Kと書いてあるが、これは1450Kの間違いっぽいので比較する場合は注意。

学習

ljspeechをtacotron2で学習するcolab notebookを元にして学習する
https://gist.github.com/erogol/97516ad65b44dbddb8cd694953187c5b

学習に使ったnotebookはこちら
https://github.com/y-kamiya/machine-learning-samples/blob/0cb64ec7be5ea5165d3a2424ade2f6948e61fdda/scripts/tts/TTS_jsut_glowtts.ipynb
うまくいった場合の参考として出力は一部残してある

また、ljspeechの学習済みモデルからの転移学習も試してみたいので、こちらのコミットをcheckoutして使うことにした
https://github.com/coqui-ai/TTS/tree/4132240

学習済みモデルのリストはこちらにある
https://github.com/coqui-ai/TTS/wiki/Experimental-Released-Models

カタカナのテキストを発音記号に変換するためにespeak-ngを使うが、こちらに書かれた修正済みのものを使う
Mozilla TTS (Tacotron2) を使って日本語音声合成 - Qiita

パラメータについてはqittaを参考にしつつ必要そうなものを足していった。精度向上のためにはもっと最適化していく必要があると思われるが一旦日本語の音声が聞ける範囲で学習できる状態にはなった

# glow tts用のconfigを元にする
CONFIG = load_config('/content/TTS/TTS/tts/configs/glow_tts_ljspeech.json') 

# 使うデータセットの設定
CONFIG['datasets'][0]['path'] = <データセットのroot>
CONFIG['datasets'][0]['meta_file_train'] = <rootからtrainデータへのpath>
CONFIG['datasets'][0]['meta_file_val'] = <rootからvalデータへのpath>
CONFIG['phoneme_language'] = 'ja'
CONFIG['text_cleaner'] = 'basic_cleaners'
# 事前にphoneme_cacheを事前作成してダウンロードしておく(高速化のため)
CONFIG['phoneme_cache_path'] = "/content/phoneme_cache"
# 0から学習なら必須
CONFIG['audio']['stats_path'] = "/content/scale_stats.npy"  
CONFIG['audio']['signal_norm'] = True


# 出力先とテスト用データ
CONFIG['output_path'] = <google drive上のpath指定するとよい>
CONFIG['test_sentences_file'] = <テスト用テキストを入れたファイルへのpath>

# 学習環境に応じて都合のよい値を入れる
CONFIG['num_loader_workers'] = 2
CONFIG['num_val_loader_workers'] = 1
CONFIG['print_step'] = 1000
CONFIG['save_step'] = 5000
# これを指定するとevaluationが飛ばされるので学習は速く済むが、その部分のログがtensorboard上に残らなくなるので見づらい
#CONFIG['test_delay_epochs'] = 250

上書きの元となるljspeech用のconfigはこちら
https://github.com/coqui-ai/TTS/blob/41322408331207093538c473891d1306785dc923/TTS/tts/configs/glow_tts_ljspeech.json

注意点としては

  • phoneme_cacheは事前に生成
    • テキストを発音記号に変換したデータでこれが実際のモデルへの入力になる
    • 生成自体に時間がかかる(30分くらい)ので毎回やるのは無駄
  • scale_stats.npyを事前に生成して指定する
    • これなしで0から学習を試してみたが声として聞こえる形にはならなかった
  • wavは事前にresampleしておく
    • resample=trueと設定することで学習時に変換してくれるが重いの処理なので時間の無駄
      • resample済みwavを読む場合のavg_load_time: 0.01秒程度
      • resampleして読む場合のavg_load_time: 5秒程度
  • wav, phoneme_cache, scale_stats.npyはダウンロードしてローカルから読むこと
    • driveの置いたものを参照するとネットワーク経由で毎回取得するので当然ではあるがとても時間がかかる
      • wavをlocalから読む場合のavg_load_time: 0.01秒程度
      • wavをdriveから読む場合のavg_load_time: 20秒程度

scale_stats.npyの生成は専用のスクリプトが用意されている
https://github.com/coqui-ai/TTS/blob/41322408331207093538c473891d1306785dc923/TTS/bin/compute_statistics.py

# 上記のconfigをロードした上で
python TTS/bin/compute_statistics.py --config_path config.json --out_path  <出力先>

phoneme_cacheは学習開始時にファイルが存在しなければ生成される。なのでそのデータセットで最初の学習開始時だけキャッシュのpathをdrive上などに設定しておく

# 上記のconfigのうち以下の設定を出力したい場所に変える
CONFIG['phoneme_cache_path'] = <出力先>

次回からはそれをローカルにダウンロードした上でそのpathを設定しておく

wavのresampleも専用のスクリプトが用意されている(今回checkoutしたversionにはなかったがmainには含まれていた)
https://github.com/coqui-ai/TTS/blob/87d674a/TTS/bin/resample.py

python TTS/bin/resample.py  --n_jobs 4 --input_dir jsut_ver1.1 --output_dir <出力先>

学習の出力をdriveに置く場合、save_stepで指定したstep毎にcheckpointが作られることになるが、一つ328MBあるのでいっぱいにならないよう注意。また、tensorboardのログにはspectrogramと音声ファイルが記録されるため見やすい反面サイズが大きいので注意(300K stepで2GBくらいになる)

結果

結果確認のおすすめ方法はこちらに書かれている
https://github.com/coqui-ai/TTS/wiki/Training-and-Testing-TTS#inspecting-training

  • validationを行うようにした上でtensorboardの出力を見るのがよい
    • まずはvalidation lossが下がっていること確認
    • 次にattentionが対角線上に並んでいることを確認
    • 最後にもっとも重要な指標としてtest用の出力音声を聞いて確認
      • train, evalの出力音声は学習データによるバイアスがかかっているためtestで確認すべき

TODOとなっている部分も多いがtensorboardの見方についての解説はこちら
https://github.com/coqui-ai/TTS/wiki/Tensorboard-Logs

以下の4つを比べてみた 1. scale_statsなしで0から学習 2. scale_statsありで0から学習 3. LJSpeechのscale_statsを指定してljspeechの学習済みモデルに追加学習 4. JSUTのscale_statsを指定してljspeechの学習済みモデルに追加学習

まず1.について、250K程度学習されたモデルで確認したところ指定した文をしゃべっているようには聞こえる状態にならなかった

statsありのモデルでは少なくとも100Kの段階で喋るように聞こえる状態になっていたため、1.の学習はそれ以上やらなかった

f:id:y-kamiya:20210613204851p:plain

lossの計算はこちら
https://github.com/coqui-ai/TTS/blob/41322408331207093538c473891d1306785dc923/TTS/tts/layers/losses.py#L402

avg_lossは他の2つのlossの和。log_mleがメインの損失(テキストからmelspectrogramへの変換を学習するためのもの)であり、loss_durがalignmentを学習すための補助的な損失(入力テキストのtoken毎の音の長さに対する損失)

この辺は論文を読んだものの説明できるほど理解できてないのでもう少しちゃんと勉強したいところ。

水色と赤のラインが学習済みモデルに追加学習を行ったものであり、300Kくらいから始まっているのはそのため。

ピンクのラインがstatsありで0から学習したモデルだが、直線部分が多くなっているのはevalを行わない設定で学習したときが何回かあったため。このように見づらくなってしまうため、スキップせずにevalを実行しつつ学習した方がやってみる分にはよさそう。

水色(LJSpeechのstats+学習済みモデル)は330K以降は右肩あがりで過学習気味、その他2つはほぼ水平なラインに見えるがわずかにlossが小さくなっていっている。

400K時点での各モデルが生成した音声はこちら(eval時にtestデータから生成されるものでvocoderはGriffin-LIm)

scale_statsありで0から学習

LJSpeechのscale_statsを指定してljspeechの学習済みモデルに追加学習

JSUTのscale_statsを指定してljspeechの学習済みモデルに追加学習

ある程度聞けるようになったものの、流暢とは言えないレベル。それぞれの結果はだいたい似たようなものだがイントネーションが微妙に違って面白い。ロスを見るとJSUTのstatsを適用して学習してるものはわずかずつだがevalデータに対するロスが下がっていっている。聞いた感じでも若干その方が自然な発音に聞こえる(気がする)

alignmentはこんな感じでstatsなしで0から学習したもの以外はきれいに並んでいるように見える f:id:y-kamiya:20210614091722p:plain

学習にかかる時間

やってみるに際してどのくらいの規模になるかを知りたいところだが、意外とこういう情報は書かれてなかったりするためわかったことを書いておく。

今回やってみた際の条件

  • batch_size: 32
  • mixed_precision: true
  • gpuはP100
  • 学習データは約7500件、評価データは160件
    • ちなみにJSUTの全データ数は7696件で音声10時間分

1 epochあたりほぼちょうど3分というところで、100K回すのに約22時間弱くらいだった。notebookには学習時のログも残してあるので参考までに
https://github.com/y-kamiya/machine-learning-samples/blob/0cb64ec7be5ea5165d3a2424ade2f6948e61fdda/scripts/tts/TTS_jsut_glowtts.ipynb

ちなみにP100を基準に書いてあるのはcolab proを使ったため。今回学習を試すにあたって最初は有料プランを使ってなかったが少しやってみたところで

  • 数日単位で回す必要があることがわかった
  • かなりの頻度で止まってしまう
    • 以前に比べてちゃんと判定されているようで3時間くらい経つと人が操作していることの確認が出てきた
    • 5分おきにボタンを押すようにしたり、windowを更新したりというスクリプトを入れておいても同様

ので試しに1ヶ月使ってみることにした。少し前から一度使ってみようとは思っていたのでちょうどいい機会だったが、使ってみた感想としては制限がかなり緩和されるためかなり使いやすかった(これで1000円でいいのかと思うくらい)。ただ、話がそれるのでcolab proについては次の記事として書くことにする。

ちなみに1 epochにかかる時間として、V100の場合だと2分半程度、T4なら3分半程度だった(と思う)

まとめ

生成したmelspectrogramをMultiBandMelGANで音声に変換したものも比較する予定だったが、長くなったので一旦ここまでにしてまとめ。

coqui-ttsは

  • 学習済みモデルに日本語は存在しないが、espeak-ngを用いることで日本語のテキストから発音への変換を行って学習することが可能
  • 英語モデルの学習・推論用のcolab notebookは公開されているため同じ形でやることで比較的スムーズに学習開始できた
    • 冒頭で挙げたqiitaで学習を試した際の工夫や結果が書かれているのでなおやりやすい

学習を行う場合に思ったこととしては

  • 学習中に出力されるログやcheckpointで結構容量を食うのでdriveを使ってる場合は注意
  • フリーのcolaboratoryなら100Kまで学習させて結果を比較するくらいにするのが良さそう(ちょうど1日分くらいで済む)

これをやるなかで調べてみたが、日本語のttsであればespnetが学習済みモデルも提供していて環境が整っているに見えたのでそちらも試して比較してみたい。