Skip to main content
分散深層学習とChainerMN
について
鈴木 脩司
Preferred Networks, Inc.
ChainerMNによる機械学習の高速化勉強会
2017/11/28
自己紹介
名前:鈴木脩司
経歴
 -2015 東京工業大学 大学院情報理工学科 計算工学専攻
– バイオインフォマティクス、特に配列相同性検索の高速化について研究
 2015-2017 株式会社富士通研究所
– クラウドの運用管理について研究
 2017- 株式会社Preferred Networks
– Researcherとして、バイオヘルスケア分野、ChainerMNの研究開発
ちなみに、2011-2012の間、アルバイトとしてFixstarsでGPU等のアクセラレータ
を使った高速化をやってました
2
会社紹介:Preferred Networks, Inc. (PFN)
 設立: 2014/3月(Preferred Infrastructureからの分社)
– 出資者: NTT (2014), Fanuc (2015), TOYOTA (2015)
 本社: 東京都千代田区大手町
支社:カルフォルニア、サンマテオ
 従業員数: 約110名(殆どがEngineers & Researchers)
 事業内容: Internet of Thing + 機械学習
– 交通
– 製造業
– バイオヘルスケア
3
今日のお話
 分散深層学習の基礎
 ChainerMNについて
 ChainerMNの使い方
 高速化への道
 ChainerMNの今後
4
分散深層学習の基礎
5
2つのアプローチ
Data Parallel
6
Model Parallel
分散深層学習のアプローチ
データ並列
 全ワーカーがモデルのレプリカを持つ
 異なるデータを処理し協力してモデルを更新
モデル並列
 レイヤーごとやレイヤー内で計算を分割
 GPU メモリが足りなかった時代に行われていた
7
8
同期型 非同期型
Computation
Parameter
Server
Push grad
Pull model
データ並列の2つのアプローチ
9
同期型 非同期型
Computation
Parameter
Server
Push grad
Pull modelどっちが良いのか?
データ並列の2つのアプローチ
10
同期型 非同期型
スループット低 スループット高
よくある誤解
スループット = NN にデータを食わせる速度
11
同期型 vs. 非同期型
12
同期型 非同期型
スループット低 スループット高
スループットのみを見るのは愚か!
[Figure 3, Chen+ 2016]
Gradient Staleness
 Stale gradients = 古いパラメータに対して計算された勾
配
 Staleness はモデルの改善に悪影響がある
14
ImageNet (ILSVRC’17) CLS 優勝チーム
17
個人的見解
 同期型じゃないとダメということは無いのではないか
 非同期型でも上手くチューニングすれば同じ精度までいける場合は多いと予想
 ただし同期型の方がチューニングがしやすいので実用的
18
ChainerMNについて
19
ChainerMN
 Chainer の追加パッケージ
 分散深層学習に必要な機能を追加
特徴
 高速 NVIDIA NCCL, CUDA-Aware MPI 等の最新技術の活用により高い性能を実現
 柔軟 Chainer の柔軟性を損なわず、動的なネットワークですら分散学習可能
 簡単 既存のコードを少し変更すれば利用可能
20
ChainerMN
 ChainerMN は MPI を利用、SPMD で動作
SPMD = single program, multiple data; 全プロセスは同じスクリプトで動作する
 1 プロセス (ワーカー)で1 GPU
sakura1
train_imagenet.py (GPU0)
train_imagenet.py (GPU1)
train_imagenet.py (GPU2)
train_imagenet.py (GPU3)
sakura2
train_imagenet.py (GPU0)
train_imagenet.py (GPU1)
train_imagenet.py (GPU2)
train_imagenet.py (GPU3)
$ mpiexec -n 8 -host sakura1,sakura2 python train_imagenet.py
21
通常の学習イテレーション
Forward Backward Optimize
62
Gradient
𝜹𝑬
𝜹𝒘
Gradient
𝜹𝑬
𝜹𝒘
22
学習イテレーション
23
通常のChainerによる学習
Forward Backward Optimize
All-Reduce
Forward
Forward
Forward
Backward
Backward
Backward
Optimize
Optimize
Optimize
ChainerMNによる分散学習
All-Reduce
 各ワーカーが持つ値を集計し、集計した結果を全ワーカーに配る処理
 ChainerMNでは全ワーカーが持つ勾配𝑔𝑖から平均 ҧ𝑔を計算して配る
