ディープラーニングのための GPU講習会- 九州大学スーパーコンピュータシステム ITOによる実習 -ハンズオン#2:
マルチ GPUを用いたディープラーニングの高速化
山崎和博
NVIDIA, ディープラーニング ソリューションアーキテクト

ハンズオン#2のテーマ: 分散学習

AGENDA

タスク#1: マルチGPUでの学習(option)タスク#2: マルチノードでの学習ハンズオン#2のゴール
複数のGPU/ノードを利用できるようになる• ITOの複数ノードを利用してジョブを流す方法を把握する• マルチGPU/マルチノードでの学習方法を理解する3

ハンズオン#2のテーマ: 分散学習

4

分散学習
モデル/データの大規模化モデルの複雑化
LeNet
5layers

GoogLeNet
24layers

ResNet152layers?

データの拡大?

ImageNet
256x256, 1M imgs

MNIST
28x28,60K imgs
5

分散学習
マルチGPU/マルチノードで分散して学習

データ並列（data parallelism）

モデル並列（model parallelism）
Large Scale Distributed Deep Networks より 6

分散学習
マルチGPU/マルチノードで分散して学習• 複数GPUで同じネットワーク（パラメータ）を共有• データを分割してそれぞれ勾配計算し、パラメータを更新→ 学習時間の短縮が期待できる
データ並列（data parallelism）
Large Scale Distributed Deep Networks より

7

分散学習
マルチGPU/マルチノードで分散して学習• ネットワーク（パラメータ）を複数GPUで分散して保持• 同じデータに対し、各GPUが協調してパラメータを更新→ 1GPUに乗り切らない大規模なネットワークを処理できる可能性
モデル並列（model parallelism）
Large Scale Distributed Deep Networks より 8

分散学習における注意点
バッチサイズと精度の関係
バッチサイズによって、同じ学習回数で達成できる精度が変化する

分散学習すると、トータルのバッチサイズは大きくなりがち→ 問題になる
可能性あり

https://arxiv.org/abs/1705.08741 より

9分散学習における注意点
バッチサイズと精度の関係
バッチサイズによって、分散学習すると、学習率やバッチサイズを調整することで、精度低下を回避する研究が多数
同じ学習回数でトータルのバッチサイズ• Train longer, generalize
better:closingthegeneralization
gap
達成できる精度がは大きくなりがちin large batch training
ofneuralnetworks
変化する→ 1問題になる• Accurate, Large Minibatch SGD: Training ImageNet inHour
可能性あり• LARGE BATCH TRAINING OF CONVOLUTIONAL NETWORKS• Don't Decay the Learning Rate, Increase the Batch Size……が、今日は細かいところに踏み込みません

https://arxiv.org/abs/1705.08741 より

10

タスク#1: マルチGPUでの学習11

マルチGPUで学習ジョブを流す
簡単な例で、ジョブの流し方を把握する

以下の作業を完了させる。

1. train_cifar_multi_gpu.pyに「PUT YOUR CODE」という箇所があるので、書き換えてプログラムを完成させる
2. スクリプトの修正完了したら実行
1. pjsub run_multi_gpu_training.sh

3. 結果を確認
12

スクリプトの変更箇所
マルチGPU向けにデータ用iteratorを変更

メインのGPUとして使うものを指定

マルチGPU用の更新ロジックを指定

デバイス(=GPU)のリストを作成
テストで使うGPUを指定13

CHAINERのAPI
学習の実行• ネットワークの更新ロジックを指定•

chainer.training.updaters.MultiprocessParallelUp
dater(iterators, optimizer, devices)•

基本はシングルGPUのクラスと同じ使い方••

GPUへのモデル転送は明示的に実施する必要なし

学習データのイテレータをGPU数と同じ数準備する•

デバイス名をキーとする辞書(dict)を作成•

メインのGPUのみデバイス名を「main」にする必要あり14

メインのGPU？
ChainerのMultiprocessParallelUpdaterにおけるData Parallelism
シングルGPUの場合Forward

Backward

マルチGPUの場合
Forward計算した勾配を全GPUで共有してモデル更新

UpdateBackward
UpdateForwardBackward
1iteration15

メインのGPU？
ChainerのMultiprocessParallelUpdaterにおけるData Parallelism
シングルGPUの場合
ForwardBackward

マルチGPUの場合
Forward

ForwardUpdate

集約した勾配を使ってモデル更新

各GPUで計算した
Backward
勾配をメインに集約
Backward※ ChainerMNとは、異なる計算モデルを使っているようです

各GPUへ更新済み
モデルパラメータを配布

1iteration
16

