このブログは、株式会社フィックスターズのエンジニアが、あらゆるテーマについて自由に書いているブログです。
先日はChainerMNによる機械学習の高速化勉強会にご参加いただきありがとうございました!想定していたより多くの方に参加いただけました。
質疑時間や懇親会での討論もとても有意義なもので、とても良い勉強会になったと思います。
今日のブログは、そちらの勉強会でお話した内容の書き起こし(資料公開)に加えて、その後の再実験でより良い結果が出ましたので、その結果もまとめて報告したいと思います。
第一部のPFN鈴木さんによる『分散深層学習とChainerMNについて』の発表資料はPFN&PFIさんのSlideShareに上がっていますので、そちらをご覧ください。
なお、本記事に書いてある事柄は、記事執筆時点(2017年12月5日)の情報であり、今後の高火力・Chainerの発展にともなって変更される箇所が多数あるかと思います。最新の情報については、それぞれのヘルプ等を参照ください。
細かな話に入る前に、結果を先にお見せしたいと思います。
ここで、p1,p4,p8,p16と書いてあるのは、それぞれ1,4,8,16プロセスで並列処理した結果です。ご覧の通り、ChainerMNを用いて4,8,16と並列数が増えていくに従って処理速度もスケールさせることができました!
最近、「AIしたい!」(大意)という話をよくお聞きします。
そこまで大げさでなくても、AI関連のお仕事などがとても活発な昨今です。
この「AIしたい」とは何か。
よくよくお話を伺ったりして考えてみると、多くの場合、それは「深層学習(Deep Learning)を使って問題を解決したい」ということ、つまり「深いニューラルネットワークを使って機械学習したい」という意味であったりします。
ではじゃあ早速「深層学習しよう」と考えた時、どうしましょうか。
一つの方法としては、深層学習をするソフトウェアを全部自分で作るというのがあります。しかし、それはオススメしません。
なぜなら、この分野は現在とても研究が盛んで、とっっても進歩が速い分野です。例えば、よくあることなのですが、先週公開された手法について今日には改良された論文がarXivにある、みたいなことがあります。
ですので、自分が深層学習に対する専業の専門家でない限り、これに常に追いつき続けるのはかなり無謀です。
加えて、そもそもの目的は深層学習を使うことではなく、深層学習を使って何かの課題を解決したいというところにあります。
手法や実装を深く掘り下げるには、自分の勉強のためにはいいですが、問題解決をしたいという要望に応えるには少し遠回りです。
そこで、深層学習を使うには、既存のライブラリやフレームワークを使うのが良いです。
これは、行列演算をするのに全部自力で使わずに、BLAS系ライブラリを使うのに似ています。
そうすることで、非専門家であっても最新の手法の恩恵を受けやすくなり、また、速度面についてもライブラリ・フレームワークの開発者側にお任せしてしまうことができます。
これにより、アプリケーション(問題解決法の開発)に集中しやすくなります。
さてでは、深層学習用のフレームワークを使いましょう。しかし世の中には、深層学習用のフレームワークはいっぱいあります。
どれがいいでしょうか?分散学習するにあたって同期・非同期であるかなど、色々な評価軸がありますが、今回はChainerを使うことにします。
理由は、年始に公開されたPFN秋葉さんによる記事『ChainerMNによる分散深層学習の性能について』で、「分散並列してもスケールする」「学習制度も劣化しない」という報告があり、時間のかかる学習はChainerでなら高速化できるかもという期待ができそうだからです。
Chainerとは、Preferred Networksさんが開発している深層学習用フレームワークです。
Github.com上でオープンソースとして開発されており、全てPythonで書かれているのが特徴です。
それゆえ、ニューラルネットワークを簡単に作ることができ、また、複数GPUも含むGPUサポートも受けられます。
現時点での最新版は、v3.1.0(2017-11-17)であり、本記事でもこのバージョンを使用します。
今回、このChainerで分散学習するために、ChainerMNを用います。
ChainerMNは、Chainerの追加パッケージで、基本的には既存のChainerコードにoptimizerをかぶせるだけで分散学習できるようになるというものです。
先述の記事の通り、128GPUまでスケールするとのことです。また、先日公開された“Extremely Large Minibatch SGD: Training ResNet-50 on ImageNet in 15 Minutes”では、ChainerMNを使って、1024GPUを使ってResNet50によるImageNetデータセットの学習が15分で完了できたとのことです。
最新版はv1.0.0(2017-09-01)で、本記事でもこのバージョンを使用します。
ChainerとChainerMNの詳細については、勉強会第一部で鈴木さんが説明されているので、そちらをご覧ください。
さて、これでChainer/ChainerMNを用いれば分散並列して学習時間が短縮できそうなことが分かりました。
でも、そうは言ってもGPUをそんなに何十枚・何百枚と持っているとは限らないと思います。
また、もし仮に持てたとしても、そんなにたくさん管理するのはとても大変です(専業の管理者が必要になります)。
そこで、クラウドを利用すればいい!ということになります。
とは言うものの、またここで「ではどのクラウドを利用すればいいだろうか?」という話になります。例えば以下のようなサービスがあります。
今回は、一番最後の高火力コンピューティングを利用することにしました。
高火力コンピューティングは、さくらインターネットによるGPUクラウドサービスです。
特徴として、まずGPUノードをまるっと時間単位で借りられるというのがあります(月額課金もあります)。以下は現時点での高火力(時間課金モデル)で利用できるGPUになります。
GPU | 枚数 | 料金[円/時] |
---|---|---|
Maxwell TITAN X | x4 | 288 |
Pascal TITAN X | x4 | 294 |
Tesla P40 | x1 | 349 |
Tesla P100 | x1 | 357 |
ご覧の通り、時間あたり300円前後で借りることができます。時間課金というと例えばAWSでも借りることができますが、AWSはサービスの選択肢が多く設定も少し難しい部分があります。
一方、高火力であれば、先述の通りノード1つをまるっと渡してもらえるので、普段作業しているワークステーションと遜色ない使い勝手になります。
また、日本語ヘルプも充実しており、例えばNVIDIAドライバのインストール方法なども丁寧に解説されているため、日本で活動する人たちにとってはとてもありがたいです。
ということで、今回は、分散機械学習するためのフレームワークとしてChainerおよびChainerMNを使い、GPU環境では高火力コンピューティング(時間課金)を使うことにしました。
というわけで、さっそく動かし方を説明していきます。動かすためには、以下の5ステップが必要です。
まず最初に、高火力(時間課金)を契約し、使えるようにします。今回は、複数GPUで分散処理させるため、Pascal TITAN Xが4枚載っているQuadモデルを利用します。手順は以下のとおりです
これが終われば、後は普通にCUDA環境をインストールします。NVIDIA公式にある通りです。今回はCUDA 9.0.176-1を使用します。
次に、cuDNNを入れます。cuDNNはNVIDIA謹製の深層学習用ライブラリであり、Chainerは内部でこのライブラリを利用し高速処理しています。
こちらも、基本的にはNVIDIA公式にある通りに進めていけば問題ありません。
libcudnn7_7.0.3.11-1+cuda9.0_amd64.deb
libcudnn7-dev_7.0.3.11-1+cuda9.0_amd64.deb
$ sudo dpkg -i libcudnn7_7.0.3.11-1+cuda9.0_amd64.deb
$ sudo dpkg -i libcudnn7-dev_7.0.3.11-1+cuda9.0_amd64.deb
そして、NCCL2もインストールします。NCCL2も、cuDNNと同じくNVIDIA謹製のライブラリであり、GPU間の集団通信をするためにChainerMNが利用します。
手順はこれまでと同様NVIDIA公式にしたがって操作すれば入れることができます。
nccl-repo-ubuntu1604-2.0.5-ga-cuda9.0_3-1_amd64.deb
$ sudo dpkg -i nccl-repo-ubuntu1604-2.0.5-ga-cuda9.0_3-1_amd64.deb
$ sudo apt update
$ sudo apt install libnccl2 libnccl-dev
注意として、NCCLには、NCCL1と呼ばれるGithub.comでオープンソースとして開発されていたバージョンがありますが、こちらは既に開発が停止されており、また別物になっていますので、間違えないように注意してください。
その後、Python3をインストールします。Ubuntu 16.04はデフォルトではPython2が入っていますが、後でMultiprocessIteratorを使ったImageNetサンプルを動かすためにPython3が必要です。
ここで、システムとの競合を避けるためにpyenv(とvirtualenv)を使うのが良い望ましいです。インストールには以下のコマンドを実行します
$ sudo apt install git
$ git clone https://github.com/pyenv/pyenv.git ~/.pyenv
$ git clone https://github.com/pyenv/pyenv-virtualenv.git ~/.pyenv/plugins/pyenv-virtualenv
そして以下を~/.bashrc
の先頭に追加します。
通常このような設定は末尾につけますが、今回の環境では、bashrc等の先頭に「非対話型シェルの場合は設定しない」という処理が入っているため、後でMPIを使った時にも設定されるように必ず先頭に追記してください。
export PYENV_ROOT=$HOME/.pyenv
export PATH=$PYENV_ROOT/bin:$PATH
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"
そして、ここで再ログインするか、source ~/.bashrc
します。その後、以下のコマンドでPython3を使うことができます。
$ sudo apt install zlib1g-dev libffi-dev libssl-dev libbz2-dev libreadline-dev libsqlite3-dev
$ pyenv install 3.6.3
$ pyenv virtualenv 3.6.3 mychainer
$ pyenv global mychainer
ここまでくると、あとはChainerを入れます。Chainerはpip
で簡単に入ります。
$ pip install cupy
$ pip install chainer
以前はCuPyはChainerの一部でしたが、最近になって分離されたため、必ずCuPyも別途インストールしてください。そうしないと、計算にGPUを使えなくなります。
ここまでで、Chainerが動くようになったはずなので確認しましょう。ここでは、MNISTのサンプルを動かしてみて、以下のように出ればOKです。
$ wget https://github.com/chainer/chainer/archive/v3.1.0.tar.gz
$ tar xf v3.1.0.tar.gz
$ python chainer-3.1.0/examples/mnist/train_mnist.py -g1
GPU: 1
# unit: 1000
# Minibatch-size: 100
# epoch: 20
epoch main/loss validation/main/loss main/accuracy validation/main/accuracy elapsed_time
1 0.194015 0.112149 0.941251 0.9657 4.4908
2 0.0740059 0.079804 0.976565 0.9742 7.15152
3 0.0474544 0.0736811 0.984482 0.9792 9.79236
total [########..........................................] 16.67%
this epoch [################..................................] 33.33%
2000 iter, 3 epoch / 20 epochs
224.39 iters/sec. Estimated time to finish: 0:00:44.566185.
さて、ここからChainerMNの導入に移っていきます。まず最初に、OpenMPIをCUDA Aware APIありでインストールするところからです。
これは、後でChainerMNをGPUありで動かすために必要になってきます。
$ sudo apt install paco
$ wget https://www.open-mpi.org/software/ompi/v3.0/downloads/openmpi-3.0.0.tar.bz2
$ tar xf openmpi-3.0.0.tar.bz2
$ cd openmpi-3.0.0
$ mkdir build
$ cd build
$ ../configure --with-cuda
$ make -j4
$ sudo paco -lp openmpi-3.0.0 "make install"
$ sudo sh -c "echo /usr/local/lib > /etc/ld.so.conf.d/local.conf"
$ sudo ldconfig
$ mpiexec --version
mpiexec (OpenRTE) 3.0.0
注意点として、apt等で入るOpenMPIではCUDA Aware APIが使えないため、必ず--with-cuda
オプションを付けてconfigure
して、自力ビルドしてください。
もし既にapt等で入れてしまっている場合は、綺麗に消してから実行してください。今回の環境では、最初は空っぽの状態で借りているはずですので、aptで触る前にビルドしてインストールすれば良いです。
OpenMPIが入ったら、ちゃんとMPIが動作するか確認しましょう。以下のように動けばOKです。
$ mpiexec -n 2 printenv OMPI_COMM_WORLD_RANK
0
1
0と1の順番は前後するかもしれません。
そしてようやく、ChainerMNをインストールします。これもpip
で簡単に入れられます。
$ pip install mpi4py
$ pip install cython
$ pip install chainermn
これでChainerMNを使う設定ができました。ノード並列する前に、1ノードでChainerMNが動くか確認します。
$ wget https://github.com/chainer/chainermn/archive/v1.0.0.tar.gz
$ tar xf v3.1.0.tar.gz
$ mpiexec -n 4 python chainermn-1.0.0/examples/mnist/train_mnist.py -g
==========================================
Num process (COMM_WORLD): 4
Using GPUs
Using hierarchical communicator
Num unit: 1000
Num Minibatch-size: 100
Num epoch: 20
==========================================
epoch main/loss validation/main/loss main/accuracy validation/main/accuracy elapsed_time
1 0.279561 0.114889 0.919 0.965 2.31978
(略)
14 0.0109197 0.0769081 0.9962 0.9815 15.0908
total [####################################..............] 73.33%
this epoch [#################################.................] 66.67%
2200 iter, 14 epoch / 20 epochs
152.54 iters/sec. Estimated time to finish: 0:00:05.244517.
ここでエラーになる場合は、OpenMPIをaptで入れてしまってないか、pyenv等の設定を.bashrcの先頭でなく末尾に入れてしまっていないか確認してください。
ここまできたら、あとは複数ノードでも動かしてみましょう。
まずはじめに、高火力コンピューティングの上で、ローカルネットワークを構築します。
これをやらないと、ノード間通信が全てグローバル経由になってしまい、ネットワーク帯域がとても遅くなり、分散してもまったく速度が上がらなくなります。
設定手順は基本的には、公式ヘルプに従って作業すれば完了します。
まず、サーバーコンソールにログインして、ネットワークを作成します。その後、ローカルネットワークを構成したいサーバー全てに同じネットワークを割り当てます。
次に、各ノードにログインして、ローカルネットワークを使えるように設定します。/etc/network/interfaces
を適当なファイルで開き、末尾の
# The secondary network interface
#auto eno3
iface eno3 inet manual
# bond-master bond1
#auto eno4
iface eno4 inet manual
# bond-master bond1
# bonding
#auto bond1
iface bond1 inet static
# address xxx.xxx.x.x
# netmask xxx.xxx.xxx.x
# bond-mode 4
# bond-slaves eno3 eno4
# bond-miimon 100
# bond-updelay 5000
# bond-xmit_hash_policy 2
となっている部分のコメントアウトを外し、xxxになっている部分を変更し保存します。
# The secondary network interface
auto eno3
iface eno3 inet manual
bond-master bond1
auto eno4
iface eno4 inet manual
bond-master bond1
# bonding
auto bond1
iface bond1 inet static
address 192.168.0.(1から254までの好きな番号)
netmask 255.255.255.0
bond-mode 4
bond-slaves eno3 eno4
bond-miimon 100
bond-updelay 5000
bond-xmit_hash_policy 2
(1から254までの好きな番号)の部分については、各ノードに一意に割り当てます。例えば4ノード借りている場合は、それぞれのノードで192.168.0.1, 192.168.0.2, 192.168.0.3, 192.168.0.4とすると分かりやすいでしょう。編集が終わったら
$ sudo service networking restart
を忘れずに。これでローカルネットワークが開通しましたので、実際にnetperf等で動かしてみると以下のように10Gbイーサネットの恩恵が受けられていることが確認できます。
$ netperf -f M -H 192.168.0.2
MIGRATED TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 192.168.0.2 () port 0 AF_INET : demo
Recv Send Send
Socket Socket Message Elapsed
Size Size Size Time Throughput
bytes bytes bytes secs. MBytes/sec
87380 16384 16384 10.00 1119.74
設定が何をやっているかなどの詳細については別のヘルプの各OSのセットアップ仕様>Ubuntu 16.04にある「7.2 ネットワーク冗長構成でのローカル接続側NICの設定」を参照してください。
無事にネットワークも作れましたので、MPI通信の準備のために、hostfileというのを書きます。hostfileというのは、使用するノードのIPアドレス等を羅列したファイルです。
例えば、先のローカルネットワークで構築した4ノードで、各ノードが4GPUある場合、作業ディレクトリに以下のようなhostfileを作ってください。
192.168.0.1 slots=4
192.168.0.2 slots=4
192.168.0.3 slots=4
192.168.0.4 slots=4
そして、MPIが複数ノードでも動くか確認します。先のhostfileと同じディレクトリで以下を実行してみます
$ mpiexec -n 16 -hostfile hostfile --map-by ppr:4:node printenv OMPI_COMM_WORLD_RANK
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
数の順番は前後するかもしれませんが、0から15の数字が表示されればOKです。オプションの--map-by ppr:4:node
は、1ノードあたり4プロセス起動するという意味です。
今回の環境では、hostfileにソケット数を指定しないと1プロセスしか起動できず、指定しても4プロセスにならないことがありましたので、必ず指定してください。
また、ここで、応答が帰ってこなかったりエラーになっている場合は、例えばsshのログインに失敗している可能性があります。
SSHでパスワードやサーバー証明書の確認なしにログインできるかどうかを確認してください。
最後に、複数ノードでChainerMNを動かすことができます。同じくMNISTのサンプルを動かしてみます。
$ mpiexec -np 16 -hostfile hostfile --map-by ppr:4:node python chainermn-1.0.0/examples/mnist/train_mnist.py -g
==========================================
Num process (COMM_WORLD): 16
Using GPUs
Using hierarchical communicator
Num unit: 1000
Num Minibatch-size: 100
Num epoch: 20
==========================================
epoch main/loss validation/main/loss main/accuracy validation/main/accuracy elapsed_time
1 0.550476 0.21444 0.843947 0.937232 69.9355
2 0.170056 0.135458 0.95054 0.958036 138.297
これで、ChainerMNを動かすところまでできました。
以上はMNISTサンプルでしたが、これではデータセットもモデルも小さすぎるので、本番のためにImageNet(ILSVRC2012)データセットを用意しましょう。
ChainerおよびChainermnのサンプルにあるtrain_imagenet.pyを使うためには、以下の手順が必要です。
以上をすべて手作業でやっていると日が暮れても終わらないので、変換スクリプト”ImagenetConverterForChainer”を用意しました。使用方法はREADMEをご覧ください。
以上までで準備が整ったので、実際に動かしてみます。
実行環境は、先述の通り、さくら高火力(時間課金)Pascal TITANが4枚載ったQuadモデルを用います。今回は4ノードまで借りて、以下のコマンドで計測しました
$ python train_imagenet.py /opt/traindata/ILSVRC2012/train.ssv /opt/traindata/ILSVRC2012/val.ssv -g0 -a resnet50
$ python train_imagenet_data_parallel.py /opt/traindata/ILSVRC2012/train.ssv /opt/traindata/ILSVRC2012/val.ssv -a resnet50
$ mpiexec -n 4 python train_imagenet.py /opt/traindata/ILSVRC2012/train.ssv /opt/traindata/ILSVRC2012/val.ssv -a resnet50
$ mpiexec --np 8 -hostfile hostfile --map-by ppr:4:node python train_imagenet.py/opt/traindata/ILSVRC2012/train.ssv /opt/traindata/ILSVRC2012/val.ssv -a resnet50
$ mpiexec --np 16 -hostfile hostfile --map-by ppr:4:node python train_imagenet.py /opt/traindata/ILSVRC2012/train.ssv /opt/traindata/ILSVRC2012/val.ssv -a resnet50
基本的には、exampleにあるtrain_imagenet.pyをそのまま使います。ただし、val_interval
とlog_interval
は(1, 'epoch')
に固定しました。
実験結果は以下のようになりました(これは冒頭の概要部分で載せたものと同じものです)。ご覧の通り、ノード並列して分散学習することで、処理速度(単位時間あたりの処理画像枚数)を大幅に向上させることができました!
特に、ChainerMNを用いて4→8→16ノードと2倍ずつノード数を増やしていくことで、処理速度は1.5倍強ずつ高速化されています。これは最初の秋葉さんによる記事である通り「スケールする」ことを示しています。
ただし、理想状態ではノード数が2倍になった時には2倍高速化されてほしいところです。少し足りないので、内訳をちゃんと計測してみます。
計測にはプロファイラー等を使う手もありますが、ここでは素朴に、該当する部分にタイマーを仕込んでみます。ChainerMNを使った時の1反復は、chainermn/optimizers.pyの_MultiNodeOptimizer.updateメソッドにあり、以下のようになっています。
def update(self, lossfun=None, *args, **kwds):
target = self.target
if lossfun is not None:
use_cleargrads = getattr(self, '_use_cleargrads', False)
loss = lossfun(*args, **kwds)
if use_cleargrads:
target.cleargrads()
else:
target.zerograds()
loss.backward()
del loss
if self.needs_broadcast:
self.communicator.broadcast_data(target)
super(_MultiNodeOptimizer, self).__setattr__(
'needs_broadcast', False)
else:
self.communicator.allreduce_grad(target)
self.actual_optimizer.update(None, *args, **kwds)
ここで、以下のようにタイマーを仕込んで、標準出力に出すことで、時間を計測してみました。
def update(self, lossfun=None, *args, **kwds):
import mpi4py.MPI
rank = mpi4py.MPI.COMM_WORLD.rank
import time
t=[0, 0, 0]
mpi4py.MPI.COMM_WORLD.Barrier()
start_total = time.time()
target = self.target
mpi4py.MPI.COMM_WORLD.Barrier()
start = time.time()
if lossfun is not None:
use_cleargrads = getattr(self, '_use_cleargrads', False)
loss = lossfun(*args, **kwds)
if use_cleargrads:
target.cleargrads()
else:
target.zerograds()
loss.backward()
del loss
t[0] = time.time() - start
if self.needs_broadcast:
self.communicator.broadcast_data(target)
super(_MultiNodeOptimizer, self).__setattr__(
'needs_broadcast', False)
else:
mpi4py.MPI.COMM_WORLD.Barrier()
start = time.time()
self.communicator.allreduce_grad(target)
t[1] = time.time() - start
mpi4py.MPI.COMM_WORLD.Barrier()
start = time.time()
self.actual_optimizer.update(None, *args, **kwds)
t[2] = time.time() - start
t_total = time.time() - start_total
mpi4py.MPI.COMM_WORLD.Barrier()
print(rank, t_total, t[0], t[1], t[2])
これで、反復毎に、反復全体の時間、compute(forward+backward)、allreduce、optimizeのそれぞれの時間が計測できます。その結果、以下の通りになりました
ご覧の通り、16ノードの時にはallreduceにかかる時間が半分以上を占めてしまっているのが分かります。
そこで、このallreduceの時間について追ってみます。
まず、ローカル回線は10Gbイーサネットとのことなので、そもそもMPI通信でその速度が出ているかを確認しました。
確認には、簡単なMPI P2P通信の計測スクリプトを使いました。結果は以下のとおりです。
ご覧の通り、8-9[Gibps]と、ほぼ10Gbイーサネットを使い切っていることが確認できました。
そこで次に、実際のallreduceではどれぐらいのネットワーク帯域が出ているか確認しました。
まず、MPIのメッセージ長ですが、先のupdateメソッドのbackwardが計算し終わった後辺りに
sum = 0
for _, param in sorted(target.namedparams()):
sum += param.grad.nbytes
print(sum)
というコードを仕込んで、実測してみます。今回の測定に用いたRetNet50での結果は102228384
[byte]と出ました。これをLとします。
allreduceの場合、reduceとbcastで2回通信します。通信量Nは、通信アルゴリズムの実装にも寄りますが、概ね、プロセス数pに対して、$N = \frac{2L(p-1)}{p}$と近似できます。
今回ChainerMNのサンプルそのままであるhierarchical communicatorを用いたので、実際にMPI通信しているプロセスは各ノードにつき1プロセスだけです。
よって、4プロセス、8プロセス、16プロセスの時は、それぞれp=1,2,4となり、つまり、通信量は$N=0, L, \frac{3}{2}L$になります。当然ですが、1ノードしか使っていない場合は通信しません。
一方、allreduceにかかった時間は、先の計測結果から、2ノードの時0.15[s]、4ノードの時0.22[s]であることが分かっています。これらを全て代入すると、以下の結果を得えられました
allreduceの処理の中には純粋なMPI通信だけでなく、NCCLを用いたreduce/bcast処理やCPU-GPUデータ転送も入っていることを考えると、実効帯域8[Gibps]に対して5[Gibps]程度は十分に帯域を使い切っていると考えて良さそうです。
以上が今回の結果になり、まとめると以下のようになります。
今回はあくまでResNet50とILSVRC2012データセットを用いた結果であることに留意してください。
特に、精度については、冒頭で紹介した『ChainerMN による分散深層学習の性能について』の「分散深層学習の難しさと課題について」節で触れられている通り、並列数を増やすとバッチサイズが増える影響で、精度劣化の原因となります。
今回は、1000分類問題に対してバッチサイズが最大で32×16プロセス=512程度であったので、経験的に極端な精度劣化がないと期待できる範囲での実験結果です。さらなる並列数でも精度を担保するには、バッチサイズを小さくしたりその他の工夫が必要です。
工夫の詳細については、PFN鈴木さんの発表資料aのp.47あたりからに詳しいですので、そちらもご覧ください。
今後、また機会があれば、更なる大規模ノードでそれらの改良手法を取り入れての実験もしてみたいと思います。
また、スループットの図の通り、シングルノードであれば、Chainerのtrain_imagenet_data_parallel.pyを使ったほうが、ChainerMNを使うより高速であるという結果も得られています。こちらについては、原因はまだ調査中で、ChainerMNのGithubにもIssueとして報告済みです。進展ありましたら本記事にでも追記しようと思います。
本成果は、国立研究開発法人新エネルギー・産業技術総合開発機構(NEDO)の委託事業「IoT推進のための横断技術開発プロジェクト」の先導調査研究テーマ「実社会ビッグデータ処理基盤を実現する大規模データセンター構築・運用技術の研究開発」(代表提案者:さくらインターネット株式会社)によって得られたものです。
また、本成果を出すにあたり、特にネットワーク周りの設定について、さくらインターネット株式会社の須藤さんに多大なご助言をいただきました。
keisuke.kimura in Livox Mid-360をROS1/ROS2で動かしてみた
Sorry for the delay in replying. I have done SLAM (FAST_LIO) with Livox MID360, but for various reasons I have not be...
Miya in ウエハースケールエンジン向けSimulated Annealingを複数タイルによる並列化で実装しました
作成されたプロファイラがとても良さそうです :) ぜひ詳細を書いていただきたいです!...
Deivaprakash in Livox Mid-360をROS1/ROS2で動かしてみた
Hey guys myself deiva from India currently i am working in this Livox MID360 and eager to knwo whether you have done the...
岩崎システム設計 岩崎 満 in Alveo U50で10G Ethernetを試してみる
仕事の都合で、検索を行い、御社サイトにたどりつきました。 内容は大変参考になりま...
Prabuddhi Wariyapperuma in Livox Mid-360をROS1/ROS2で動かしてみた
This issue was sorted....