colaboratoryでapexのinstallに失敗する

タイトルの通りでapexのinstallに失敗したのでメモ。

実行したコマンドはこちら

!git clone https://github.com/NVIDIA/apex.git
!git checkout ebcd7f084bba96bdb0c3fdf396c3c6b02e745042 # 2021/09/18時点での最新
%cd apex
!pip install -v --no-cache-dir --global-option="--cpp_ext" --global-option="--cuda_ext" ./

エラーにかかわる部分の出力がこちら

 torch.__version__  = 1.9.0+cu102


    /tmp/pip-req-build-uufz822x/setup.py:67: UserWarning: Option --pyprof not specified. Not installing PyProf dependencies!
      warnings.warn("Option --pyprof not specified. Not installing PyProf dependencies!")

    Compiling cuda extensions with
    nvcc: NVIDIA (R) Cuda compiler driver
    Copyright (c) 2005-2020 NVIDIA Corporation
    Built on Mon_Oct_12_20:09:46_PDT_2020
    Cuda compilation tools, release 11.1, V11.1.105
    Build cuda_11.1.TC455_06.29190527_0
    from /usr/local/cuda/bin

    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/tmp/pip-req-build-uufz822x/setup.py", line 171, in <module>
        check_cuda_torch_binary_vs_bare_metal(torch.utils.cpp_extension.CUDA_HOME)
      File "/tmp/pip-req-build-uufz822x/setup.py", line 106, in check_cuda_torch_binary_vs_bare_metal
        "https://github.com/NVIDIA/apex/pull/323#discussion_r287021798.  "
    RuntimeError: Cuda extensions are being compiled with a version of Cuda that does not match the version used to compile Pytorch binaries.  Pytorch binaries were compiled with Cuda 10.2.
    In some cases, a minor-version mismatch will not cause later errors:  https://github.com/NVIDIA/apex/pull/323#discussion_r287021798.  You can try commenting out this check (at your own risk).
    Running setup.py install for apex ... error

colab上で使われるcudaが11.1だが、pytorchがcuda10.2でビルドされたものであるため失敗。ということでapex自体は特に関係なく、デフォルトで入ってるpytorchがなぜかcolab上で使われるcudaのバージョンと異なっているためだった。

ちなみにcudaとpytorchのバージョン確認方法はこちらの通り
https://qiita.com/ysit/items/a601cb59523cc1961556

!nvcc --version
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2020 NVIDIA Corporation
Built on Mon_Oct_12_20:09:46_PDT_2020
Cuda compilation tools, release 11.1, V11.1.105
Build cuda_11.1.TC455_06.29190527_0

!python -c 'import torch; print(torch.__version__) '
1.9.0+cu102

pytorchの公式ページからcuda11.1向けのインストール方法を確認してpip入れる
https://pytorch.org/

!pip3 install torch==1.9.0+cu111 torchvision==0.10.0+cu111 torchaudio==0.9.0 -f https://download.pytorch.org/whl/torch_stable.html

これでapexのインストールは成功するようになった。

pytorchのdata load時に処理が止まる

dataloader経由でデータを取得する際に処理待ちのような状態になって動かなくなることがあったのでメモ。

具体的にやろうとしてたのはDETRを動かしてみることで、画像2枚だけの小さなデータセットを用意して実行したところ以下の部分以降に処理が進まなかった
https://github.com/facebookresearch/detr/blob/eb9f7e03ed8e2ed2cd55528989fe7df890bc3fc0/datasets/coco.py#L29

  • 前後にprintを置いて後ろの出力だけが出ない
  • エラーが出るわけではなく学習処理は実行中のまま進まない
  • 上記のtransformsを単純に ToTensor() だけに置き換えてみても同様

学習開始の際に以下のようなwarningが出てたのでそれも調べてみる

[W ParallelNative.cpp:212] Warning: Cannot set number of intraop threads after parallel work has started or after set_num_threads call when using native parallel backend (functio
n set_num_threads)

メッセージでググるとこういうのがヒットする https://stackoverflow.com/questions/64772335/pytorch-w-parallelnative-cpp206 https://github.com/pytorch/pytorch/issues/46409

私の環境は

  • macOS
  • python3.7.7
  • pytorch1.9.0

なのでリンクにある状態とは異なるものの、ひとまず num_worker=0 として試したらwarningは出なくなり、かつ処理も進むようになった。というわけでdataloaderが複数workerで動いていることによる問題だった模様。

OMP_NUM_THREADS=1, seems to resolve the issue. The num_workers parameters on DataLoader seems to have no effect on the issue.

githubのissueだとこのように書いてあるが、pytorchのversionが異なるためかnum_workerの設定だけで問題なかった。

ちなみにせっかくなので調べてみたところ OMP_NUM_THREADS はCPU上での並列処理のスレッド数を設定するものだった
https://pytorch.org/docs/stable/notes/cpu_threading_torchscript_inference.html https://qiita.com/sakaia/items/6d31dedb396463023cfe

