この文書は、2019年10月にHPC AI Innovation LabのRakshith Vasudev氏とJohn Lockman氏によって作成されました。
概要
Horovodは、Tensorflow、Keras、Pytorch、MXNetなど、さまざまなディープ ラーニング フレームワークに関するトレーニングを迅速に行うための分散ディープ ラーニング フレームワークです。TensorFlowを使用して、分散ディープ ラーニング プロジェクトを簡単かつ迅速に作成できるように開発されました。Horovodでは、トレーニング プロセスの並列化がサポートされています。データの並列化とモデルの並列化の両方をサポートします。Horovodを使用するニューラル ネットワーク トレーニング ジョブが実行されている場合、これらの共通のヒントを使用してデバッグを行い、パフォーマンスを向上させることができます。
説明
この記事では、参照例としてCheXNetを使用します。CheXNetは、DenseNetを活用して、指定された胸のレントゲン写真から最大14の病状を特定するAI放射線科医アシスタント モデルです。
- パフォーマンスの問題をデバッグしようとする際に、適切な環境設定により大幅に時間が節約できます。ディープ ラーニング用フレームワークのGPUバージョンが使用されていることを確認してください。この例では、anaconda提供のtensorflow-gpuを使用しています。
-
horovodrunまたはmpirunをバインド パラメーターと一緒に使用すると、パフォーマンスが向上します。理想的には、GPUを制御するプロセスを最も近いCPUソケットにバインドする必要があります。Dell EMC PowerEdge C4140での最適なオプションは--map-byソケットです。バインド オプションを指定する必要はありません。
これは次のようになります。
mpirun --map-by socket -np x python pyfile.py - -pyoptions
- ジョブは、1つのGPUで1つのMPIプロセスが動作するようにセットアップする必要があります。GPUの数よりも多くのプロセスが存在する場合、プロセスはコンピューティング リソースと競合し、優れたパフォーマンスでジョブを実行することはできません。上記の例では、xは使用するGPUの数と同じである必要があります。
-
GPUごとに1つのプロセスを設定するには、tensorflowのConfigProto()を次のように使用します。
config.gpu_options.visible_device_list=str(hvd.local_size())
-
GPUを使用しているプロセスの数やGPUのメモリー消費量を確認するには、「watch nvidia-smi」を使用できます。これにより、電力消費量を確認することもできます。
図1: メモリー、電力、GPUの使用率を表示するコマンド出力のスクリーンショット。
- データ パイプラインが正しく設定されており、mpirunパラメーターが正しい場合は、モデル トレーニングの開始後、GPUの使用率が常に90%を超えている必要があります。まれに10~25%の使用率まで低下することは許容されますが、これは頻繁には発生しない場合です。
- GPUメモリーがほぼ満杯でも、メモリー要件を超過しないように制限されるように、バッチ サイズを設定します。学習率のスケーリングを達成できる方法を考慮することが重要です。学習率のスケーリングは、GPU数の増加に応じて、GPU数に比例して学習率が倍増する必要があるという概念に基づいています。これにより、モデルを効果的に収束することができます。この方法により、モデルの収束を損なうことなく、GPUに対して可能なイメージの最大数を適合させ、i/o操作の数を削減します。学習率のスケーリングが、分散ワークロードの設定でモデルの収束を向上させるためのベスト ソリューションではないケースもあることに注意する必要があります。
学習率のスケーリングが必要かどうかを確認するには、次を行います。
a)分散モードでの学習率のスケーリングの有無にかかわらず、モデルをトレーニングします。
b)習率のスケーリングなしのモデルが、学習率のスケーリングありのモデルよりも優れている場合は、学習率のスケーリングは不要です。
具体的には、収束に関するトレーニングを行う際に、バッチごとに可能なイメージの最大数に適合させるのは、必須ルールではありません。多くの場合、バッチ サイズと収束の間には矛盾があるため(学習率のスケーリングが使用されているかどうかに関係なく)、データ科学者は使用事例に基づいて決定できる必要があります。
また、「watch nvidia-smi」を使用してGPUメモリーの消費量を確認することもできます。 この使用事例では、学習率のスケーリングは使用されていません。これは、AUC値とローカルなミニバッチ サイズが64の場合により優れたモデルが生成されたためです。この文書で説明されるように、学習率のスケーリングを使用している場合は、エポックのウォームアップを使用することが一般的です。
-
horovod timelineとnvprofを使用してジョブをプロファイルして、発生する可能性のあるボトルネックを表示します。 多くの場合、ボトルネックの原因は次のいずれかにあります。
a)tfデータ パイプラインが正しく設定されていないため、アクセラレーターがアイドル状態のときにデータを準備する時間が多くかかる。この問題を解決するには、tfパイプラインを修正する必要があります。
tfデータ パイプラインの設定方法については、この文書を参照してください。
b)通信が正しいファブリックを使用していない可能性がある。InfiniBandを使用していることを確認するには、次のようにmpirunの実行中に-x NCCL_DEBUG=INFOを含めます。
mpirun -np 8 --map-by socket -x NCCL_DEBUG=INFO python $HOME/models/chexnet/chexnet.py --batch_size=128 --epochs=15
または-x bindingを含むhorovodrunを使用します。
- 適切に分散を実装するには、GPUは相互に効果的に通信できる必要があります。効果的に通信していない場合は、通信のボトルネックが発生します。これらが最適に通信しているかどうかを確認するには、次のプロセスを使用します。
まず、前述の8bで説明しているように、-x bindingを使用してGPUの通信を確認します。
GPUが通信している場合:
1)ノード内の最適な通信は、次のようになります。
gpu002:1299562:1299573 [0] NCCL INFO Ring 00 : 0[0] -> 1[1] via P2P/IPC
2)ノード外の最適な通信は、次のようになります。
gpu028:149460:149495 [0] NCCL INFO Ring 01 : 16 -> 0 [send] via NET/IB/0
gpu009:164181:164216 [0] NCCL INFO Ring 01 : 12 -> 8 [receive] via NET/IB/0
gpu009:164181:164216 [0] NCCL INFO Ring 01 : 4 -> 8 [receive] via NET/IB/0
特に、使用されているノードやGPUの数が対応するパフォーマンスに効果的に反映されない場合には、ディープ ラーニング ジョブの配布が困難になることがあります。アクセラレーターへの投資を最大限に活用できるように、次のベスト プラクティスが導入されていることを確認してください。
- 正しいバインド オプションを設定する、
- GPUメモリーを無駄しないように複数プロセスを評価する、
- 最新パイプライン アプローチを使用する、
- プロファイルを作成して、ジョブが実行されている時間のうち少なくとも80%の間GPUが使用されていることを確認する、
- 最新のCUDA関連ライブラリーを使用する