DeepSpeedを用いたHuggingface Transformersの複数ノードでの学習

こんにちは。Chief Research Oficerの西鳥羽です。今回はDeepSpeedを用いてHuggingface Transformersの複数ノードでの学習をする方法を紹介します。

Huggingface Transformersは事前学習済みモデルを簡単に扱うことができるフレームワークです。BERTなどの言語モデルをはじめとして最近はWhisperなどの音声モデル、DETRなどの画像モデルも扱えるようになっています。Huggingface Transformersでは数多くの事前学習済みモデルを用意しているため事前学習を行わなくても用いることは可能ですが、多くのモデルで事前学習にも対応しています。

Huggingface Transformerでは複数GPUが搭載されている単一のサーバーでの学習に対応していて、そちらは特に設定の変更などは無く学習の実行ができます。複数のGPUが搭載された複数のサーバーを用いた学習は、このブログでも紹介したことがある(紹介1 紹介2)DeepSpeedというライブラリを用いると行うことができます。

DeepSpeedでの並列化のしくみ

DeepSpeedでの複数サーバーでの学習を行う際、それぞれのサーバーでのプロセスの起動、終了を含めた管理はDeepSpeedが行ってくれます。従ってユーザーから行う操作は transformers/examples/pytorch/language-modeling/run_mlm.py などの学習プログラムを実行するノードからのみ行います。 学習プログラムを実行すると、DeepSpeedはまず各ノード各GPUを管理するプロセスを立ち上げるために全てのノードに搭載されているGPUの枚数分のSSH接続を行います。

DeepSpeedで複数サーバでの学習を行う際の初期化。学習プログラムを実行している親ノード自身も含めて全てのサーバー、GPUごとにsshログインを行ってプロセスを立ち上げます。全てのSSHの通信を掲載すると図が煩雑で分かりにくくなってしまうため、一部のSSHでの通信のみを記載しています。

それぞれのプロセスが生成された後、すべてのプロセスで学習データの読み込み、モデルの読み込みが行われます。そのあとにモデルの学習が行われます。モデルの学習はパラメータや勾配の共有などを行いながら全プロセス協調して行われます。始めのプロセスの立ち上げの通信がSSHで行われていたため、パラメータの共有などもSSHが使われて遅いのでは? と思われるかもしれませんが、学習時のモデルのパラメータや勾配の共有の通信はNCCLを通じた高速な通信を用いて行われます。そのため、Infinibandを用いるなどの高速な通信の性能を活かすことができます。

また、注意点としては学習データの読み込み時にディスクアクセスがボトルネックになって時間がかかってしまうことがあります。これは学習データの読み込み・前処理・キャッシュのためにHuggingface Datasetsを用いることが多いかと思われますが、Huggingface Datasetsの仕様上、学習を始める前にデータの大量の読み込み処理が数回発生します。この読み込み処理が全プロセスでほぼ同時に行われてしまうため、特に共有ディスクにそれらのファイルを配置してアクセスできるようにしている場合にディスクアクセスがボトルネックになってしまいます。このような事態が発生してしまう場合には学習データおよびキャッシュファイルをそれぞれのサーバーのローカルのSSDにコピーしておいたりBeeONDなどのSSDの性能を活かすことができる分散ファイルシステム上に移動しておくなどして同時アクセスへの対応を行っておくことによって学習実行までの時間を短縮することができます。

事前準備

Huggingface TransformersおよびPytorchのインストール手順を説明します。単一のサーバーで学習する時のインストール方法と変わらないので既にHuggingface Transformersを用いて学習を行う環境を構築している方はスキップしてもらって構いません。インストールの詳しい方法、或いは最新バージョンに対応した方法は Installation を参照してください。また、CUDAおよびNCCLをインストールしておく必要がありますが、ここでは割愛します。1/30現在ではPytorchおよびDeepSpeedがCUDA11系しかサポートしていないのでCUDA11系を事前にインストールしてください。

Pytorchのインストール

DeepSpeedはPytorchのみに対応しています。以下のコマンドを実行してPytorchをインストールしてください*1

pip install torch

Huggingface Transformersのインストール

Pytorchのインストールが完了した後はHuggingface Transformersのインストールを行います。複数サーバーでの学習はリリースバージョンでも行うことは可能ですが、exampleの学習スクリプトを用いるためEditable Installを行います。

git clone https://github.com/huggingface/transformers.git
cd transformers
pip install -e .

複数サーバーでの学習のためのセットアップ

DeepSpeedを用いて複数サーバーで学習を行う際、単一のサーバーで学習するときに加えていくつかの設定が必要になります。

DeepSpeedのインストール

Huggingface Transformersをインストールした後にDeepSpeedのインストールを行います。 pip install deepspeed などのコマンドでインストールを行ってください*2。DeepSpeedは機能が豊富で全てをインストールしようとすると追加で依存ライブラリのインストールが必要になったりもするので、細かくインストールする機能を制御したい場合はDeepSpeedのInstallation Detailsを参照してください。