この設定にすると当然dataloaderのworkerの並列動作がなくなるのでパフォーマンスは低下するが、今回はまず動かしてみるという状態なので今の所はこれで解決。

coqui-ai/ttsでvocoderの学習

こちらの続きとしてvocoderの学習と、生成したmelspectrogramをvocoderに通して音声生成してみる
https://jsapachehtml.hatenablog.com/entry/2021/06/16/094213

学習

前回書いた通りmultiband-melganを試してみる。データセットもJSUTで同じくLJSpeechと同様の形式にしたものを使う。

なお、コードはv0.0.13のtag時点のものをcheckoutした。理由としてはなるべく最新の状態に近いものを利用したかったが、v0.0.14以降だとconfigの実装が変更されていて少し設定方法を直す必要があって手間だったためその直前を使った。

configの設定はこちら

DATAROOT = '/content/ljspeech_structure_22050'
DATAROOT_DRIVE ='/mydrive/machine-learning/tts/data/jsut_ver1.1_ljspeech_structure'

CONFIG = load_config('TTS/vocoder/configs/multiband_melgan_config.json') 

CONFIG['data_path'] = f"{DATAROOT}/wavs/"
CONFIG['audio']['stats_path'] = None
CONFIG['output_path'] = f"{DATAROOT_DRIVE}/output"
CONFIG['num_loader_workers'] =  2
CONFIG['num_val_loader_workers'] = 1
CONFIG['test_sentences_file'] = f"{DATAROOT_DRIVE}/test_sentences_file"
CONFIG['print_step'] = 1000
CONFIG['save_step'] = 5000

# デフォルトのconfigには存在しないがこちらの2つの設定がないとエラーになるので追加
CONFIG['use_l1_spec_loss'] = False
CONFIG['diff_samples_for_G_and_D'] = False

注意点は200K stepを超えないとdiscriminatorの学習が始まらない設定がデフォルトになっていること。

学習に用いたnotebookはこちら
https://github.com/y-kamiya/machine-learning-samples/blob/master/scripts/tts/TTS_jsut_multiband_melgan_example.ipynb

学習にかかる時間

batch_size=64(デフォルトのconfigの設定そのまま)で学習を行った際の例

200Kまで(discriminatorなし)

   --> TRAIN PERFORMACE -- EPOCH TIME: 50.83 sec -- GLOBAL_STEP: 133591
     | > avg_G_stft_loss_mg: 0.63728
     | > avg_G_stft_loss_sc: 0.21645
     | > avg_G_subband_stft_loss_mg: 0.58217
     | > avg_G_subband_stft_loss_sc: 0.22271
     | > avg_G_loss: 0.82930
     | > avg_G_gen_loss: 0.82930
     | > avg_G_adv_loss: 0.00000
     | > avg_loader_time: 0.59188
     | > avg_step_time: 0.41982

evaluationも含めて1 epoch分にかかる時間は130sec程度

200K以降(discriminatorあり)

--> TRAIN PERFORMACE -- EPOCH TIME: 75.39 sec -- GLOBAL_STEP: 425051
     | > avg_G_stft_loss_mg: 0.80059
     | > avg_G_stft_loss_sc: 0.36194
     | > avg_G_subband_stft_loss_mg: 0.72644
     | > avg_G_subband_stft_loss_sc: 0.37061
     | > avg_G_mse_fake_loss: 0.31279
     | > avg_G_loss: 1.91177
     | > avg_G_gen_loss: 1.12979
     | > avg_G_adv_loss: 0.78198
     | > avg_D_mse_gan_loss: 0.44839
     | > avg_D_mse_gan_real_loss: 0.13879
     | > avg_D_mse_gan_fake_loss: 0.13842
     | > avg_D_loss: 0.44839
     | > avg_loader_time: 0.80281
     | > avg_step_time: 0.62103

evaluationも含めて1 epoch分にかかる時間は190sec程度

データのロードにかかる時間が計算にかかる時間より長い。これは教師データとなる音声データからmelspectrogramを生成しているためかも?と思ったのだが、cacheの設定はONになっているため、そうであれば一度ロードした後は速くなるはずなので違った模様。

実行時間の見方について注意だが、 EPOCH TIME: 75.39 sec というのは計算時間(step_time)のみの合計を表している。今回のデータセットだと1 epochあたり120 stepだったので以下のような時間配分になっていることがわかる(200K以降のデータを例にして)

  • 学習の計算時間: 0.621 * 120 := 75 sec
  • 学習のデータロード時間: 0.803 * 120 := 96 sec
  • 評価にかかる時間: 190 - 75 - 96 = 19 sec

学習/評価用のデータ数はこちらの設定で決まっており、デフォルトは10となっている
https://github.com/coqui-ai/TTS/blob/v0.0.13/TTS/vocoder/configs/multiband_melgan_config.json#L143