24
All-Reduce
𝑔0 = 1
𝑔1 = 2
ҧ𝑔 = 1.5
ҧ𝑔 = 1.5
25
詳細: https://chainer.org/general/2017/02/08/Performance-of-Distributed-Deep-
Learning-Using-ChainerMN.html
Microsoft Azureを利用した実験
26
27
最新のTOP500(2017年11月現在)で、
国内のIndustry segmentで1位
ImageNetを1024GPUを使って15分で学習 [Akiba+ 2017]
28
1024GPUでも、ほぼ線形にスケール
参考文献:
https://arxiv.org/abs/1711.04325
ChainerMNの使い方
ChainerMNに必要なライブラリ (GPUを利用する場合)
1. Chainer
– CuPyとcuDNN も含めてインストールする
2. MPI (OpenMPIなど)
– CUDA-Awareのサポートを入れてコンパイルする
3. NCCL
– githubにあるものはバージョンが古いので、NVIDIAのサイトからダウンロード
4. MPI4py
5. Cython
– 事前にpipなどでインストールする必要がある
30
CPUのみの場合は以下を参照
http://chainermn.readthedocs.io/en/latest/installation/guide.html
ChainerMNのインストールとサンプル実行(GPUを利用する場合)
 インストール方法
– $ pip install chainermn
 MNISTのサンプルの実行
– $ git clone git@github.com:chainer/chainermn.git
– $ cd chainermn
– $ mpiexec -n 4 python examples/mnist/train_mnist.py –g
 この例では1つのノードで4つのワーカーが起動して、4つのGPUを使って分散学
習が実行される
31
あれ、動かない?と思ったら・・・
 ChainerMNドキュメントのStep-by-Step Troubleshootingを見る!
– http://chainermn.readthedocs.io/en/latest/installation/troubleshooti
ng.html
 Step-by-Step TroubleshootingはChainerMNを使うときにハマりそう
な部分を、1つ1つ問題がないか確認していく手順が書かれている
– たとえば・・・
 Check SSH connection
32
$ ssh host00 'hostname'
host00 # without hitting the password
$ ssh host01 'hostname'
host01 # without hitting the password
...
Chainerのコードを改良してChainerMNを使う手順
 説明にはMNISTのサンプルを利用
 完成コード
– https://github.com/chainer/chainermn/blob/master/examples/mnist
/train_mnist.py
 最低限必要な変更点
1. Communicatorの作成と使用するGPUの指定
2. マルチノード用のOptimizerの作成
3. データの分配
4. マルチノード用のEvaluatorの作成
33
1. Communicatorの作成と使用するGPUの指定
 Communicatorの作成
– 高い並列性能を実現するためには計算機環境に合わせてCommunicatorを
選択する必要がある。詳しくは以下を参照
 https://chainermn.readthedocs.io/en/latest/reference/index.html#communicators
 プロセスが使用するGPUを指定
– ChainerのMNISTのサンプル:
– ChainerMNのMNISTのサンプル:
34
# args.gpu: 使用するGPUのID
chainer.cuda.get_device(args.gpu).use()
# comm.intra_rank:マシン内におけるワーカーの番号
device = comm.intra_rank
chainer.cuda.get_device(device).use()
comm = chainermn.create_communicator()
2. マルチノード用のOptimizerの作成
 マルチノード用のOptimizerを以下のようにして作成
– ChainerのMNISTのサンプル:
– ChainerMNのMNISTのサンプル:
35
optimizer = chainer.optimizers.Adam()
optimizer = chainermn.create_multi_node_optimizer(
chainer.optimizers.Adam(), comm)
3.データの分配
 2.までの状態では、1つのワーカーだけで改良前と同じサイズのデータを学習
– このため、Chainerのサンプルよりも1エポックのバッチサイズが、ワーカー数倍増加
 改良前とバッチサイズを同じ大きさにするために、1つのワーカーがデータの読
み込みを行い、全ワーカーに分割して配る
– ChainerのMNISTのサンプル:
– ChainerMNのMNISTのサンプル:
36
train, test = chainer.datasets.get_mnist()
if comm.rank == 0: # rank 0 のワーカーが読み込み
train, test = chainer.datasets.get_mnist()
else:
train, test = None, None
# データを分割して配る
train = chainermn.scatter_dataset(train, comm)
test = chainermn.scatter_dataset(test, comm)
4. マルチノード用のEvaluatorの作成
 マルチノード用のEvaluatorを以下のように作成
– ChainerのMNISTのサンプル:
– ChainerMNのMNISTのサンプル:
– 注意
 PrintReportなどのExtensionは1つのワーカーだけ実行するようする
37
trainer.extend(extensions.Evaluator(test_iter, model, device=args.gpu))
evaluator = extensions.Evaluator(test_iter, model, device=device)
evaluator = chainermn.create_multi_node_evaluator(evaluator, comm)
trainer.extend(evaluator)
実行!
 シングルノード実行
