2017年9月1日ChainerMNが正式リリースされました*1。β版では使用していましたが、新しい環境に一からインストールしてつかってみました。
ChainerMNについて
ChainerMNは、Chainerの拡張機能の1つで分散深層学習をサポートするものです。複数サーバにあるたくさんのGPUをつかって、学習を早く終わらすことができます。*2
ディープラーニングの学習の並列化には、データ並列とモデル並列があり、ChainerMNではデータ並列を採用しています。他のフレームワークでも実装が簡単なことから一般にはデータ並列が用いられています。
データ並列とは、workerにモデルをコピーし、別々のデータ(バッチ)を配り、それぞれのworkerで勾配の計算を行います。workerはプロセスのことで、4GPU搭載させた2台のサーバ(ノード)で並列処理させるときのworkerの数は8となります。
そして、各workerで算出された勾配から平均の勾配を計算し、重みを更新し各workerのモデルに配布します。ChainerMNでは、All-Reduceというステップが担当します。
使用環境
ノード間通信を行いたいので、同じ環境を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 MPIやMVAPICHがあります。今回は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のインストール
NCCLはNVIDIAが提供している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
のように記載します。
- Open MPIの場合、プロセス数を
# 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日程度に短縮できれば、大きなメリットだと思います。