Intelのテクノロジーを使用して、PyTorchの分散ファインチューニングを高速化する
'Intelテクノロジーを使い、PyTorchの分散ファインチューニングを高速化する'
驚異的なパフォーマンスを持つ最先端のディープラーニングモデルでも、トレーニングには長い時間がかかることがよくあります。トレーニングジョブを高速化するために、エンジニアリングチームは分散トレーニングに頼っています。これは、クラスタ化されたサーバーがそれぞれモデルのコピーを保持し、トレーニングセットのサブセットでトレーニングを行い、結果を交換して最終的なモデルに収束するという分割統治技術です。
グラフィックプロセッシングユニット(GPU)は、ディープラーニングモデルのトレーニングにおいて長い間デファクトの選択肢でした。しかし、転移学習の台頭により、状況が変化しています。モデルは今や巨大なデータセットからゼロからトレーニングされることはほとんどありません。代わりに、特定の(より小さい)データセットで頻繁に微調整され、特定のタスクに対してベースモデルよりも精度の高い専用モデルが構築されます。これらのトレーニングジョブは短いため、CPUベースのクラスタを使用することは、トレーニング時間とコストの両方を管理するための興味深いオプションとなります。
この投稿の内容
この投稿では、インテル Xeon Scalable CPUサーバのクラスタ上でPyTorchのトレーニングジョブを分散して高速化する方法について説明します。Ice Lakeアーキテクチャを搭載し、パフォーマンス最適化されたソフトウェアライブラリを実行する仮想マシンを使用して、クラスタをゼロから構築します。クラウドまたはオンプレミスの環境で、簡単にデモを自身のインフラストラクチャに複製することができるはずです。
テキスト分類ジョブを実行し、MRPCデータセット(GLUEベンチマークに含まれるタスクの1つ)でBERTモデルを微調整します。MRPCデータセットには、ニュースソースから抽出された5,800の文のペアが含まれており、各ペアの2つの文が意味的に同等であるかどうかを示すラベルが付いています。このデータセットはトレーニング時間が合理的であり、他のGLUEタスクを試すのはパラメーターさえ変更すれば可能です。
- BLOOMトレーニングの技術背後
- AIチップに対する潜在的な米国の輸出制限がテック市場を揺るがす
- Inflection AIは、テックの巨人や業界の巨頭によって主導された13億ドルの資金調達を確保しました
クラスタが準備できたら、まずは単一のサーバーでベースラインのジョブを実行します。その後、2つのサーバーや4つのサーバーにスケールアップして、スピードアップを計測します。
途中で以下のトピックについて説明します:
- 必要なインフラストラクチャとソフトウェアのビルディングブロックのリストアップ
- クラスタのセットアップ
- 依存関係のインストール
- 単一ノードのジョブの実行
- 分散ジョブの実行
さあ、作業を始めましょう!
インテルサーバの使用
最高のパフォーマンスを得るために、Ice Lakeアーキテクチャに基づいたインテルサーバを使用します。これには、Intel AVX-512やIntel Vector Neural Network Instructions(VNNI)などのハードウェア機能がサポートされています。これらの機能は、ディープラーニングのトレーニングや推論で一般的に見られる操作を高速化します。これらについては、このプレゼンテーション(PDF)で詳しく学ぶことができます。
主要なクラウドプロバイダーは、インテル Ice Lake CPUを搭載した仮想マシンを提供しています:
- Amazon Web Services:Amazon EC2 M6iおよびC6iインスタンス
- Azure:Dv5/Dsv5シリーズ、Ddv5/Ddsv5シリーズ、Edv5/Edsv5シリーズの仮想マシン
- Google Cloud Platform:N2 Compute Engine仮想マシン
もちろん、独自のサーバーも使用することができます。Cascade Lakeアーキテクチャ(Ice Lakeの前身)をベースにしている場合は、Cascade LakeもAVX-512とVNNIをサポートしているため、問題ありません。
インテルパフォーマンスライブラリの使用
PyTorchでAVX-512とVNNIを活用するために、インテルはPyTorchのための拡張機能を設計しています。このソフトウェアライブラリはトレーニングと推論の高速化を提供するため、必ずインストールする必要があります。
分散トレーニングにおいて、主なパフォーマンスのボトルネックは通常ネットワーキングです。実際、クラスタ内の異なるノードは、モデルの状態情報を定期的に交換して同期する必要があります。トランスフォーマーは数十億のパラメータを持つ大規模なモデルであり(場合によってはそれ以上)、情報のボリュームは膨大であり、ノード数が増えるにつれてますます悪化します。したがって、ディープラーニングに最適化された通信ライブラリを使用することが重要です。
実際、PyTorchにはtorch.distributed
パッケージが含まれており、さまざまな通信バックエンドをサポートしています。ここでは、ディープラーニングで使用される通信パターンの効率的な実装であるIntel oneAPI Collective Communications Library(oneCCL)を使用します(all-reduceなど)。oneCCLのパフォーマンスについては、このPyTorchブログ記事で詳しく学ぶことができます。
ビルディングブロックについて理解ができたので、トレーニングクラスタの全体的なセットアップについて話しましょう。
クラスタのセットアップ
このデモでは、Amazon EC2インスタンスを使用しています。Amazon Linux 2(c6i.16xlarge、64 vCPU、128GB RAM、25Gbit/sのネットワーキング)で実行しています。セットアップは他の環境では異なる可能性がありますが、手順は非常に似ています。
4つの同一のインスタンスが必要なので、同じセットアップを4回実行するのを避けるため、何らかの自動化を計画する必要があります。ここでは、1つのインスタンスを手動でセットアップし、このインスタンスから新しいAmazon Machine Image(AMI)を作成し、このAMIを使用して3つの同一のインスタンスを起動します。
ネットワーキングの観点からは、次のセットアップが必要です:
- すべてのインスタンスでセットアップとデバッグのためのsshアクセスのためにポート22を開く。
- マスターインスタンス(トレーニングを開始するインスタンス)と他のすべてのインスタンス(マスターも含む)の間でパスワードなしのsshを構成する。
- クラスタ内のoneCCL通信のためにすべてのインスタンスですべてのTCPポートを開く。 これらのポートを外部に開かないように注意してください。AWSは、特定のセキュリティグループを実行しているインスタンスからの接続のみを許可する便利な方法を提供しています。以下は私のセットアップの見本です。
さて、最初のインスタンスを手動でプロビジョニングしましょう。まず、インスタンス自体を作成し、上記のセキュリティグループをアタッチし、128GBのストレージを追加します。コストを最適化するために、スポットインスタンスとして起動しました。
インスタンスが起動したら、依存関係をインストールするためにssh
で接続します。
依存関係のインストール
以下の手順に従います:
- Intelツールキットをインストールする
- Anacondaディストリビューションをインストールする
- 新しい
conda
環境を作成する - PyTorchとPyTorchのIntel拡張機能をインストールする
- oneCCLをコンパイルしてインストールする
transformers
ライブラリをインストールする
多くのように見えますが、複雑なものはありません。さあ始めましょう!
Intelツールキットのインストール
まず、Intel OneAPI base toolkitとAI toolkitをダウンロードしてインストールします。詳細については、Intelのウェブサイトをご覧ください。
wget https://registrationcenter-download.intel.com/akdlm/irc_nas/18236/l_BaseKit_p_2021.4.0.3422_offline.sh
sudo bash l_BaseKit_p_2021.4.0.3422_offline.sh
wget https://registrationcenter-download.intel.com/akdlm/irc_nas/18235/l_AIKit_p_2021.4.0.1460_offline.sh
sudo bash l_AIKit_p_2021.4.0.1460_offline.sh
Anacondaのインストール
次に、Anacondaディストリビューションをダウンロードしてインストールします。
wget https://repo.anaconda.com/archive/Anaconda3-2021.05-Linux-x86_64.sh
sh Anaconda3-2021.05-Linux-x86_64.sh
新しいconda環境の作成
パスを更新するためにログアウトして再ログインします。その後、整理整頓するために新しいconda
環境を作成します。
yes | conda create -n transformer python=3.7.9 -c anaconda
eval "$(conda shell.bash hook)"
conda activate transformer
yes | conda install pip cmake
PyTorchとPyTorchのIntel拡張機能のインストール
次に、PyTorch 1.9とIntel拡張ツールキットをインストールします。 バージョンは一致する必要があります。
yes | conda install pytorch==1.9.0 cpuonly -c pytorch
pip install torch_ipex==1.9.0 -f https://software.intel.com/ipex-whl-stable
oneCCLのコンパイルとインストール
次に、oneCCLをコンパイルするために必要ないくつかのネイティブ依存関係をインストールします。
sudo yum -y update
sudo yum install -y git cmake3 gcc gcc-c++
次に、oneCCLリポジトリをクローンし、ライブラリをビルドしてインストールします。 再度、バージョンが一致している必要があります。
source /opt/intel/oneapi/mkl/latest/env/vars.sh
git clone https://github.com/intel/torch-ccl.git
cd torch-ccl
git checkout ccl_torch1.9
git submodule sync
git submodule update --init --recursive
python setup.py install
cd ..
transformersライブラリのインストール
次に、transformers
ライブラリとGLUEタスクを実行するために必要な依存関係をインストールします。
pip install transformers datasets
yes | conda install scipy scikit-learn
最後に、実行する例を含むtransformers
リポジトリのフォークをクローンします。
git clone https://github.com/kding1/transformers.git
cd transformers
git checkout dist-sigopt
これで完了です!単一ノードのジョブを実行しましょう。
単一ノードのジョブの実行
ベースラインを得るために、transformers/examples/pytorch/text-classification
内のrun_glue.py
スクリプトを実行する単一ノードのジョブを起動しましょう。これはいずれのインスタンスでも動作するはずであり、分散トレーニングに進む前の健全性チェックとして適しています。
python run_glue.py \
--model_name_or_path bert-base-cased --task_name mrpc \
--do_train --do_eval --max_seq_length 128 \
--per_device_train_batch_size 32 --learning_rate 2e-5 --num_train_epochs 3 \
--output_dir /tmp/mrpc/ --overwrite_output_dir True
このジョブは7分46秒かかります。では、oneCCLを使用して分散ジョブを設定して処理を高速化しましょう!
oneCCLを使用した分散ジョブの設定
分散トレーニングジョブを実行するには、次の3つのステップが必要です。
- トレーニングクラスタのノードをリストアップする
- 環境変数を定義する
- トレーニングスクリプトを変更する
トレーニングクラスタのノードをリストアップする
マスターインスタンス上のtransformers/examples/pytorch/text-classification
内で、hostfile
という名前のテキストファイルを作成します。このファイルにはクラスタ内のノードの名前(IPアドレスでも構いません)を格納します。最初の行はマスターインスタンスを指す必要があります。
以下は私のファイルです。
ip-172-31-28-17.ec2.internal
ip-172-31-30-87.ec2.internal
ip-172-31-29-11.ec2.internal
ip-172-31-20-77.ec2.internal
環境変数の定義
次に、マスターノード上でいくつかの環境変数を設定する必要があります。特に、マスターノードのIPアドレスが必要です。oneCCL変数の詳細については、ドキュメントを参照してください。
for nic in eth0 eib0 hib0 enp94s0f0; do
master_addr=$(ifconfig $nic 2>/dev/null | grep netmask | awk '{print $2}'| cut -f2 -d:)
if [ "$master_addr" ]; then
break
fi
done
export MASTER_ADDR=$master_addr
source /home/ec2-user/anaconda3/envs/transformer/lib/python3.7/site-packages/torch_ccl-1.3.0+43f48a1-py3.7-linux-x86_64.egg/torch_ccl/env/setvars.sh
export LD_LIBRARY_PATH=/home/ec2-user/anaconda3/envs/transformer/lib/python3.7/site-packages/torch_ccl-1.3.0+43f48a1-py3.7-linux-x86_64.egg/:$LD_LIBRARY_PATH
export LD_PRELOAD="${CONDA_PREFIX}/lib/libtcmalloc.so:${CONDA_PREFIX}/lib/libiomp5.so"
export CCL_WORKER_COUNT=4
export CCL_WORKER_AFFINITY="0,1,2,3,32,33,34,35"
export CCL_ATL_TRANSPORT=ofi
export ATL_PROGRESS_MODE=0
トレーニングスクリプトの変更
以下の変更は、分散トレーニングを可能にするために私たちのトレーニングスクリプト(run_glue.py
)に既に適用されています。独自のトレーニングコードを使用する場合は、同様の変更を適用する必要があります。
torch_ccl
パッケージをインポートします。- マスターノードのアドレスとクラスタ内のノードのローカルランクを受け取ります。
+import torch_ccl
+
import datasets
import numpy as np
from datasets import load_dataset, load_metric
@@ -47,7 +49,7 @@ from transformers.utils.versions import require_version
# Transformersの最小バージョンがインストールされていない場合はエラーになります。自己責任で削除してください。
-check_min_version("4.13.0.dev0")
+# check_min_version("4.13.0.dev0")
require_version("datasets>=1.8.0", "修正するには: examples/pytorch/text-classification/requirements.txtをpip installします")
@@ -191,6 +193,17 @@ def main():
# またはこのスクリプトに--helpフラグを渡すことで確認できます。
# 現在、異なる引数のセットを保持し、関心事をより明確に分離しています。
+ # cpu-distのためのローカルランクを追加します
+ sys.argv.append("--local_rank")
+ sys.argv.append(str(os.environ.get("PMI_RANK", -1)))
+
+ # ccl固有の環境変数
+ if "ccl" in sys.argv:
+ os.environ["MASTER_ADDR"] = os.environ.get("MASTER_ADDR", "127.0.0.1")
+ os.environ["MASTER_PORT"] = "29500"
+ os.environ["RANK"] = str(os.environ.get("PMI_RANK", -1))
+ os.environ["WORLD_SIZE"] = str(os.environ.get("PMI_SIZE", -1))
+
parser = HfArgumentParser((ModelArguments, DataTrainingArguments, TrainingArguments))
if len(sys.argv) == 2 and sys.argv[1].endswith(".json"):
セットアップは完了しました。トレーニングジョブを2つのノードと4つのノードにスケールアップしましょう。
oneCCLを使用した分散ジョブの実行
マスターノードでは、mpirun
を使用して2つのノードのジョブを起動します。 -np
(プロセス数)は2に設定され、-ppn
(ノードごとのプロセス数)は1に設定されます。そのため、hostfile
の最初の2つのノードが選択されます。
mpirun -f hostfile -np 2 -ppn 1 -genv I_MPI_PIN_DOMAIN=[0xfffffff0] \
-genv OMP_NUM_THREADS=28 python run_glue.py \
--model_name_or_path distilbert-base-uncased --task_name mrpc \
--do_train --do_eval --max_seq_length 128 --per_device_train_batch_size 32 \
--learning_rate 2e-5 --num_train_epochs 3 --output_dir /tmp/mrpc/ \
--overwrite_output_dir True --xpu_backend ccl --no_cuda True
数秒で、最初の2つのノードでジョブが開始されます。ジョブは4分39秒で完了し、1.7倍のスピードアップが実現されます。
-np
を4に設定し、新しいジョブを起動すると、クラスタの各ノードで1つのプロセスが実行されるのが見えます。
トレーニングは2分36秒で完了し、3倍のスピードアップが実現されます。
最後のことですが、--task_name
をqqp
に変更し、Quora Question Pairs GLUEタスク(40万以上のトレーニングサンプルが含まれる)も実行しました。微調整の時間は次のとおりです:
- 単一ノード: 11時間22分、
- 2つのノード: 6時間38分(1.71倍)、
- 4つのノード: 3時間51分(2.95倍)。
スピードアップはかなり一貫しているようです。異なる学習率、バッチサイズ、およびoneCCLの設定を試してみてください。さらに高速化できるはずです!
結論
この投稿では、Intel CPUとパフォーマンスライブラリに基づいた分散トレーニングクラスターの構築方法と、このクラスターを使用してファインチューニングジョブを高速化する方法について学びました。実際に、転移学習はCPUトレーニングを再び注目させるものであり、次のディープラーニングワークフローの設計と構築時にはぜひ考慮すべきです。
この長い投稿をお読みいただきありがとうございました。情報が役に立ったと思います。ご意見や質問は[email protected]までお気軽にお寄せください。次回まで、学び続けましょう!
ジュリアン
We will continue to update VoAGI; if you have any questions or suggestions, please contact us!
Was this article helpful?
93 out of 132 found this helpful
Related articles