– $ mpiexec -n 4 python train_mnist.py –g
 1つのノードで4つのワーカーを立ち上げ、4個のGPUを使って分散学習を行う
 マルチノード実行
– $ mpiexec -n 8 -host host00,host01 python train_mnist.py –g
 host00,host01 という2つのノードで、4つずつワーカーが立ち上げ、
合計8個のGPUを使って分散学習を行う
38
よくある質問その1
39
ChainerMN を使ったら遅くなりました
通信が遅いと速くなりません
All-Reduceなど、逐次処理の部分の割合が多いと、
いくら並列化しても速くなりません。この後紹介する方法で高速化してみてください
よくある質問その2
40
MNISTが速くならないんですが・・・
データサイズが小さいMNISTは速くなり
ません
MNISTのexampleは動作確認するには十分ですが、速度を測定するには不適切です
高速化への道
41
ChainerMNの高速化の流れ
1. まずはプロファイリング
2. バッチサイズを頑張ってあげる
3. 適切なcommunicatorを使う
42
アムダールの法則
 計算機の並列度を上げた場合に、全体として期待できる全体の性能向上の程度
を数式として表現したもの (by Wikipedia先生)
 期待できる並列化による性能向上は以下の通り
1
1 − 𝑃 +
𝑃
𝑁
ChainerMNでいえば、少なくともAll-Reduceの部分は逐次実行の部分になる
ので、All-Reduceにかかる割合が全体の割合に比べて大きい場合、並列度をい
くら上げても速くならない
43
𝑃: 並列化できる処理の割合
𝑁: 並列度
1 − 𝑃 の部分(逐次実行の部分)が大きいと
並列度を上げても速くならない
まずはプロファイル
 高速化する前に必ずプロファイルを取る
– どこが遅いのか定量的に把握するために重要
– All-Reduce以外でも逐次処理になっている部分があれば、その割合を見積もり、並列
化によって本当に速くなるのか確認
– 関数レベルの高速化で十分速くなる場合も多いので、そのようなものがないかこれで
確認
 Chainer, ChainerMNで使える代表的なプロファイラ
– cProfile (Python標準)
– NVPROF (CUDA用のプロファイラ)
44
cProfile使ったプロファイルの取り方
 Pythonに標準で入っているプロファイラ。基本的なことは大体できる。
 ChainerMNの場合、以下のようにすれば、学習部分のプロファイルが取れる
45
import cProfile
import pstats
…
pr = cProfile.Profile()
pr.enable()
# trainの実行部分
trainer.run()
pr.disable()
# 時間のかかっている関数が上にくるようにソート
stats = pstats.Stats(pr)
print(“sort by cumtime”)
stats.sort_stats('cumtime')
print(stats.print_stats())
NVPROFを使ったプロファイルの取り方
 CUDA関係用のプロファイラ
– 参考: http://docs.nvidia.com/cuda/profiler-users-guide/index.html#prepare-
application
 コードを変更しなくても以下のように実行するだけでプロファイルが取れる
– $ mpiexec -n 2 nvprof -o ‘%h-%p.nvprof’ python
examples/mnist/train_mnist.py –g
 プロファイル結果はGUIのNVVPで見ると細かいところも見れてわかりやすいの
でおすすめ
– 参考: https://developer.nvidia.com/nvidia-visual-profiler
– ただし、ChainerMNのプロファイル結果は重いことが多いので、数イテレーションで
止まるようにしたり、以下を参考にして、大きいファイルも読み込めるようにすると
良い
http://docs.nvidia.com/cuda/profiler-users-guide/index.html#large-data
46
バッチサイズを頑張ってあげる
 All-Reduceの割合が大きい場合、並列化によって速くするには1ノードあたりの
バッチサイズを大きくする
– 1ノードあたりのバッチサイズが大きくなると、1イテレーションあたりのAll-
Reduce以外の処理でかかる時間が相対的に大きくなるので、並列化効率が上昇する
 精度が落ちていないかを確認し、落ちている場合は学習率等のパラメータを調
整する必要がある 47
ただし、バッチサイズを大きくした場合、
精度が低下する可能性大!
参考: [Goyal+ 2017]
ImageNetの学習で最近注目な手法
 Goyal, P et al. (2017). Accurate, Large Minibatch SGD: Training
ImageNet in 1 Hour.
– http://arxiv.org/abs/1706.02677
 Smith, S. L. et al. (2017). Don’t Decay the Learning Rate, Increase the
Batch Size.
– https://arxiv.org/abs/1711.00489
48
Goyal, P et al. (2017). Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour.
以下の手法により、精度を落とさずにバッチサイズを大きくすることに成功し、
256GPUを使って1時間でImageNetの学習を完了
 学習率のLinear Scaling Rule
