Akira Naruse, Senior Developer Technology Engineer, 2018/12/15
Chainer で Tensor コア (fp16) を
使いこなす
2
ドーナツで、発表枠を、頂戴しました
128個
90個
3
最初におことわり…
タイトル:
• Chainer で Tensor コア (fp16) を使いこなす
中身:
• ありとあらゆる手段を使い、Chainer で Tensor コア (fp16) を
どこまで使いきれるかを、実験した
4
VOLTA TENSOR コア
FP16/FP32 混合精度行列演算ユニット
Tesla P100 Tesla V100
FP16 20 TFLOPS 125 TFLOPS
(Tensor コア)
FP32 10 TFLOPS 16 TFLOPS
FP16
FP16
× + FP32
FP32
FP16 FP32
5
FP16 で学習して、精度は大丈夫?
• 2 つのテクニック
• ロス スケーリング
• ロス値をスケールアップし、勾配消失を緩和
• ウェイト更新直前に、勾配値をスケールダウン
• FP32 ウェイト更新 (FP16 と FP32 の併用)
• Forward/Backward は FP16 で計算
• Update で FP32 を使用
Mixed Precision Training
https://devblogs.nvidia.com/mixed-precision-training-deep-neural-networks/
勾配値の分布
(*) FP16 にすると、多くの勾配値がゼロになってしまう
6
ロス スケーリングと FP32 ウェイト更新
勾配勾配勾配勾配勾配勾配勾配
損失
関数
誤差
Backward
Forward
Update
勾配をスケールダウンし (例: 1/10 倍)、
FP32 ウェイト (master) を更新し、
FP16 ウェイトにコピーする
Backward 直前に誤差をスケールアップし
(例: 10 倍)、勾配消失を回避する
7
FP16 で精度を維持する方法
• FP16 で学習しても、FP32 モデルとほぼ同じ精度
• 主要な DL フレームワークで利用可能
• TensorFlow
• PyTorch
• MxNet
• Chainer
Mixed Precision Training
https://devblogs.nvidia.com/mixed-precision-training-deep-neural-networks/
Better
8
本日のお題
ResNet-50 のトレーニングを
Tensor コア/fp16 を用いて
Chainer で速くする
(*) K. He, et.al., Deep Residual Learning for Image Recognition, arxive:1512.03385
Bottleneck architecture
ResNet-34 ResNet-50
9
テスト環境
Machine: DGX Station
GPU: Tesla V100-16GB
CUDA: 9.2
cuDNN: 7.4.1
Chainer: v6.0.0b1+
CuPy: v6.0.0b1+
10
注意事項: その1
cuDNN のワークスペースサイズを大きめに設定する
chainer.cuda.set_max_workspace_size(512*1024*1024) # 512MiB
cuDNN の Auto Tuning を使う
config.autotune = True
(*) そうしないと、適切なアルゴリズムが選択されない、ことが多い
cuDNN の高速 Batch Normalization 実装を使う
config.cudnn_fast_batch_normalization = True
(*) fp32 でも有効、オーバーフローの可能性有るので注意
cuDNN tips
11
注意事項: その2
モデル記述を fp16 向けに修正
• 入力: fp16 に cast
• Initializer、Batch normalization: dtype に fp16 指定
本当は、fp16 モードをセットするだけで OK なんだけど..
CHAINER_DTYPE=float16
Chainer tips
def forward(self, x, t):
- h = self.bn1(self.conv1(x))
+ h = self.bn1(self.conv1(F.cast(x, np.float16)))
h = F.max_pooling_2d(F.relu(h), 3, stride=2)
self.conv1 = L.Convolution2D(3, 64, 7, 2, 3,
- initialW=initializers.HeNormal())
- self.bn1 = L.BatchNormalization(64)
+ initialW=initializers.HeNormal(dtype=np.float16))
+ self.bn1 = L.BatchNormalization(64, dtype=np.float16)
12
性能比較
良い点:
• BS: 128 以上で、fp32 より速い
• BS: 256で学習可能 (fp32 だと
メモリ不足)
悪い点:
• BS: 64 以下だと、fp32 より遅い
FP32 vs. FP16
0
100
200
300
400
500
600
700
800
900
bs:32 bs:64 bs:128 bs:256
Images/sec
fp32 fp16
Better
13
TRAINING ITERATION
CPU の仕事
• IO: 入力画像の前処理 (Decode,
Augmentation)
• Forward/Backward/Update: 計算グラフ管理,
メモリ管理, GPU カーネル投入
IO Forward Backward Update
GPU の仕事
• Forward/Backward/Update: CPU
から投入されたカーネルを実行
14
性能比較
良い点:
• BS: 128以上で、fp32 より速い
• BS: 256で学習可能 (fp32 だと
メモリ不足)
悪い点:
• BS: 64以下だと、fp32 より遅い
FP32 vs. FP16
0
100
200
300
400
500
600
700
800
900
bs:32 bs:64 bs:128 bs:256
Images/sec
fp32 fp16
Better
15
NVVP で挙動を確認 (FP32, BS: 64)
高い GPU 稼働率
FWD BWD UPDIO
CPU
FWD BWD
GPU
拡大
16
NVVP で挙動を確認 (FP32, BS: 64)
拡大図
高い GPU 稼働率
FWD BWD
17
NVVP で挙動を確認 (FP16, BS: 64)
GPU稼働率が低い
FWD BWDIO
CPU
FWD BWDGPU
Tensor コア使用で GPU 処理は速くなった。しかし、CPU の処理が間に合わない
18
NVVP で挙動を確認 (FP16, BS: 64)
拡大図
GPU 稼働率が低い
FWD BWD
19
対策: CPU の負荷を減らす
IO 処理を高速化、別スレッドで実行
• NVIDIA DALI
GPU 操作回数、GPU カーネル数を削減
• Batched weight update、 Fused Bnorm/ReLu、 NHWC format
計算グラフ管理負荷を削減
• Chainer static_graph
20
NVIDIA DALI
https://github.com/NVIDIA/DALI (オープンソース)
前処理は別スレッドで実行
一部前処理は GPU 利用して加速
Chainer、DALI 対応済
(experimental)
• 詳しくは、examples/imagenet を参照
21
NVIDIA DALIの効果
良い点:
• BS: 64以上で、fp32 より速い
• BS: 128 はBS: 256 とほぼ同性能
悪い点:
• BS: 32以下だと、fp32 より遅い
0
100
200
300
400
500
600
700
800
900
bs:32 bs:64 bs:128 bs:256
Images/sec
fp32 fp16 fp16 (+dali)
Better
22
NVIDIA DALI の効果確認 (FP16, BS: 64)
IO 処理は、他とオーバーラップ
FWD BWD
FWD BWD
IO by DALI (next)
CPU
GPU
依然として、CPU の処理が間に合っていない
23
対策: CPU の負荷を減らす
IO 処理を高速化、別スレッドで実行
• NVIDIA DALI
GPU 操作回数、GPU カーネル数を削減
• Batched weight update、 Fused Bnorm/ReLu、 NHWC format
計算グラフ管理負荷を削減
• Chainer static_graph
24
BATCHED WEIGHT UPDATE
Chainer optimizer は、レイヤー毎(より正確にはパラメータ毎)に、重みを更新
• 多数の GPU カーネル起動 → CPU 負荷大
Single GPU カーネルで、全ての重みを更新 (batched_update)
Chainer #5841
PR中
update
update
CPU
GPU
25
FUSED BNORM/RELU
• cuDNN 7.4 から導入
• Fp16 and NHWC フォーマット
(後述) のときに利用可能
• レイヤーの自動融合は大変。
bnorm_add_relu モジュールを試作。
モデル記述を修正。
GPU カーネル数の削減、メモリアクセス量を削減
試作
26
NHWC FORMAT
• NCHW だと、Convolution 前後
で、合計 3 回の Tensor
Transpose が必要
• NHWC なら、Transpose 不要
→ GPU カーネル数削減
• Chainer は NCHW only
• NHWC 専用の
Convolution/Bnorm モジュール
を試作
cuDNN Convolution for TensorCore: NHWC format で高い性能を発揮
NCHW NHWC
試作
27
BATCHED+FUSED+NHWC の効果
良い点:
• 常に fp32 より速い
• BS: 256 で、1200 imgs/sec
悪い点 (相対的に):
• BS: 32は、BS: 256 の 1/3 の
性能0
200
400
600
800
1000
1200
1400
bs:32 bs:64 bs:128 bs:256
Images/sec
fp32 fp16 (+dali) fp16 (+nhwc, fused, batched)
Better
28
効果確認 (FP16, BS: 64)
NHWC+FUSED+BATCHED
FWD BWD
FWD BWD
IO by DALI (next)
CPU
GPU
CPU 負荷を減らしたが、GPU 時間も NHWC+FUSED で短縮 → まだ GPU に空きがある
29
対策: CPU の負荷を減らす
IO 処理を高速化、別スレッドで実行
• NVIDIA DALI
GPU 操作回数、GPU カーネル数を削減
• Batched weight update、 Fused Bnorm/ReLu、 NHWC format
計算グラフ管理負荷を削減
• Chainer static_graph
30
STATIC_GRAPH
説明省略
31
STATIC_GRAPHの効果
良い点:
• BS: 32/64 の性能改善
悪い点:
• BS: 128 以上で実行不可
(メモリ使用量が増える?)
0
200
400
600
800
1000
1200
1400
bs:32 bs:64 bs:128 bs:256
Images/sec
fp32 fp16 (+nhwc, fused, batched) fp16 (+static_graph)
Better
32
効果確認 (FP16, BS: 64)
Static_graph
FWD BWD
FWD BWD
IO by DALI (next)
CPU
GPU
やっと、「CPU 処理時間 < GPU 処理時間」、になった
33
ここまでのまとめ
fp32 の3倍の性能を達成 (最大で)
• DALI
• Batched weight update
• Fused Bnorm/ReLu
• NHWC format
• Static_graph
Backend が python でも、ここまで
速くできる
Tensor コア/fp16 を用いた ResNet-50 トレーニング
0
200
400
600
800
1000
1200
1400
bs:32 bs:64 bs:128 bs:256
Images/sec
fp32 fp16 (base) fp16 (best)
Better
ChainerXで
速くなる?
34
何故 “BS: 32” を速くしたいのか
Goal: ImageNet+ResNet50 with 4096 GPUs
Processor DL framework Time
Microsoft Tesla P100 x8 Caffe 29 hours
Facebook Tesla P100 x256 Caffe2 1 hour
Google TPUv2 x256 TensorFlow 30 mins
PFN Tesla P100 x1024 Chainer 15 mins
Tencent Tesla P40 x2048 TensorFlow 6.6 mins
SONY Tesla V100 x2176 NNL 3.7 mins
Google TPUv3 x1024 TensorFlow 2.2 mins
35
LARGE MINI-BATCH PROBLEM
Local BS: 32 でも、4096 GPU だと、
Global BS: 128K
ImageNet + ResNet50だと、Global
BS: 32~64K でも、validation
error < 25% を出せるようになってき
たが..
SGD の限界?
ImageNet, ResNet50, Local BS:32
(*) P. Goyal, et.al., Facebook,
“Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour”
Better
36
SGD 以外の選択肢
Backprop で得られる「勾配ベクトル」の向きは、
それほど正しくない
KFAC: フィッシャー行列を近似計算して勾配補正
• 学習の「向き」が SGD より正しくなる (反復数減)
• 1 反復あたりの計算量が増える
Global BS: 128K でも、75% の validation
accuracy を実証
• BS 16K 以下なら、30~35 epoch で収束
(SGD は90 epoch 必要)
K. Osawa, et.al., “Second-order Optimization Method for Large Mini-batch:
Training ResNet-50 on ImageNet in 35 Epochs”
(*) 東工大 横田研・松岡研との共同研究
ImageNet + ResNet50
Better
Distributed KFAC optimizer
on Chainer
37
ご案内
「第1 回 ディープラーニング分散学習ハッカソン」
https://www.cc.u-tokyo.ac.jp/events/lectures/111/
Chainer で Tensor コア (fp16) を使いこなす