CHAINERのAPI
データの分割• chainer.datasets.split_dataset_n_random(dataset, n)•

学習データのイテレータ作成時に使用•データをランダムにGPU数分割

17

データ分割？
各GPUへの分配
シングルGPUの場合

マルチGPUの場合

特に何もしないと、1epochで処理されるデータ量がGPU数倍に

18

データ分割？
各GPUへの分配
シングルGPUの場合

マルチGPUの場合各GPUで分担してトータルのデータ量を統一

19スクリプトの修正を試みましょう

コンソールに戻ってください！

20

スクリプトの変更箇所(答え; 1/2)
train_iter=[chainer.iterators.SerialIterator(sub_train, args.batchsize) \for sub_train \in chainer.datasets.split_dataset_n_random(train, args.n_gpus)]

master_gpu_id=0.get_device_from_id(master_gpu_id).

21スクリプトの変更箇所(答え; 2/2)
devices_list={'main': master_gpu_id}
devices_list.update({'gpu{}'.format(i):i for i in range(1, args.n_gpus)})device=0
updater=training.updaters.MultiprocessParallelUpdater(

train_iter, optimizer, devices=devices_list)
22

マルチGPUでの学習実行結果
Start a training script using single GPU.
# Minibatch-size: 32# epoch: 3

Using CIFAR10 dataset: /home/tmp/dl_lecture/20180511_handson/data//cifar10_256px_5p.pickle.gz
epoch main/loss
validation/main/loss main/accuracy validation/main/accuracy elapsed_time
1
3.97842.32899
0.11788
0.117578
53.34992
3.339432.36698
0.117388
0.0957031
101.6163
2.44274
2.238940.183894
0.157422
149.873
Throughput: 50.04068014993887 [images/sec.] (7500 / 149.87805876194034)
Start a training script using multiple GPUs.
# GPUs: 4# Minibatch-size: 32# epoch: 3

シングルGPUだと50.0images/sec.

Using CIFAR10 dataset: /home/tmp/dl_lecture/20180511_handson/data//cifar10_256px_5p.pickle.gz{'main': 0, 'gpu1': 1, 'gpu2': 2, 'gpu3': 3}/home/tmp/dl_lecture/20180511_handson/app/pyenv/versions/3.6.0/lib/python3.6/sitepackages/chainer/training/updaters/multiprocess_parallel_updater.py:148: UserWarning: optimizer.lr is changed to 0.0125 byMultiprocessParallelUpdater for new batch size.
format(optimizer.lr))
epoch main/loss
validation/main/loss main/accuracy validation/main/accuracy elapsed_time
12.58079
33.0115
0.109375
0.112891
37.26562
2.73551
2.334850.11875
0.109375
51.93923
2.87886
2.35601
0.121711
0.10625
66.0055
23
Throughput: 113.61732577778486 [images/sec.] (7500 / 66.0110590410186)

同条件で4GPU使うと113.6images/sec.と高速に

マルチGPUでの学習実行結果
Start a training script using single GPU.
# Minibatch-size: 32# epoch: 3

Using CIFAR10 dataset: /home/tmp/dl_lecture/20180511_handson/data//cifar10_256px_5p.pickle.gz
epoch main/loss
validation/main/loss main/accuracy validation/main/accuracy elapsed_time
1
3.97842.32899
0.11788
0.117578
53.34992
3.339432.36698
0.117388
0.0957031
101.61632.44274
2.238940.183894
0.157422
149.873
Throughput: 50.04068014993887 [images/sec.] (7500 / 149.87805876194034)
Start a training script using multiple GPUs.
# GPUs: 4# Minibatch-size: 32# epoch: 3

シングルGPUだと50.0images/sec.

4GPU使っているのにたった約2.3倍？
Using CIFAR10 dataset: /home/tmp/dl_lecture/20180511_handson/data//cifar10_256px_5p.pickle.gz{'main': 0, 'gpu1': 1, 'gpu2': 2, 'gpu3': 3}/home/tmp/dl_lecture/20180511_handson/app/pyenv/versions/3.6.0/lib/python3.6/sitepackages/chainer/training/updaters/multiprocess_parallel_updater.py:148: UserWarning: optimizer.lr is changed to 0.0125 byMultiprocessParallelUpdater for new batch size.
format(optimizer.lr))
epoch main/lossvalidation/main/loss main/accuracy validation/main/accuracy elapsed_time
12.58079
33.0115
0.109375
0.112891
37.26562
2.73551
2.334850.11875
0.109375
51.93923
2.87886
2.35601
0.121711
0.10625
66.0055
24
Throughput: 113.61732577778486 [images/sec.] (7500 / 66.0110590410186)

