kumilog.net

データ分析やプログラミングの話などを書いています。

ChianerMNによる分散深層学習

f:id:xkumiyu:20171101181337p:plain

2017年9月1日ChainerMNが正式リリースされました*1。β版では使用していましたが、新しい環境に一からインストールしてつかってみました。

ChainerMNについて

ChainerMNは、Chainerの拡張機能の1つで分散深層学習をサポートするものです。複数サーバにあるたくさんのGPUをつかって、学習を早く終わらすことができます。*2

ディープラーニングの学習の並列化には、データ並列とモデル並列があり、ChainerMNではデータ並列を採用しています。他のフレームワークでも実装が簡単なことから一般にはデータ並列が用いられています。

データ並列とは、workerにモデルをコピーし、別々のデータ(バッチ)を配り、それぞれのworkerで勾配の計算を行います。workerはプロセスのことで、4GPU搭載させた2台のサーバ(ノード)で並列処理させるときのworkerの数は8となります。

そして、各workerで算出された勾配から平均の勾配を計算し、重みを更新し各workerのモデルに配布します。ChainerMNでは、All-Reduceというステップが担当します。

f:id:xkumiyu:20170908141152p:plain f:id:xkumiyu:20170908141147p:plain 出典:Overview — ChainerMN 1.0.0 documentation

使用環境

ノード間通信を行いたいので、同じ環境を2ノード用意します。

ChainerMNのインストール

公式のインストールガイドに従いインストールします。

インストールに必要なもの

ChainerMNのインストールに必要なものは次の3つです。

  • Chainer本体
  • MPI
  • NVIDIA NCCL

Chainer本体のインストールは以下の記事を参照ください。

http://xkumiyu.hatenablog.com/entry/2017/09/05/214520xkumiyu.hatenablog.com

環境変数の設定

以下の環境変数を設定しておきます。

# CUDA関連
export CUDA_PATH=/usr/local/cuda
export PATH=$CUDA_PATH/bin:$PATH
export CPATH=$CUDA_PATH/include:$CPATH
export LD_LIBRARY_PATH=$CUDA_PATH/lib64:$LD_LIBRARY_PATH

# NCCL関連
export NCCL_ROOT=/usr/local/nccl
export CPATH=$NCCL_ROOT/include:$CPATH
export LD_LIBRARY_PATH=$NCCL_ROOT/lib/:$LD_LIBRARY_PATH
export LIBRARY_PATH=$NCCL_ROOT/lib/:$LIBRARY_PATH

MPIのインストール

MPIは分散処理をするための規格で、実装されたソフトウェアとしてはOpen MPIMVAPICHがあります。今回はOpen MPIを使います。共存は避けたほうが良いです。

$ wget https://www.open-mpi.org/software/ompi/v2.1/downloads/openmpi-2.1.1.tar.bz2
$ tar xf openmpi-2.1.1.tar.bz2
$ cd openmpi-2.1.1/
$ ./configure --with-cuda=$CUDA_PATH
$ make -j4
$ sudo make install
$ sudo ldconfig

動作確認

インストールが完了すると、mpirunコマンドが使えるようになっているはずです。

$ mpirun --version
mpirun (Open MPI) 2.1.1

Report bugs to http://www.open-mpi.org/community/help/

NCCL 2のインストール

NCCLNVIDIAが提供しているGPU間の通信ライブラリです。NCCL 1ではノード内のマルチGPUに対応していましたが、NCCL 2では、マルチノード / マルチGPUに対応しました。ChainerMNは、β版ではNCCL 1のみの対応でしたが、正式版ではNCCL 2に対応するようになりました。

NCCLは、こちらからダウンロードできます。できますが、NVIDIAメンバーへの登録が必要です。面倒です。今回はUbuntu16.04, CUDA 8.0を入れているので、「Download NCCL v2, for CUDA 8.0, August 30, 2017」->「NCCL 2.0.5 for Ubuntu 16.04 and CUDA 8」と選択してダウンロードします。

$ sudo dpkg -i nccl-repo-ubuntu1604-2.0.5-ga-cuda8.0_2-1_amd64.deb
$ sudo apt update
$ sudo apt install libnccl2 libnccl-dev

また、NCCL 2の公式インストールガイドはこちらにあります。

ChainerMNのインストール

pip install cython
pip install chainermn

トラブルシューティング

はじめ以下のようなエラーがでました。

$ pip install chainermn