Chainer で Tensor コア (fp16) を使いこなす

  • 1.
    Akira Naruse, SeniorDeveloper Technology Engineer, 2018/12/15 Chainer で Tensor コア (fp16) を 使いこなす
  • 2.
  • 3.
    3 最初におことわり… タイトル: • Chainer でTensor コア (fp16) を使いこなす 中身: • ありとあらゆる手段を使い、Chainer で Tensor コア (fp16) を どこまで使いきれるかを、実験した
  • 4.
    4 VOLTA TENSOR コア FP16/FP32混合精度行列演算ユニット Tesla P100 Tesla V100 FP16 20 TFLOPS 125 TFLOPS (Tensor コア) FP32 10 TFLOPS 16 TFLOPS FP16 FP16 × + FP32 FP32 FP16 FP32
  • 5.
    5 FP16 で学習して、精度は大丈夫? • 2つのテクニック • ロス スケーリング • ロス値をスケールアップし、勾配消失を緩和 • ウェイト更新直前に、勾配値をスケールダウン • FP32 ウェイト更新 (FP16 と FP32 の併用) • Forward/Backward は FP16 で計算 • Update で FP32 を使用 Mixed Precision Training https://devblogs.nvidia.com/mixed-precision-training-deep-neural-networks/ 勾配値の分布 (*) FP16 にすると、多くの勾配値がゼロになってしまう
  • 6.
    6 ロス スケーリングと FP32ウェイト更新 勾配勾配勾配勾配勾配勾配勾配 損失 関数 誤差 Backward Forward Update 勾配をスケールダウンし (例: 1/10 倍)、 FP32 ウェイト (master) を更新し、 FP16 ウェイトにコピーする Backward 直前に誤差をスケールアップし (例: 10 倍)、勾配消失を回避する
  • 7.
    7 FP16 で精度を維持する方法 • FP16で学習しても、FP32 モデルとほぼ同じ精度 • 主要な DL フレームワークで利用可能 • TensorFlow • PyTorch • MxNet • Chainer Mixed Precision Training https://devblogs.nvidia.com/mixed-precision-training-deep-neural-networks/ Better
  • 8.
    8 本日のお題 ResNet-50 のトレーニングを Tensor コア/fp16を用いて Chainer で速くする (*) K. He, et.al., Deep Residual Learning for Image Recognition, arxive:1512.03385 Bottleneck architecture ResNet-34 ResNet-50
  • 9.
    9 テスト環境 Machine: DGX Station GPU:Tesla V100-16GB CUDA: 9.2 cuDNN: 7.4.1 Chainer: v6.0.0b1+ CuPy: v6.0.0b1+
  • 10.
    10 注意事項: その1 cuDNN のワークスペースサイズを大きめに設定する chainer.cuda.set_max_workspace_size(512*1024*1024)# 512MiB cuDNN の Auto Tuning を使う config.autotune = True (*) そうしないと、適切なアルゴリズムが選択されない、ことが多い cuDNN の高速 Batch Normalization 実装を使う config.cudnn_fast_batch_normalization = True (*) fp32 でも有効、オーバーフローの可能性有るので注意 cuDNN tips
  • 11.
    11 注意事項: その2 モデル記述を fp16向けに修正 • 入力: fp16 に cast • Initializer、Batch normalization: dtype に fp16 指定 本当は、fp16 モードをセットするだけで OK なんだけど.. CHAINER_DTYPE=float16 Chainer tips def forward(self, x, t): - h = self.bn1(self.conv1(x)) + h = self.bn1(self.conv1(F.cast(x, np.float16))) h = F.max_pooling_2d(F.relu(h), 3, stride=2) self.conv1 = L.Convolution2D(3, 64, 7, 2, 3, - initialW=initializers.HeNormal()) - self.bn1 = L.BatchNormalization(64) + initialW=initializers.HeNormal(dtype=np.float16)) + self.bn1 = L.BatchNormalization(64, dtype=np.float16)
  • 12.
    12 性能比較 良い点: • BS: 128以上で、fp32 より速い • BS: 256で学習可能 (fp32 だと メモリ不足) 悪い点: • BS: 64 以下だと、fp32 より遅い FP32 vs. FP16 0 100 200 300 400 500 600 700 800 900 bs:32 bs:64 bs:128 bs:256 Images/sec fp32 fp16 Better
  • 13.
    13 TRAINING ITERATION CPU の仕事 •IO: 入力画像の前処理 (Decode, Augmentation) • Forward/Backward/Update: 計算グラフ管理, メモリ管理, GPU カーネル投入 IO Forward Backward Update GPU の仕事 • Forward/Backward/Update: CPU から投入されたカーネルを実行
  • 14.
    14 性能比較 良い点: • BS: 128以上で、fp32より速い • BS: 256で学習可能 (fp32 だと メモリ不足) 悪い点: • BS: 64以下だと、fp32 より遅い FP32 vs. FP16 0 100 200 300 400 500 600 700 800 900 bs:32 bs:64 bs:128 bs:256 Images/sec fp32 fp16 Better
  • 15.
    15 NVVP で挙動を確認 (FP32,BS: 64) 高い GPU 稼働率 FWD BWD UPDIO CPU FWD BWD GPU 拡大
  • 16.
    16 NVVP で挙動を確認 (FP32,BS: 64) 拡大図 高い GPU 稼働率 FWD BWD
  • 17.
    17 NVVP で挙動を確認 (FP16,BS: 64) GPU稼働率が低い FWD BWDIO CPU FWD BWDGPU Tensor コア使用で GPU 処理は速くなった。しかし、CPU の処理が間に合わない
  • 18.
    18 NVVP で挙動を確認 (FP16,BS: 64) 拡大図 GPU 稼働率が低い FWD BWD
  • 19.
    19 対策: CPU の負荷を減らす IO処理を高速化、別スレッドで実行 • NVIDIA DALI GPU 操作回数、GPU カーネル数を削減 • Batched weight update、 Fused Bnorm/ReLu、 NHWC format 計算グラフ管理負荷を削減 • Chainer static_graph
  • 20.
    20 NVIDIA DALI https://github.com/NVIDIA/DALI (オープンソース) 前処理は別スレッドで実行 一部前処理はGPU 利用して加速 Chainer、DALI 対応済 (experimental) • 詳しくは、examples/imagenet を参照
  • 21.
    21 NVIDIA DALIの効果 良い点: • BS:64以上で、fp32 より速い • BS: 128 はBS: 256 とほぼ同性能 悪い点: • BS: 32以下だと、fp32 より遅い 0 100 200 300 400 500 600 700 800 900 bs:32 bs:64 bs:128 bs:256 Images/sec fp32 fp16 fp16 (+dali) Better
  • 22.
    22 NVIDIA DALI の効果確認(FP16, BS: 64) IO 処理は、他とオーバーラップ FWD BWD FWD BWD IO by DALI (next) CPU GPU 依然として、CPU の処理が間に合っていない
  • 23.
    23 対策: CPU の負荷を減らす IO処理を高速化、別スレッドで実行 • NVIDIA DALI GPU 操作回数、GPU カーネル数を削減 • Batched weight update、 Fused Bnorm/ReLu、 NHWC format 計算グラフ管理負荷を削減 • Chainer static_graph
  • 24.
    24 BATCHED WEIGHT UPDATE Chaineroptimizer は、レイヤー毎(より正確にはパラメータ毎)に、重みを更新 • 多数の GPU カーネル起動 → CPU 負荷大 Single GPU カーネルで、全ての重みを更新 (batched_update) Chainer #5841 PR中 update update CPU GPU
  • 25.
    25 FUSED BNORM/RELU • cuDNN7.4 から導入 • Fp16 and NHWC フォーマット (後述) のときに利用可能 • レイヤーの自動融合は大変。 bnorm_add_relu モジュールを試作。 モデル記述を修正。 GPU カーネル数の削減、メモリアクセス量を削減 試作
  • 26.
    26 NHWC FORMAT • NCHWだと、Convolution 前後 で、合計 3 回の Tensor Transpose が必要 • NHWC なら、Transpose 不要 → GPU カーネル数削減 • Chainer は NCHW only • NHWC 専用の Convolution/Bnorm モジュール を試作 cuDNN Convolution for TensorCore: NHWC format で高い性能を発揮 NCHW NHWC 試作
  • 27.
    27 BATCHED+FUSED+NHWC の効果 良い点: • 常にfp32 より速い • BS: 256 で、1200 imgs/sec 悪い点 (相対的に): • BS: 32は、BS: 256 の 1/3 の 性能0 200 400 600 800 1000 1200 1400 bs:32 bs:64 bs:128 bs:256 Images/sec fp32 fp16 (+dali) fp16 (+nhwc, fused, batched) Better
  • 28.
    28 効果確認 (FP16, BS:64) NHWC+FUSED+BATCHED FWD BWD FWD BWD IO by DALI (next) CPU GPU CPU 負荷を減らしたが、GPU 時間も NHWC+FUSED で短縮 → まだ GPU に空きがある
  • 29.
    29 対策: CPU の負荷を減らす IO処理を高速化、別スレッドで実行 • NVIDIA DALI GPU 操作回数、GPU カーネル数を削減 • Batched weight update、 Fused Bnorm/ReLu、 NHWC format 計算グラフ管理負荷を削減 • Chainer static_graph
  • 30.
  • 31.
    31 STATIC_GRAPHの効果 良い点: • BS: 32/64の性能改善 悪い点: • BS: 128 以上で実行不可 (メモリ使用量が増える?) 0 200 400 600 800 1000 1200 1400 bs:32 bs:64 bs:128 bs:256 Images/sec fp32 fp16 (+nhwc, fused, batched) fp16 (+static_graph) Better
  • 32.
    32 効果確認 (FP16, BS:64) Static_graph FWD BWD FWD BWD IO by DALI (next) CPU GPU やっと、「CPU 処理時間 < GPU 処理時間」、になった
  • 33.
    33 ここまでのまとめ fp32 の3倍の性能を達成 (最大で) •DALI • Batched weight update • Fused Bnorm/ReLu • NHWC format • Static_graph Backend が python でも、ここまで 速くできる Tensor コア/fp16 を用いた ResNet-50 トレーニング 0 200 400 600 800 1000 1200 1400 bs:32 bs:64 bs:128 bs:256 Images/sec fp32 fp16 (base) fp16 (best) Better ChainerXで 速くなる?
  • 34.
    34 何故 “BS: 32”を速くしたいのか Goal: ImageNet+ResNet50 with 4096 GPUs Processor DL framework Time Microsoft Tesla P100 x8 Caffe 29 hours Facebook Tesla P100 x256 Caffe2 1 hour Google TPUv2 x256 TensorFlow 30 mins PFN Tesla P100 x1024 Chainer 15 mins Tencent Tesla P40 x2048 TensorFlow 6.6 mins SONY Tesla V100 x2176 NNL 3.7 mins Google TPUv3 x1024 TensorFlow 2.2 mins
  • 35.
    35 LARGE MINI-BATCH PROBLEM LocalBS: 32 でも、4096 GPU だと、 Global BS: 128K ImageNet + ResNet50だと、Global BS: 32~64K でも、validation error < 25% を出せるようになってき たが.. SGD の限界? ImageNet, ResNet50, Local BS:32 (*) P. Goyal, et.al., Facebook, “Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour” Better
  • 36.
    36 SGD 以外の選択肢 Backprop で得られる「勾配ベクトル」の向きは、 それほど正しくない KFAC:フィッシャー行列を近似計算して勾配補正 • 学習の「向き」が SGD より正しくなる (反復数減) • 1 反復あたりの計算量が増える Global BS: 128K でも、75% の validation accuracy を実証 • BS 16K 以下なら、30~35 epoch で収束 (SGD は90 epoch 必要) K. Osawa, et.al., “Second-order Optimization Method for Large Mini-batch: Training ResNet-50 on ImageNet in 35 Epochs” (*) 東工大 横田研・松岡研との共同研究 ImageNet + ResNet50 Better Distributed KFAC optimizer on Chainer
  • 37.