同条件で4GPU使うと113.6images/sec.と高速に

マルチGPUによる高速化の詳細
高速化のキモは？
シングルGPUの場合
PreProc.

例: 200 iter.
FB

UF

B

F

例: 100 iter.

マルチGPUの場合
PreProc.

UFB

FB CUFC FB

B CUCFBF

B C

B

U

並列化によって総処理時間が短くなることを期待

U

C
time
25

マルチGPUによる高速化の詳細
高速化のキモは？
シングルGPUの場合
PreProc.

例: 20 iter.
FB

U

F

B

前処理のような共通して時間を要する箇所の比率が大きくF

例: 10 iter.

マルチGPUの場合
PreProc.

UF

B

F

B C

U

F

C F

B

B C

U

C

B

U

繰り返し回数が少ない間は並列化の効果が見えにくい
F B
U
F

B C

C
time
26

マルチGPUによる高速化の詳細
Start a training script using single GPU.
# Minibatch-size: 32# epoch: 50
Using CIFAR10 dataset: /home/tmp/dl_lecture/20180511_handson/data//cifar10_256px_5p.pickle.gz
epoch main/lossvalidation/main/loss main/accuracy validation/main/accuracy elapsed_time
13.86343
13.6284
0.111946
0.130859
53.8101(中略)
50
0.185783
2.09066
0.942708
0.472266
2423.78
Throughput: 51.297360859528304 [images/sec.] (125000 / 2436.772533820942)
Start a training script using multiple GPUs.
# GPUs: 4# Minibatch-size: 32

シングルGPUの場合は50epochでも変化なし# epoch: 50
Using CIFAR10 dataset: /home/tmp/dl_lecture/20180511_handson/data//cifar10_256px_5p.pickle.gz{'main': 0, 'gpu1': 1, 'gpu2': 2, 'gpu3': 3}/home/tmp/dl_lecture/20180511_handson/app/pyenv/versions/3.6.0/lib/python3.6/sitepackages/chainer/training/updaters/multiprocess_parallel_updater.py:148: UserWarning: optimizer.lr is changed to 0.0125 byMultiprocessParallelUpdater for new batch size.
format(optimizer.lr))
epoch main/loss
validation/main/loss main/accuracy validation/main/accuracy elapsed_time
12.82759
45.4497
0.104687
0.144922
39.0766(中略)
50
0.252575
3.72759
0.921053
0.260156
746.626
27
Throughput: 164.73705493653375 [images/sec.] (125000 / 758.7849621820496)

50epoch回すと4GPUで164.7images/sec.の約3.2倍

(OPTION)
タスク#2: マルチノードでの学習28

マルチノードで学習ジョブを流す
簡単な例で、ジョブの流し方を把握する

以下の作業を完了させる。

1. train_cifar_multi_node.pyに「PUT YOUR CODE」という箇所があるので、書き換えてプログラムを完成させる
2. スクリプトの修正完了したら実行
1. pjsub run_multi_node_training.sh

3. 結果を確認
29

ジョブスクリプト
マルチノード利用の条件指定

pjsubコマンドの引数には以下などを指定#!/bin/bash
可能#PJM –L (pjsubコマンドの引数)• rscunit サブシステム名#PJM –j (標準エラーを標準出力にまとめる)• rscgrp リソースグループ名#PJM –X (環境変数引き継ぎ) • elapse 最大実行時間• vnode 仮想ノード数• vnode-core ノードあたりの最大コア数...
講習会用のリソースグループは2ノード指定の例)
1ノードまでなので注意……
–L ”vnode=2”30

マルチノード向けジョブスクリプト
軽い解説
ChainerMNを試してみたい方は……
• vnode=1• mpirun –n 4としてください

使用ノード数を2に(講習会では指定不可)

CPUソケットごとに2プロセス、トータル8プロセス起動

31

スクリプトの変更箇所(1/2)
MPIプロセス情報を参照してどのGPUを割り当てるか指定

元データは親相当のプロセスのみロードするよう制御

各プロセスにデータを分配
32

スクリプトの変更箇所(2/2)

マルチノード実行向けに最適化ロジックをラップ

マルチノード環境で適切に評価が動くようラップ
33

CHAINERMNのAPI
環境情報の取得• communicator.rank•

ワーカーID(プロセスID)を取得• communicator.intra_rank•

ノード内のプロセスIDを取得• ノード内2プロセスで、2ノード使用の場合の例•ノード1: rank=0&intra_rank=0, rank=1&intra_rank=1•