...
chainermn/nccl/nccl.c:472:30: fatal error: cuda_runtime_api.h: No such file or directory

調べてみるとcuda_runtime_api.hはあるんですよね。

$ sudo find / -name 'cuda_runtime_api.h'
/usr/local/cuda-8.0/targets/x86_64-linux/include/cuda_runtime_api.h

どうやらパスが通っていないようでした。公式ドキュメントには、

export CPATH=$NCCL_ROOT/include:$CPATH

と書いてあるのですが、CPATHにはCUDAのPATHも通す必要があります。環境変数の設定に記載してあるとおり

export CPATH=$CUDA_PATH/include:$CPATH

とCUDAのパスも設定すれば解決します。また、環境変数を設定し直したらcythonのインストールからやり直す必要があります。

$ pip uninstall cython
$ pip install cython
$ pip install chainermn

ChianerMNによる分散深層学習

シングルノードでの並列処理

インストールが完了したので、いよいよ並列処理を行ってみます。公式のExampleを動かします。

$ git clone https://github.com/chainer/chainermn.git
$ cd chainermn/examples/mnist
$ mpiexec -n 2 python train_mnist.py -g

実行時のGPUの使用率を確認してみます。

$ nvidia-smi
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 375.66                 Driver Version: 375.66                    |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  Tesla K80           Off  | 1B1B:00:00.0     Off |                    0 |
| N/A   40C    P0    97W / 149W |    175MiB / 11439MiB |     81%      Default |
+-------------------------------+----------------------+----------------------+
|   1  Tesla K80           Off  | F481:00:00.0     Off |                    0 |
| N/A   46C    P0    78W / 149W |    180MiB / 11439MiB |     69%      Default |
+-------------------------------+----------------------+----------------------+

2枚とも使われていることが分かります。

ちなみに、MNISTのExampleでは、1GPUの場合と2GPUの場合とで処理時間はほとんど変りませんでした。動かしたパラメータはデフォルトで、どちらも1分程度で学習が終わりました。おそらくネットワーク構造が簡単なのとデータサイズが小さいため、並列化によるオーバーヘッドと相殺されているものと思われます。

マルチノードでの並列処理

並列処理の命令を行う(mpiexecを実行する)ノードをマスターノードといい、マスターノードからの命令を受ける他のノードをスレーブノードといいます。

事前準備

マルチノードの場合は、以下の準備が必要です。

  • すべてのノードにChainerMNをインストール
  • すべてのノードに実行するプログラムとデータを準備
    • 同じパスに置きます。今回は$HOME/chainermn/examples/mnist/train_mnist.py
    • 今回はすべてのノードでgit clone https://github.com/chainer/chainermn.gitとしましたが、共有フォルダを用意しても良いと思います。
  • マスターノードからスレーブノードにパスフレーズなしでsshログインできるように設定
  • マスターノードに並列処理を実行するノードのhostfileを作成
    • Open MPIの場合、プロセス数をcpu=2のように記載します。
# hostfile
gpu-01 cpu=2
gpu-02 cpu=2

並列処理の実行

スレーブノードで環境変数がうまく読み込まれなかったため、マスターノードの環境変数を渡しました。

$ mpiexec -n 4 --hostfile hostfile -x PATH -x LD_LIBRARY_PATH python chainermn/examples/mnist/train_mnist.py -g

うまく行かない場合、公式ドキュメントのトラブルシューティングの項が参考になります。(インストールガイドの倍くらい分量があります。)

ちなみに、実行時間は2分くらいかかり、1GPUやシングルノード2GPUのときと比べ、増加しています。マルチノードにしたことで、ノード間の通信というオーバヘッドが加わったことで遅くなっているものと思われます。

まとめ

ChianerMNをインストールし、シングルノードとマルチノードによる分散深層学習を実施しました。ExampleのMNISTのデータで多層パーセプトロン程度だと、分散深層学習のメリットを活かすことができなかったので、もっと大規模なデータとネットワーク構造で学習する場合に使いたいと思います。

例えば、1GPUで1週間かかる学習を、16GPU*3で半日や1日程度に短縮できれば、大きなメリットだと思います。

*1:マルチノードでの分散学習パッケージChainerMN 正式版 v1.0.0 をリリース – Preferred Networks

*2:中の人は、金で時間を買うものっていってました。

*3:AWSではp2.16xlargeというインスタンスで16GPU使えるそうです。