日本語CTRLを1から学習する - 5

はじめに

前回まで学習の基本的な設定を行ってきた。
今回はCTRLの学習高速化について調査・検証する。

Deep Learning学習高速化について

Deep Learningの学習高速化については以下のようなアプローチが考えられる。

  • Neural Netの軽量化
  • プログラムの最適化
  • 並列化

Neural Netの軽量化

パラメータの削減

層の数・潜在状態数を削減することで学習は速くなる。
ただ、パラメータ数を減らしすぎるとNeural Netの表現能力が下がり、
パフォーマンスにも影響が出るため不用意に下げることはできず、
どの程度まで下げて問題ないか検証する必要がある。

アーキテクチャの軽量化

BERTはTransformerを大量に重ねたアーキテクチャであり、
表現能力の高さと引き換えに学習・推論の速度が問題となる。

それを解決するためにBERTを高速化したアーキテクチャがいくつか
提案されている。

  • DistilBERT1 : DistilationでBERTを軽量化
  • ALBERT2 : embeddingと層間パラメータ共有で軽量化

DistilBERTは元の学習済みモデルがないとDistilationできないため今回は利用できない。
ALBERTの層間のパラメータ共有は良さそうに見えるが、文章生成系で有効かは不明。

プログラムの最適化

Deep Learningの学習プログラムの最適化は以下のような要素がある

  • ライブラリの選択
  • マシンに対する最適化

ライブラリの選択

以下ではtensorflowとpytorchの2つのライブラリについて 速度面で比較する。
※筆者は以前tensorflow1系を使っていたが、ここ1年ほどはpytorchを利用してきたため
最新のtensorflowの事情には詳しくない。

pytorch と tensorflowにおけるtransformersライブラリの性能比較 (推論速度) https://medium.com/huggingface/benchmarking-transformers-pytorch-and-tensorflow-e2917fb891c2

上記記事いよると推論速度的にはtensroflow, pytorch間ではそれほど大きな違いはない。
ただ上記はあくまで推論速度の推定である。

Titan RT上での各ライブラリの比較
https://medium.com/syncedreview/tensorflow-pytorch-or-mxnet-a-comprehensive-evaluation-on-nlp-cv-tasks-with-titan-rtx-cdf816fc3935

タスクによって学習速度の上下は入れ替わっている。
ただ、この比較はgithubから得たソースコードによって行っているらしいので、
個々のソースコードがどの程度最適化しているかが均一ではない。
またtensorflowのバージョンも1.10~1.13とやや古いものとなっているため2系でどうなるかは不明。

どちらのライブラリも最終的にcudnnでGPUを利用しているので、
GPUで計算しているうちはそれほど大きな差は出ないと思われる。
(TPUを使うと変わってくるかもしれない。)
当面は使い慣れたpytorchを使っていこうと思う。

混合精度による高速化

Neural Net内の計算はfloat32のデータ型で計算されることが多いが、
これをfloat16など低精度の型に落とすことでメモリ使用量が減少する。
Neural Net内の全てのパラメータの型を変換してしまうと性能が低下して
しまうため、必要な部分だけ高精度で必要ない部分は低精度とする
混合精度(mixed precision)という手法が利用される。

Nvidia apex3 は自動でpytorchのmixed precisionを実現してくれるライブラリである。
Transformer系のモデルはメモリの使用量も多くなるためこれを用いることで より大きなバッチサイズを利用できる。

並列化

並列化は複数のCPUまたはGPUを利用して1つのモデルを学習することである。
Deep Learningの場合は複数のGPUを利用して学習を高速化することは珍しくない。
pytorchは複数GPUによる学習をサポートしている。

参考
https://qiita.com/arutema47/items/2b92f94c734b0a11609d
https://nomoto-eriko.hatenablog.com/entry/2019/06/03/174633
http://kaga100man.com/2019/12/08/post-110/

