[[343402]] 1. トレーニングのボトルネックはどこですか? - GPU 使用率が低い: モデルのトレーニング中は GPU メモリが完全に使用されますが、GPU 使用率は不安定で、0% になることもあれば、90% になることもあります。
- トレーニング データの量が膨大: トレーニング データの量は数百万または数千万と膨大です。1 エポックのトレーニングに長い時間がかかり、モデルの反復サイクルが長すぎます。
2. GPU 使用率の向上: CPU と GPU GPU 使用率が低いです。主な原因は、CPU の処理効率が GPU に追いつけないことです。 2.1 CPUとGPUの通信 - CPU は、データのロードとデータの前処理、およびメモリとビデオ メモリ間での継続的なデータ交換を担当します。
- GPU はモデルのトレーニングを担当します (インターネットからの画像)
2.2 解決策 マルチプロセス並列処理を使用してCPUのデータ読み込みパフォーマンスを高速化します - keras keras は、データを並列処理し、GPU モデル トレーニング用にキューにプッシュするマルチプロセス アプローチを採用するワーカー use_multiprocessing を提供します。プロセスは互いのリソースに影響を与える可能性があるため、大きいほど良いです。ワーカーは 2、4、または 8 に設定できます。
- 実行モデル.フィットジェネレータ(
- ジェネレータ= training_generator 、
- class_weight ={0: config.weights, 1: 1},
- エポックエポック=エポック、
- 詳細= 1 、
- ステップ数/エポックステップ数/エポック=ステップ数/エポック、
- コールバック= callbacks_list 、
- 検証データ=有効なジェネレータ、
- 検証手順検証手順= 検証手順、
- シャッフル= True 、
- 労働者= 8 、
- use_multiprocessing = True 、
- 最大キューサイズ= 20
- Pytorch torch は、データのロード時に同様のパラメータ num_workers を提供します。 pin_memory=True メモリを必要とせずにビデオメモリに直接ロードできます
- torch.utils.data.DataLoader(image_datasets[x],
- バッチサイズバッチサイズ=バッチサイズ、
- シャッフル= True 、
- 労働者数= 8 、
- pin_memory = True )
3. 分散並列トレーニング 3.1 パラレルモード トレーニングデータの量が多い場合は、複数のマシンと複数の GPU を使用することでトレーニング効率を向上させることができます。 Hadoop や Spark などの分散データ処理フレームワークとは異なり、ディープラーニングのトレーニングにはパラメータの順方向伝播と逆方向伝播が含まれるため、次の 2 つの並列方法があります。 - モデルの並列処理: 分散システム内の異なるマシン (GPU/CPU など) は、ネットワーク モデルの異なる部分を担当します。通常、ニューラル ネットワーク モデルの異なるネットワーク レイヤーは異なるマシンに割り当てられるか、同じレイヤー内の異なるパラメーターは異なるマシンに割り当てられます。通常、これらは NLP モデルなどのグラフィック カードに収まらない非常に大きなモデルです。モデル並列処理の欠点は、レイヤー間に依存関係が存在する可能性があり、完全に並列化できないことです。 (インターネットからの写真)
- データ並列処理: 異なるマシンに同じモデルの複数のコピーがあり、各マシンに異なるデータが割り当てられ、その後、すべてのマシンの計算結果が何らかの方法でマージされます。これはビッグデータの状況に適しています。データ並列処理が解決する必要がある問題は、データの分割と転送、およびパラメータの更新です。
3.2 データの並列処理 Facebook は、「正確で大規模なミニバッチ SGD: 1 時間で ImageNet をトレーニング」で、ResNet-50 ネットワークの「データ並列」トレーニングに 256 個の GPU を使用する方法を紹介しました。 - データのセグメンテーション: 大きなバッチサイズを使用し、ワーカーの数に応じてセグメント化し、異なるワーカーに分散して実行します。
- パラメータ更新:パラメータ更新には2つのモードがあります:(1)パラメータサーバー(2)リング更新(サーバーレスモード)
3.2.1 パラメータサーバーモード パラメータ サーバー モードについては、下の図を参照してください。各ワーカーがトレーニングのバッチを完了した後、パラメータをバックプロパゲーションするときに、すべてのワーカーはパラメータをパラメータ サーバーに渡して集約および平均化し、その後、各ワーカーに渡して 2 番目のトレーニング バッチを入力します。 (インターネットからの写真) パラメータ サーバーには 1 つ以上の構造モードがあります。このデータ並列モードの効率が向上するかどうかは、パラメータ サーバーとワーカー間の通信効率、つまり、最も遅いワーカーのトレーニング時間と、パラメータ サーバーがパラメータを受信して更新し、それを送り返すのにかかる時間によって決まることがわかります。ワーカーの数が多い場合、パラメータ サーバーがボトルネックになる可能性があります。 (インターネットからの写真) 3.2.2 リング削減 Baidu が提案したリングリデュースは、パラメータサーバーを廃止し、パラメータを更新するためにリング構造を採用しています。リング削減は、すべてのワーカーをリング構造に編成し、2 つのワーカーを互いに隣接させます。各ワーカーは隣接するワーカーとのみパラメータを交換します。数回のやり取りの後、すべてのワーカーに他のワーカーのパラメータ情報が含まれ、更新の目的が達成されます。 (インターネットからの写真) 次の図はいくつかのステップを示しています。プロセスを高速化するために、ring-reduce はすべてのパラメータを一度に交換しません。代わりに、最初にパラメータを分割し、分割されたパラメータを連続的に交換します。 4. 実装フレームワーク: Horovod Horovod は、Uber がオープンソース化したもう 1 つのディープラーニング ツールです。その開発は、Facebook の「1 時間の ImageNet トレーニング ペーパー」と Baidu の Ring Allreduce の利点を活用しており、ユーザーが分散トレーニングを実装するのに役立ちます。 https://github.com/horovod/horovod NCCL を使用して、Baidu の ring-allreduce 実装を置き換えます。 NCCL は、ring-allreduce の高度に最適化されたバージョンを提供する NVIDIA の集合通信ライブラリです。 NCCL 2 では、複数のマシン間で ring-allreduc を実行できます。 スタンドアロンのトレーニング コードを分散コードに変更する場合、分散トレーニングを変換するために必要な手順はわずかです。 - Horovod のインストール 環境をインストールする手間を省くために、docker の horovod をインストールすることをお勧めします。 HorovodはNCCL 2オープンMPIに依存している
- $ mkdir horovod-docker-gpu
- $ wget -O horovod-docker-gpu/Dockerfile https://raw.githubusercontent.com/horovod/horovod/master/Dockerfile.gpu
- $ docker build -t horovod:最新のhorovod-docker-gpu
- マシンワーカーマシン間の SSH 接続
- トレーニング コードを変更します。horovod は、tf、keras、pytorch、mxnet などのさまざまなディープラーニング フレームワークをサポートします。 kerasを例にとると、主な6つのステップを変更します(1)初期化:hvd.init()(2)GPUコンピューティングリソースの割り当て:config.gpu_options.visible_device_list = str(hvd.local_rank())(3)パラメータの分散更新を実現するための分散オプティマイザー:opt = hvd.DistributedOptimizer(opt)(4)すべてのワーカーモデルの初期化の一貫性を定義しますhvd.callbacks.BroadcastGlobalVariablesCallback(0)(5)モデルは特定のワーカーに保存されます
- __future__ から print_function をインポートする
- kerasをインポートする
- keras.datasetsからmnistをインポートする
- keras.modelsからSequentialをインポートする
- keras.layers から Dense、Dropout、Flatten をインポートします
- keras.layers から Conv2D、MaxPooling2D をインポートします
- kerasからバックエンドをKとしてインポートします
- インポート数学
- テンソルフローをtfとしてインポートする
- horovod.keras を hvd としてインポートします
- # Horovod: Horovod を初期化します。
- hvd.init()
- # Horovod: ローカルランクの処理に使用する GPU をピン留めする (プロセスごとに 1 つの GPU)
- config = tf.ConfigProto ()です。
- config.gpu_options.allow_growth = True
- config.gpu_options.visible_device_list = str (hvd.local_rank())
- K.set_session(tf.Session( config config =config))
- バッチサイズ= 128
- num_classes = 10
- # Horovod: GPU の数に基づいてエポックの数を調整します。
- エポック= int (math.ceil(12.0 / hvd.size()))
- # 入力画像のサイズ
- 画像行数、画像列数= 28、28
- # データはシャッフルされ、トレーニングセットとテストセットに分割されます
- (x_train, y_train)、(x_test, y_test) = mnist.load_data()
- K.image_data_format() == 'channels_first'の場合:
- x_train x_train = x_train.reshape(x_train.shape[0], 1, img_rows, img_cols)
- x_test x_test = x_test.reshape(x_test.shape[0], 1, img_rows, img_cols)
- 入力形状= (1, 画像行数, 画像列数)
- それ以外:
- x_train x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1)
- x_test x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 1)
- 入力シェイプ= (画像行、画像列、1)
- x_train x_train = x_train.astype('float32')
- x_test x_test = x_test.astype('float32')
- x_train /= 255
- x_テスト /= 255
- print('x_train の形状:', x_train.shape)
- print(x_train.shape[0], 'トレーニングサンプル')
- print(x_test.shape[0], 'テストサンプル')
- # クラスベクトルをバイナリクラス行列に変換する
- y_train = keras.utils.to_categorical (y_train、num_classes)
- y_test = keras.utils.to_categorical (y_test、num_classes)
- モデル=シーケンシャル()
- モデルを追加します(Conv2D(32,カーネルサイズ= (3, 3),
- アクティベーション= 'relu' 、
- 入力形状入力形状=入力形状))
- model.add(Conv2D(64, (3, 3),アクティベーション= 'relu' ))
- モデルを追加します(MaxPooling2D(プールサイズ=(2, 2)))
- モデル.add(ドロップアウト(0.25))
- モデルを追加します(フラット化())
- model.add(Dense(128, activation = 'relu' ))
- モデルを追加します(ドロップアウト(0.5))
- model.add(Dense(num_classes, activation = 'softmax' ))
- # Horovod: GPU の数に基づいて学習率を調整します。
- opt = keras.optimizers.Adadelta (1.0 * hvd.size())
- # Horovod: Horovod Distributed Optimizer を追加します。
- opt = hvd.DistributedOptimizer (opt)
- model.compile( loss = keras .losses.categorical_crossentropy,
- optオプティマイザ=opt、
- メトリック= ['精度'])
- コールバック= [
- # Horovod: 初期変数状態をランク 0 から他のすべてのプロセスにブロードキャストします。
- # これは、すべてのワーカーの一貫した初期化を保証するために必要です。
- # トレーニングはランダムな重みで開始されるか、チェックポイントから復元されます。
- hvd.callbacks.BroadcastGlobalVariablesCallback(0)、
- ]
- # Horovod: 他のワーカーによってチェックポイントが破損するのを防ぐため、ワーカー 0 にのみチェックポイントを保存します。
- hvd.rank() == 0 の場合:
- callbacks.append(keras.callbacks.ModelCheckpoint('./checkpoint-{epoch}.h5'))
- モデル.fit(x_train, y_train,
- バッチサイズバッチサイズ=バッチサイズ、
- コールバックコールバック=コールバック、
- エポックエポック=エポック、
- 詳細= 1 、
- 検証データ= (x_test, y_test))
- スコア=モデル.evaluate(x_test, y_test,詳細= 0 )
- print('テスト損失:', スコア[0])
- print('テスト精度:', score[1])
- horovodrun による分散トレーニングの実行
horovodrun -np 16 -H server1:4、server2:4、server3:4、server4:4 python train.py 5. 結論 この記事では、GPU の利用と分散トレーニング Horovod フレームワークを通じてディープラーニング トレーニングを改善する方法について説明します。 - CPUのロードと前処理を並列化することで、GPUがCPUを待つ必要がなくなる
- Horovod は、データの並列処理を可能にして、大量のデータのトレーニングの反復時間を改善するために使用されます。
|