SSHの設定

DeepSpeedを用いて複数ノードでの学習を行う際、パスワードやパスフレーズの入力無しでSSHログインができる必要があります。そのため、ユーザー名やSSH接続を受け付けるポート番号、認証に用いる鍵の設定などがデフォルトではない場合 .ssh/config を修正して学習で用いる予定の全てのサーバーにパスワード、パスフレーズ無しのログインができるように設定します。

環境変数

複数サーバーで学習する際、学習で用いる各サーバーにSSHログインをするため、学習プログラム実行時に設定している環境変数は引き継がれません*3。 そのため、HF_HOME で学習データやモデルのキャッシュが置かれる場所を変更する場合など、SSHログインした後に環境変数が設定されるように Bourne ShellBashで読み込まれる .profile に記述しておく必要があります*4。以下はモデルや学習データのキャッシュの保存場所を /tmp/cache に変更する設定の例です。.profile に記述します。

export HF_HOME=/tmp/cache

この他、PYTHONPATHNCCL_IB_DISABLE などTransformers以外にも依存ライブラリが用いる設定のために指定している環境変数があれば同様に .profile に記述します。

学習に用いるサーバーおよびGPUの設定

学習プログラムを実行する時には他のサーバーへのログインやプロセスの生成などの処理はDeepSpeedが行ってくれるため、直接コマンドを実行するサーバー以外での操作必要は無いのですが、どのサーバーを使うかの情報を記述する必要があります。サーバーアドレス名、GPU数の順番に記述します。例えばworker-1, worker-2 というアドレスの2台のサーバーでそれぞれ8枚のGPUを用いて学習する時、以下のように記述します。

worker-1 slots=8
worker-2 slots=8

学習の実行

学習の実行時には deepspeed コマンドを経由して実行します。以下の例はrun_mlm.pyを用いて事前学習を行う時の例です。事前準備の学習に用いるサーバーおよびGPUの設定を記述したファイルを hostfile.txt という名前で保存しているとします。

また、DeepSpeedには学習の省メモリ化、高速化を行う機能もあり*5、その設定を ds.conf に記述し、--deepspeed ds.conf というオプションで指定しています。こちらに関しては単一ノードでの設定と同様なので割愛します。この設定に関しては過去のブログ記事DeepSpeed Integration を参照してください。それ以外の学習のパラメータやハイパーパラメータは単一でのサーバーでの学習の時と同様にコマンドライン引数で与えることができます。

deepspeed --hostfile=hostfile.txt \
          run_mlm.py \
          --deepspeed ds.conf \
          --model_name_or_path /path_to_model_dir \
          --dataset_name /path/to/datasets  \
          --max_seq_length 512 \
          --preprocessing_num_workers 128 \
          --do_train \
          --per_device_train_batch_size 16 \
          --per_device_eval_batch_size 128 \
          --learning_rate 2e-4 \
          --weight_decay 0.01 \
          --adam_beta1 0.9 \
          --adam_beta2 0.999 \
          --adam_epsilon 1e-6 \
          --warmup_steps 10000 \
          --save_steps 100 \
          --logging_steps 100 \
          --save_total_limit 2 \
          --dataloader_num_workers 2 \
          --output_dir /path/to/output_dir \
          --max_steps 50000

まとめ

今回はDeepSpeedを用いてHuggingface Transformersの複数ノードでの学習を行う方法を紹介しました。以前の記事で紹介したZeROなどの省メモリ化や16bit浮動小数点を用いた高速化などの機能と組み合わせて用いることも可能なので、Huggingface Transformersのみの時よりも大きなパラメータサイズのモデルの学習を行うことが可能になります。

Transformerをベースとした言語モデルはモデルサイズが大きい方が性能が良くなる傾向にあるので、大きなモデルを学習する方法が提供されているのはありがたいものです。

*1:また、Pytorchが標準でサポートしているバージョンではないCUDAをインストールしている場合にはこちらの Install Pytorchを参照してください。

*2:環境変数無しでインストールした場合はJITコンパイルされます。この方法だと実行開始時にコンパイルを行うため試行錯誤しにくく、筆者は DS_BUILD_OPS=1 pip install deepspeed と指定してインストール時にコンパイルするようにしています。こうすることで実行を開始するまでの時間が短縮されます。

*3:筆者は確認してませんが、 ~/.deepspeed_env というファイルに記述することで環境変数が設定されるかもしれません。https://www.deepspeed.ai/getting-started/#multi-node-environment-variables

*4:ここでは .profile としましたが、必ずしも .profileである必要はありません。シェルでログインしたときに読み込まれるファイルは幾つかあり、そのいずれかのファイルでも構いません。

*5:むしろ学習の高速化、省メモリ化の方がメインの機能です。Training Overview and Features - DeepSpeed