ノード2: rank=2&intra_rank=0, rank=3&intra_rank=134

マルチノード実行時の状況
GPUとプロセス: ノード内4プロセス、2ノードの例
ノード#0

Rank=0Intra_rank=0
Rank=1
Intra_rank=1

ノード#1

Rank=2Intra_rank=2

Rank=4Intra_rank=0

Rank=6
Intra_rank=2

Rank=3Intra_rank=3

Rank=5Intra_rank=1

Rank=7Intra_rank=335

CHAINERMNでの分散ロジック
All-Reduceの利用
シングルGPUの場合
ForwardBackward

マルチGPUの場合
Forward

ForwardUpdate

共有された勾配でGPUごとにモデル更新

Backward

UpdateBackward

UpdateAll-ReduceによりGPU間で勾配を集約・共有

1iteration
36

CHAINERMNのAPI
分散学習用のAPI• chainermn.scatter_dataset(dataset, comm,shuffle)•

データセットを分割して、各プロセスに配布• chainermn.create_multi_node_optimizer(actual_optimizer, communicator)•

マルチノード環境で動作するよう、optimizerをラップ• chainermn.create_multi_node_evaluator(actual_evaluator, communicator)•

マルチノード環境で動作するよう、evaluatorをラップ

37

スクリプトの修正を試みましょう

コンソールに戻ってください！

38

スクリプトの変更箇所(答え; 1/2)
device = comm.intra_rank

if comm.rank == 0:

train=chainermn.scatter_dataset(train, comm, shuffle=True)

test=chainermn.scatter_dataset(test, comm, shuffle=True)39スクリプトの変更箇所(答え; 2/2)

optimizer=chainermn.create_multi_node_optimizer(chainer.optimizers.MomentumSGD(0.05), comm)evaluator=chainermn.create_multi_node_evaluator(evaluator, comm)40

マルチノードでの学習実行結果(前略)
Start a training script using multiple nodes.
==========================================
Num process (COMM_WORLD): 8
Using GPUs
Using non_cuda_aware communicator
Num Minibatch-size: 32
Num epoch: 4==========================================
Using CIFAR10 dataset: /home/tmp/dl_lecture/20180511_handson/data//cifar10_256px_5p.pickle.gz[scb0038:92367] 7 more processes have sent help message help-mpi-btl-openib.txt / default subnet
prefix[scb0038:92367] Set MCA parameter "orte_base_help_aggregate" to 0 to see all help / error messages
epoch main/loss
validation/main/loss main/accuracy validation/main/accuracy elapsed_time
2ノードx4GPUでは12.51613
3584.920.153125
0.128906
14.7621(中略)
264.9images/sec.
4
2.62698
2.357030.131250.105217
37.7453
Throughput: 264.9120710495522 [images/sec.] (10000 / 37.74837424501311)(後略)
41

マルチノードでの学習実行結果(前略)
Start a training script using multiple nodes.
==========================================
Num process (COMM_WORLD): 8
Using GPUs
Using non_cuda_aware communicator
Num Minibatch-size: 32
Num epoch: 50==========================================
Using CIFAR10 dataset: /home/tmp/dl_lecture/20180511_handson/data//cifar10_256px_5p.pickle.gz
シングルGPU(51.3img/s)に対して[scb0038:92367] 7 more processes have sent help message help-mpi-btl-openib.txt / default subnet
約6.5倍
prefix[scb0038:92367] Set MCA parameter "orte_base_help_aggregate" to 0 to see all help / error messages
epoch main/loss
validation/main/loss main/accuracy validation/main/accuracy elapsed_time
50epoch回すと
12.743992348.71
0.118750.0895287
12.7662(中略)
331.7images/sec.
500.165634
2.696170.934375
0.354839
376.877
Throughput: 331.66972621055066 [images/sec.] (125000 / 376.88094547600485)(後略)42

(FYI) 性能が思うように出ないときにプロファイラによる解析• 十分にGPUを使い切れているかどうかを確認する際、プロファイラが有効なことがある• コマンドはnvprof• 結果の可視化にはNVIDIA Visual Profilerを使う• 実行するには対象コマンドcmdに対し、nvprof -o ${resultfile} cmdとすればOK• オプション--profile-child-processesを追加すると子プロセスも追跡できる• 子プロセスを追うときは、出力ファイル名に%pを含める（ファイル名にプロセスIDを含めるため）43

(FYI) 性能が思うように出ないときにプロファイラによる解析

シングルGPUでの実行例

Nvprof -o ${filepath} cmd--profile-child-processes

マルチGPUでの実行例

44www.nvidia.com/dli