HorovodRunner: distributed deep learning with Horovod | Databricks on AWS [2022/8/17時点]の翻訳です。
本書は抄訳であり内容の正確性を保証するものではありません。正確な内容に関しては原文を参照ください。
HorovodRunnerは、Horovodフレームワークを用いたDatabricks上で分散ディープラーニングワークロードを実行するための汎用APIです。HorovodとSparkのバリアーモードを組み合わせることで、DatabricksのSparkで長時間稼働するディープラーニングトレーニングジョブに対して高い安定性を提供できるようになります。HorovodRunnerはディープラーニングトレーニングのコードとHorovodのフックを含むPythonメソッドを受け取ります。HorovodRunnerはそのメソッドをドライバー上でpickleし、Sparkワーカーに分散させます。HorovodのMPIジョブは、バリアー実行モードを用いてSparkジョブとして埋め込まれます。最初のエグゼキューターがBarrierTaskContext
を用いて全てのタスクエグゼキューターのIPアドレスを収集し、mpirun
を用いてHorovodのジョブを起動します。それぞれのPython MPIプロセスは、pickleされたユーザープログラムをロードし、デシリアライズして実行します。
HorovodRunnerによる分散トレーニング
HorovodRunnerを用いることで、HorovodトレーニングジョブをSparkジョブとして起動することができます。HorovodRunnerのAPIは以下のテーブルのメソッドをサポートしています。詳細は、HorovodRunner API documentationをご覧ください。
メソッドとシグネチャ | 説明 |
---|---|
init(self, np) |
HorovodRunnerのインスタンスを作成します。 |
run(self, main, **kwargs) |
main(**kwargs) を呼び出すことでHorovodトレーニングジョブを実行します。cloudpickleを用いてmain関数とキーワードの引数はシリアライズされ、クラスターのワーカーに分散されます。 |
HorovodRunnerを用いた分散トレーニングプログラムを開発する一般的なアプローチは以下の通りとなります。
- ノードの数を用いて初期化された
HorovodRunner
インスタンスを作成します。 - Horovod usageで説明されている方法に従って、Horovodのトレーニングメソッドを定義し、メソッド内にimport文を追加するようにします。
-
HorovodRunner
インスタンスにトレーニングメソッドを引き渡します。
例は以下のようになります。
hr = HorovodRunner(np=2)
def train():
import tensorflow as tf
hvd.init()
hr.run(train)
n
個のサブプロセスを持つドライバーでのみHorovodRunnerを実行するには、hr = HorovodRunner(np=-n)
を使用します。例えば、ドライバーのーどに4GPUがある場合、最大4
までn
を指定することができます。パラメーターnp
の詳細については、HorovodRunner API documentationをご覧ください。サブプロセスに一つのGPUを固定する方法に関しては、Horovod usage guideをご覧ください。
よくあるエラーは、TensorFlowオブジェクトが見つからない、pickleされないというものです。これは、ライブラリのimport文が他のエグゼキューターに分散されていない場合に生じます。この問題を回避するには、全てのimport文(例えば、import tensorflow as tf
)をHorovodのトレーニングメソッドと、Horovodトレーニングメソッドで呼び出されるその他の全てのユーザー定義関数の両方に含めるようにしてください。
Horovod TimelineによるHorovodトレーニングの記録
Horovodには、Horovod Timelineと呼ばれる自分のアクティビティを記録する機能が備わっています。
重要!
Horovod Timelineはパフォーマンスに多大なる影響を及ぼします。Horovod Timelineを有効化すると、Inception3のスループットが~40%減少することがあります。HorovodRunnerを高速化したい場合には、Horovod Timelineを使わないでください。
トレーニングが進行中の状態でHorovod Timelineを参照することはできません。
Horovod Timelineを記録するには、環境変数HOROVOD_TIMELINE
をタイムラインファイルを保存したい場所に設定します。タイムラインファイルを容易に取得できるように共有ストレージ上の場所を使用することをお勧めします。例えば、以下のようにDBFSローカルファイルAPIを使うことができます。
timeline_dir = "/dbfs/ml/horovod-timeline/%s" % uuid.uuid4()
os.makedirs(timeline_dir)
os.environ['HOROVOD_TIMELINE'] = timeline_dir + "/horovod_timeline.json"
hr = HorovodRunner(np=4)
hr.run(run_training_horovod, params=params)
そして、トレーニング関数の最初と最後にタイムライン固有のコードを追加します。以下のノートブックには、トレーニングの進捗を参照するワークアラウンドとして使用できるサンプルコードが含まれています。
Horovod Timelineのサンプルノートブック
タイムラインファイルをダウンロードするには、Databricks CLIかFileStoreを使用し、参照するには以下のようにChromeブラウザのchrome://tracing
機能を使用します。
開発のワークフロー
シングルノードのディープラーニングのコードを分散トレーニングに移行するための一般的なステップが存在します。このセクションのサンプルでは、これらのステップを説明しています。
-
シングルノードのコードを準備する: TensorFlow、Keras、PyTorchを用いたシングルノードのコードを準備し、テストします。
-
Horovodに移行する: Horovodを用いたコードに移行し、ドライバー上でテストするためにHorovod usageの手順に従います。
- Horovodを初期化するために
hvd.init()
を追加します。 -
config.gpu_options.visible_device_list
を用いてこのプロセスで使用されるサーバーGPUをピン留めします。プロセスごとに1つのGPUを用いる典型的なセットアップを行うと、これはローカルのランクに設定されます。この場合、サーバーの最初のプロセスには最初のGPUが割り当てられ、2番目のプロセスには2番目のGPUが割り当てられ、以降も同様となります。 - データセットのシャードを含めます。データセットオペレーターを用いることで、それぞれのワーカーがユニークなサブセットを読み込めるようになるので、分散トレーニングの実行においては非常に有用です。
- ワーカーの数で学習率をスケールさせます。ワーカーの数で、同期分散トレーニングの効率的なバッチサイズをスケールさせることができます。学習率の増加は、バッチサイズの増加を埋め合わせることになります。
-
hvd.DistributedOptimizer
でオプティマイザーをラッピングします。分散オプティマイザーは、オリジナルのオプティマイザーに徐々に計算処理を移譲し、allreduceやallgatherを用いて勾配を平均し、平均された勾配を適用します。 - ランク0から他のプロセスに初期変数の状態をブロードキャストするために、
hvd.BroadcastGlobalVariablesHook(0)
を追加します。トレーニングがランダムな重みからスタートしたり、チェックポイントから復旧した際に全てのワーカーで一貫性のある初期化が行われるようにするためにはこれが必要となります。あるいは、MonitoredTrainingSession
を使用している場合には、グローバル変数が初期化された後にhvd.broadcast_global_variables
オペレーションを実行することができます。 - 他のワーカーの挙動を破壊しないようにするために、ワーカー0でのみチェックポイントを保存するようにコードを変更します。
- Horovodを初期化するために
-
HorovodRunnerに移行する: HorovodRunnerはPython関数を呼び出すことでHorovodトレーニングジョブを実行します。メインのトレーニング処理を単一のPython関数でラップする必要があります。そして、ローカルモードでHorovodRunnerをテストしてから、分散モードでテストすることができます。
ディープラーニングライブラリのアップデート
注意
本書にはDatabricksでは使用しないslaveという単語への参照が含まれています。ソフトウェアからこの単語が削除された場合には、本書からも削除します。
TensorFlow、Keras、PyTorchをアップグレード、ダウングレードする際は、新たにインストールしたライブラリを用いてコンパイルしたHorovodを再インストールす必要があります。例えば、Tensorflowをアップグレードしたい場合、TensorFlow installation instructionsにあるinitスクリプトを使用し、最後に以下のTensorFlow固有のHorovodインストールコードを追加することをお勧めします。PyTorchなどのライブラリのアップグレード、ダウングレードのような異なる組み合わせに関しては、Horovod installation instructionsをご覧ください。
add-apt-repository -y ppa:ubuntu-toolchain-r/test
apt update
# Using the same compiler that TensorFlow was built to compile Horovod
apt install g++-7 -y
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 60 --slave /usr/bin/g++ g++ /usr/bin/g++-7
HOROVOD_GPU_ALLREDUCE=NCCL HOROVOD_CUDA_HOME=/usr/local/cuda pip install horovod==0.18.1 --force-reinstall --no-deps --no-cache-dir
サンプル
MNISTデータセットを用いた以下のサンプルでは、シングルノードのディープラーニングプログラムを、HorovodRunnerを用いてどのように分散ディープラーニングに移行するのかをデモしています。
- Distributed deep learning training using TensorFlow with HorovodRunner for MNIST
- Single node PyTorch to distributed deep learning
制限
- ワークスペースでReposのファイルサポートが有効化されており、
np
が1より大きい値に設定され、ノートブックがRepoにあるファイルからimportしている場合、HorovodRunnerは動作しません。HorovodRunner
ではなくhorovod.sparkを使用することを検討してください。