基本的にはほとんどコード変更なしで並列化してくれるようだが、
BatchNormalizationの挙動などには注意が必要なようである。
また、GPU間の負荷分散も考える必要がある。

検証

今回はGoogle Colaboratory上でapexの検証を行った。
学習したのは前回と同様の小さめのCTRLモデル。

変更点1 apexをインストール, import
!git clone https://github.com/NVIDIA/apex
!cd apex;pip install -v --no-cache-dir --global-option="--cpp_ext" --global-option="--cuda_ext" .
from apex import amp
変更点2 モデルとオプティマイザーの設定後にコード追加
model, optimizer = amp.initialize(model, optimizer)
変更点3 lossの逆伝播部分を修正
#  loss.backward()
  with amp.scale_loss(loss, optimizer) as scaled_loss:
      scaled_loss.backward()

以上の修正を行った結果、1秒あたりのIter数が2.1→6.2に上昇した。
ただこの性能向上の原因はapexを導入したことだけではない可能性がある。
Google ColaboratoryでGPUを利用する場合性能の異なる2種類のGPU
がランダムに割り振られるらしい4。今回の実行では性能の良いTesla T4の
GPUが割り振らていたので、GPUの違いにより前回より性能がよかった可能性も残る。
(前回実行した時はGPUは確認していなかった。)

ただ、以下のページにあるようにT4とK80(ColaboのもうひとつのGPU)の間の性能差は
3倍もないようなのでapexによりある程度性能向上しているものと思われる。
https://electric-blue-industries.com/2019/10/30/hardware-gpu%E3%82%B9%E3%83%9A%E3%83%83%E3%82%AF%E6%AF%94%E8%BC%83%EF%BC%88nvidia%EF%BC%89/

学習パラメータとマシン構成について

学習するマシンの構成を決める上でハイパーパラメータを決める必要がある。

学習リソース例

NICT BERT 日本語 Pre-trained モデルではNVIDIA Tesla V100を32枚使い
1週間程度かけてBERTを学習している。 バッチサイズは4096。
(※GCPで Tesla V100 8枚を1ヶ月借りる費用は1万ドルほどである...)

京都大学黒橋村脇研ではGTX-1080Tiで1ヶ月ほどかけてBERTを学習している。
これくらいならば現実的にできなくはないと思うが、 max_seq_lenが128とやや小さく感じる。

学習データの読み込み

今回学習するデータはWikipediaデータ(3GB程度)、青空文庫データ(1GB程度)、
その他となり、おそらく10GB未満になると思われる。
トークン化などの前処理を終えたデータならばメモリに乗りそうなので
データ読み込みのオーバーヘッドをなくすために32GB程度のメインメモリを持つ
CPUとする。 GPUへはbatch単位でデータを送るため転送量はそれほど問題ないと思われる。

想定パラメータ

モデルサイズに関連するパラメータは以下のようなものがある。

  • 語彙数(30000程度)
  • Transformer層数(6~12層?)
  • 潜在状態数(384~768?)
  • 学習中の文章の最大長(256程度)
  • バッチサイズ (?)

これらのパラメータは実際に使用するマシンのGPUメモリに乗るかなどを検証しなければ分からない。
できれば2GPUマシン以内で2週間程度で終わる規模の学習にしたい。
バッチサイズは元のBERTが256(Cloud TPU 16コア) CTRLが1024 (Cloud TPU 256コア)で 学習しているがそれに合わせると計算リソースが恐ろしくかかりそうなので利用可能なリソースに合わせて考えたい。

まとめと今後

今回は学習の高速化手法について考えた。
混合精度学習、並列化などの高速化手法と
マシンリソースを考慮して最適なパラメータを検討したい。
ただ、今回の検証の目的はとりあえずやってみることなので
あまりチューニングに時間を使うつもりはない。
今後は学習するデータを決めて、どの程度の計算ができるかをGCP上で検証したい。

参考文献