そのためかなり少ないにも関わらず10%程度の時間を使ってる。評価を数epoch毎にするようなオプションは用意されていなかったので今回はやっていないが、メインのスクリプトをちょっと修正すれば可能なのでちゃんと学習させる or 繰り返しやる場合は入れると良さそう。

tensorboardでの結果

440Kくらいまで学習してみた結果がこちら eval log f:id:y-kamiya:20210722125607p:plain

train log f:id:y-kamiya:20210722142257p:plain

colabなので途中で一旦終了して若干ずれた位置から再学習が始まることがあるため注意。

eval logが大きく振れているのはデータ数を少なくしているためだと思われる。基本的に形状はeval, trainともに同じであり、値も同程度になっていることが確認できる。自分で作ったデータセットなど使う場合は学習用・評価用のデータの性質の異なる部分が多くなるはずであるため、lossの値に違いが出てくるのではと考えられる。

生成された音声がこちら

教師データ

生成された音声だけ聞くとしっかり喋っている内容もわかるしイントネーションもあっていてなかなかよい感じに聞こえる。ただ、教師データの音声に比べると明らかに雑音が多いことがわかる。

ちなみに生成された音声というのは、上記教師データとなる音声をmelspectrogramに変換後それをmelganのgeneratorに通して音声波形としたもののこと。なので仮にvocoderが理想的な関数を学習してあれば教師データと遜色のない音声が生成されるはずといえる。

上記の音声に対するmelspectrogramがこちらで、右が教師データとなる音声のmelspectrogram、中央が生成した音声のもの、左がその差分 f:id:y-kamiya:20210722144611p:plain 差分を見ると明るくなっている部分もけっこう多くまだ違いがあることがわかる。目視で右と中央を見比べると確かに中央の方が全体的に輪郭がぼやけた感じになっているように見える。ただ、低周波数帯のしっかりした帯がある部分の形状は比較的同様の形で生成できてるように見える。

公開されている学習済みモデルとの比較

元のnotebookに書かれているが、1.45M分LJSpeechで学習したモデルが公開されている。今回は勉強のためにvocoderの学習もやってみたものの以下の理由によりLJSpeechで学習済みのモデルを使えば問題なさそうなことはわかっている。

  • 1.45Mと学習させたstepが圧倒的に多い
  • melspectrogramから音声波形への変換については言語の違いによる影響がそこまで大きくない

ということで、学習済みモデルを使った場合はどの程度になるのかを確認するため、上記に貼った自前で学習した際のtensorboardの結果と同じ教師データを使ってmelspectrogramから音声波形を生成してみたのがこちら

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

教師データの音声は上で載せたものと同じなので生成したものだけ貼った。教師データとほとんど遜色なく聞こえるのがわかる。

melspectrogramはカラーバーの値が小さすぎて見えないが、真ん中は黄色の部分が2.5程度、右は黄色の部分が4程度となっているので注意。そのため色は異なるもののほぼ同程度の値となっている。それが示されているのが左の差分で、自前で学習したモデルで生成した結果と比べて色のついた部分が格段に減っている(=差がない)ことがわかる。

ということで実際に使うことを考えるのであれば、日本語だからという理由だけならvocoderは自前で学習する必要はなく公開されている学習済みモデルを使った方が良さそう。

テキストからの音声生成

glow-ttsで生成したmelspectrogramをmultiband-melganに通すことで音声を生成してみる。両者とも公開されている学習済みモデルを使う場合はこちらのnotebookを実行するだけでOKなので簡単
https://colab.research.google.com/drive/1NC4eQJFvVEqD8L4Rd8CVK25_Z-ypaBHD?usp=sharing

自前のモデルを使う場合もこれを少し修正して使える。参考までに私が使ったnotebookはこちら
https://github.com/y-kamiya/machine-learning-samples/blob/master/scripts/tts/Glow_TTS_MultiBandMelGAN_example.ipynb

上記のnotebookの中ほどに教師データから変換したmelspectrogramをvocoderに通して音声を生成する処理を追加したがこの際にハマった部分をメモしておくと、GANDatasetに渡すAudioProcessorはtts用とvocoder用で適切なものを渡す必要がある。

notebookを見ればAudioProcessorはtts用とvocoder用で別のものを生成して使っているので当然なのだが、両方のモデルに対していろいろ試している中でvocoderに対する処理なのにtts用のものを指定してしまった。生成されるmelspectrogramがおかしくなるのだがここが間違っていることに気づくのに結構な時間を使ってしまうこととなった。

JSUTで0から学習したglow-ttsのモデルと公開されている学習済みのmultiband-melganでテキストから生成した音声

LJSpeechで学習済みのモデルにJSUTで100Kほど追加学習したglow-ttsのモデルと公開されている学習済みのmultiband-melganでテキストから生成した音声

どちらもたどたどしさの残るしゃべりになっているが、追加学習したものの方が若干雑音が多いかも?というくらい。なのでもしこの程度の音声でOKなのであれば学習済みモデルに対してちょっと追加学習する方が楽に済む。ただし、さらに学習を続けた場合に違いが出るのかは気になるところ。