– バッチサイズを𝑘倍したら、それに合わせて学習率𝜂を𝑘倍する
 Gradual warmup
– 学習のスタートは学習率を𝜂に設定し、各イテレーション毎に徐々に大きくし、
5epochのところで学習率を𝑘𝜂になるように調整
49
Smith, S. L. et al. (2017). Don’t Decay the Learning Rate, Increase the Batch Size.
 良く使われるDecaying learning rateの代わりに、バッチサイズを大きくする
50
適切なcommunicatorを使う
 使っているシステムによってはCommunicatorを変えることで、All-Reduceが
速くなる場合がある
 2017年11月現在のおすすめはPureNcclCommunicator
– All-ReduceをNCCLに丸投げするCommunicator
– NCCLがネットワーク構成を自動で判定し、ネットワーク構成に適したAll-Reduceを
勝手にやってくれる
51
高速化の具体例紹介:ImageNetを1024GPUで学習する際に何をしたのか?
Goyal+ 2017の論文からバッチサイズを大きくするために、以下の部分を改良
1. RMSprop Warm-up
 SGDの代わりに最初はRMSpropを使って最適化
2. Slow-Start Learning Rate Schedule
 学習率を下げるタイミングを変更
3. Batch Normalization without Moving Averages
 移動平均を使わないようにパラメータを設定
また、以下のように学習の高速化を実施 今回はここを詳しく紹介
1. PureNcclCommunicatorで1024GPUを利用
2. Convolutionのauto tuneを活用
3. FLOAT16を使ったAll-Reduce
4. NCCLパラメータのチューニング
52
PureNcclCommunicatorで1024GPUを利用
一見、簡単に動きそうな気がするが、1024 GPUの規模でNCCLを使う場合
様々な問題が発生するので、以下のように対処
 NCCLの大量file descripter open問題
– 解決方法: ulimit -n unlimited
 NCCLのスタックの大量使用
– 解決方法: ulimit -s unlimited
 NCCLの謎‘unhandled system error’
– 解決方法:全ノードのreboot
53
Convolutionのauto tuneを活用
 cuDNNが実装しているConvolution用の関数のうち、もっとも高速なも
のを自動で選択
– Chainer 3.1(2017/11/17にリリース)からの追加機能
– これによって数%の速度が向上
 Auto tuneを使う場合は以下のようにすればよい
54
chainer.global_config.autotune = True
FLOAT16を使ったAll-Reduce
 Forward, Backward等の計算ではFLOAT32を使い、All-Reduceの時のみ、
FLOAT16で実行
 これにより、通信するデータ量が半分になり、All-Reduceにかかる時間も短縮
– ResNet-50の場合、100MB→50MBに削減
– 今回の場合、ほとんど精度に影響がないことは一応確認
 現在は、ChainerMNで未実装だが、今後サポートする予定
55
All-ReduceForward Backward Optimize… …
All-ReduceForward Backward Optimize… …FP32→FP16 FP16→FP32
NCCLのパラメータチューニング
 NCCLの以下のようなパラメータを変更
– NCCL_BUFFSIZE:通信の際のバッファサイズ
– NCCL_RINGS:通信を行う際のリングの指定
 特に、NCCLで使っているリングがどうなっているのか確認することは重要
– 環境変数で以下のようにすると、どのGPU間で通信するのか確認できる (NCCLのド
キュメントにはINFOがないが、NCCL 2.0.5ではまだ使える)
 $ export NCCL_DEBUG=INFO
 ネットワーク構成によっては、ちゃんと最適化すると数%~十数%は速度が改
善
56
ChainerMNの今後
57
ChainerMNの今後
 ChainerMNでは今後以下のような機能を追加予定
– モデル並列用の機能の拡充
 実験的に追加されているモデル並列用の機能をもっと使いやすくする
– Double bufferingを活用したAll-Reduceの隠ぺい
 All-Reduceとその他の処理をオーバーラップさせることで、全体に占めるAll-
Reduceの処理時間の割合を削減
– フォールトトレランス機能の拡充
– FLOAT16対応
58
まとめ
 ChainerMNとは
– Chainerによる学習を、分散処理によって高速化するための追加パッケージ
– 特徴
 高速、柔軟、簡単
– GitHubリポジトリ
 https://github.com/chainer/chainermn
 ChainerMNの使い方
– Communicatorの作成と使用するGPUの指定、マルチノード用のOptimizer
の作成だけで簡単にChainerMN対応可能
 高速化への道
– 1. プロファイリング取る、2. バッチサイズを頑張ってあげる、3. 適切な
communicatorを使う
59