PyTorch モデルのトレーニングを高速化するための 9 つのヒント!

PyTorch モデルのトレーニングを高速化するための 9 つのヒント!

[[353240]]

ニューラルネットワークをこのようにしないでください

正直に言えば、あなたのモデルはおそらくまだ石器時代のものでしょう。おそらく、まだ 32 ビット精度や GASP を使用したり、1 つの GPU だけでトレーニングしたりしているのではないでしょうか。

ニューラル ネットワークを高速化する方法に関するガイドはオンライン上にたくさんありますが、チェックリストはありません。このチェックリストを使用して、ステップごとに実行し、モデルのパフォーマンスを最大限に引き出すようにしてください。

このガイドでは、ネットワークを最大限に活用するための最も単純な構成から最も複雑な変更まですべてを網羅しています。 Pytorch-lightning Trainer で使用できる Pytorch コードの例と関連フラグを紹介します。これにより、自分でコードを記述する必要がなくなります。

**このガイドは誰向けですか? **Pytorch を使用してディープラーニング モデルに取り組んでいる人、研究者、博士課程の学生、学者など。ここで説明するモデルのトレーニングには、数日、場合によっては数週間または数か月かかることもあります。

以下を取り上げます:

  • DataLoader の使用
  • DataLoaderのワーカー数
  • バッチサイズ
  • 勾配累積
  • 保存された計算グラフ
  • シングルに移動
  • 16ビット混合精度トレーニング
  • 複数の GPU への移行 (モデル複製)
  • 複数の GPU ノード (8 個以上の GPU) への移行
  • モデルの加速について考えるためのヒント

パイトーチライトニング

[[353241]]

ここで説明するすべての最適化は、Pytorch ライブラリ Pytorch-lightning で見つけることができます。 Lightning は Pytorch の上のラッパーであり、トレーニングを自動化しながら研究者に主要なモデル コンポーネントの完全な制御を提供します。 Lightning は最新のベストプラクティスを使用し、間違いが発生する可能性のある場所を最小限に抑えます。

MNIST 用の LightningModel を定義し、Trainer を使用してモデルをトレーニングします。

  1. pytorch_lightningからTrainerをインポート 
  2. モデル= LightningModule (…)  
  3. トレーナー=トレーナー()  
  4. トレーナー.フィット(モデル)

1. データローダー

おそらく、ここで最も簡単に速度を向上できます。データの読み込みを高速化するために h5py または numpy ファイルを保存する時代は終わりました。Pytorch データローダーを使用すると、画像データの読み込みが簡単になります (NLP データについては、TorchText を参照してください)。

Lightning では、トレーニング ループを指定する必要はなく、dataLoaders と Trainer を定義するだけで、必要なときに呼び出されます。

  1. データセット= MNIST (ルート= self .hparams.data_root、トレーニングトレーニング= train、ダウンロード= True )  
  2. ローダー= DataLoader (データセット、 batch_size = 32 shuffle = True )
  3. バッチインローダーの場合:  
  4. x, y =バッチ   
  5. モデル.トレーニングステップ(x, y)  
  6. ...

2. DataLoader のワーカー数

もう 1 つの驚くべき高速化は、バッチ読み込みを並列で実行できることです。したがって、一度に 1 つのバッチをロードする代わりに、一度に nb_workers バッチをロードできます。

  1. #遅い 
  2. ローダー= DataLoader (データセット、 batch_size = 32 shuffle = True )  
  3. # 高速(ワーカーを 10 個使用)
  4. ローダー= DataLoader (データセット、 batch_size = 32 shuffle = True num_workers = 10 )

3. バッチサイズ

[[353244]]

次の最適化手順を開始する前に、バッチ サイズを CPU-RAM または GPU-RAM で許可されている最大値まで増やします。

次のセクションでは、バッチ サイズを継続的に増やすためにメモリ フットプリントを削減する方法に焦点を当てます。

学習率を再度更新する必要がある場合があることに注意してください。経験則としては、バッチ サイズが 2 倍になると学習率も 2 倍になるということです。

4. 勾配の蓄積

計算リソースの制限に達した場合、バッチ サイズはまだ小さすぎる (たとえば 8) ため、勾配降下法で適切な推定値を提供するには、より大きなバッチ サイズをシミュレートする必要があります。

バッチ サイズを 128 にしたいとします。バッチ サイズ 8 で 16 回のフォワード パスとバックワード パスを実行し、その後に 1 つの最適化ステップを実行する必要があります。

  1. # 最後のステップをクリア 
  2. オプティマイザ.zero_grad()  
  3. # 16 累積勾配ステップ 
  4. スケール損失= 0    
  5. 範囲(16)内のaccumulated_step_iの場合:  
  6. 出力= model.forward ()  
  7. 損失= some_loss (out,y)  
  8. 損失.後方()  
  9. スケール損失 += loss.item()      
  10. # 8ステップ後に重みを更新します。実効バッチ= 8 * 16  
  11. オプティマイザ.ステップ()  
  12. # 損失は累積バッチ数に応じて拡大されるようになりました 
  13. 実際の損失=スケール損失/ 16

Lightning では、accumulate_grad_batches=16 を設定するだけですべてが自動的に実行されます。

  1. トレーナー=トレーナー( accumulate_grad_batches = 16 )  
  2. トレーナー.フィット(モデル)

5. 保持された計算グラフ

[[353246]]

記憶力を爆発的に高める最も簡単な方法の 1 つは、損失を記録して保存することです。

  1. 損失= []  
  2. ...  
  3. 損失.append(損失)
  4. print(f'現在の損失: {torch.mean(losses)'})

上記の問題は、損失にグラフ全体のコピーがまだ含まれていることです。この場合は、.item() を呼び出して解放します。

  1. ![1_CER3v8cok2UOBNsmnBrzPQ](Pytorch で超高速ニューラル ネットワークをトレーニングするための 9 つのヒント.assets/1_CER3v8cok2UOBNsmnBrzPQ.gif)# 悪い 
  2. 損失.append(損失)  
  3. #良い 
  4. 損失.append(損失.item())

Lightning は、計算グラフのコピーが保持されないように細心の注意を払っています。

6. シングルGPUトレーニング

[[353247]]

前の手順を完了したら、GPU トレーニングに進みます。 GPU でのトレーニングでは、複数の GPU コアにわたって数学的な計算が並列化されます。得られる速度向上は、使用する GPU の種類によって異なります。個人には2080Ti、企業にはV100をお勧めします。

一見すると、これは大変そうに思えるかもしれませんが、実際に必要なことは 2 つだけです。1) モデルを GPU に移動し、2) モデルにデータを実行するたびに、データを GPU に配置します。

  1. # モデルを GPU に配置する 
  2. モデル.cuda(0)  
  3. # データを GPU に置きます (変数の Cuda は Cuda のコピーを返します)  
  4. x x = x.cuda(0)  
  5. # 現在はGPUで実行されています 
  6. モデル(x)

Lightningを使用する場合は、何もする必要はなく、Trainer(gpus=1)を設定するだけです。

  1. # トレーニングに GPU 0 を使用するように Lightning に指示します 
  2. トレーナー=トレーナー( gpus =[0])  
  3. トレーナー.フィット(モデル)

GPU でトレーニングするときに注意すべき主な点は、CPU と GPU 間の転送回数を制限することです。

  1. # 高い 
  2. x x = x.cuda(0) # 非常に高価 
  3. x x = x.cpu()  
  4. x x = x.cuda(0)

メモリが不足している場合は、メモリを節約するためにデータを CPU に戻さないでください。 GPU に頼る前に、他の方法でコードや GPU 間のメモリ配分を最適化してみてください。

もう 1 つ注意すべき点は、GPU 同期を強制する操作を呼び出すことです。メモリ キャッシュをクリアすることが一例です。

  1. # 本当に悪い考えです。すべての GPU が追いつくまで停止します 
  2. torch.cuda.empty_cache()

ただし、Lightning を使用する場合、問題が発生する可能性があるのは Lightning モジュールを定義するときだけです。 Lightning はこのような間違いを起こさないように特別な注意を払っています。

7. 16ビット精度

16 ビット精度は、メモリ使用量を半分に削減する驚くべき技術です。ほとんどのモデルは 32 ビット精度の数値を使用してトレーニングされます。しかし、最近の研究では、16 ビット モデルも適切に機能することがわかりました。混合精度とは、一部のものに 16 ビットを使用し、重みなどのものは 32 ビットのままにすることを意味します。

Pytorch で 16 ビットの精度を使用するには、NVIDIA の apex ライブラリをインストールし、モデルに次の変更を加えます。

  1. # モデルとオプティマイザで16ビットを有効にする 
  2. モデル、オプティマイザ= amp .initialize(モデル、オプティマイザ、 opt_level = 'O2' )  
  3. # .backward を実行するときは、amp に実行させて損失をスケールできるようにします 
  4. amp.scale_loss(loss, optimizer) を scaled_loss として使用します。
  5.   スケール損失.後方()

amp パッケージがこのほとんどを処理します。勾配が爆発したり 0 に近づいたりする場合でも、損失をスケーリングします。

Lightning では、16 ビットを有効にするためにモデルを変更する必要はなく、上記で説明した操作を実行する必要もありません。 Trainer(精度=16)を設定するだけです。

  1. トレーナー=トレーナー( amp_level = 'O2' use_amp = False )  
  2. トレーナー.フィット(モデル)

8. 複数のGPUへの移行

さて、事態は本当に面白くなってきました。マルチ GPU トレーニングを実行する方法は 3 つ (おそらくそれ以上) あります。

バッチトレーニング

A) モデルを各GPUにコピーする、B) 各GPUにバッチの一部を与える

最初のアプローチは「バッチ トレーニング」と呼ばれます。この戦略では、モデルを各 GPU にコピーし、各 GPU がバッチの一部を取得します。

  1. # 各GPUにモデルをコピーし、バッチの4分の1を各GPUに渡します 
  2. モデル= DataParallel (モデル、デバイス=[0, 1, 2, 3])  
  3. # out には 4 つの出力があります (各 GPU に 1 つずつ)  
  4. 出力=モデル(x.cuda(0))

Lightning では、GPU の数を増やしてトレーナーに伝えるだけで、他に何もする必要はありません。

  1. # トレーニングに4つのGPUを使用するようにLightningに指示する 
  2. トレーナー=トレーナー( gpus =[0, 1, 2, 3])  
  3. トレーナー.フィット(モデル)

モデル配布トレーニング

モデルの異なる部分を異なるGPUに配置し、バッチを順番に移動する

モデルが大きすぎてメモリ内に完全に収まらない場合があります。たとえば、エンコーダーとデコーダーを備えたシーケンスツーシーケンス モデルでは、出力を生成するときに 20 GB の RAM を消費する可能性があります。この場合、エンコーダーとデコーダーを別々の GPU に配置する必要があります。

  1. # 各モデルが非常に大きいため、両方をメモリに収めることができません 
  2. エンコーダ_rnn.cuda(0)  
  3. デコーダー_rnn.cuda(1)  
  4. # GPU 0 のエンコーダーを介して入力を実行する 
  5. エンコーダ出力=エンコーダrnn (x.cuda(0))  
  6. # 次の GPU のデコーダーを介して出力を実行します 
  7. 出力=デコーダー_rnn (エンコーダー_out.cuda(1))  
  8. # 通常、すべての出力をGPU 0に戻します 
  9. アウトアウト= out.cuda(0)

このタイプのトレーニングでは、Lightning で GPU を指定する必要はありません。LightningModule 内のモジュールを正しい GPU に配置する必要があります。

  1. クラス MyModule(LightningModule):  
  2. デフ__init__():  
  3. self.encoder = RNN (...)  
  4. self.decoder = RNN (...)  
  5. def forward(x):
  6.   # モデルは最初の転送後には移動されません。  
  7. # すでに正しいGPUに搭載されています 
  8. 自己.エンコーダ.cuda(0)  
  9. 自己デコーダーcuda(1)  
  10. 出力=自己.encoder(x)  
  11. out =自己.decoder(out.cuda(1))       
  12.   # トレーナーにGPUを渡さない
  13. モデル= MyModule ()  
  14. トレーナー=トレーナー()  
  15. トレーナー.フィット(モデル)

両方の混合

上記の場合でも、エンコーダーとデコーダーは並列操作のメリットを享受できます。

  1. # これらの行を変更 
  2. self.encoder = RNN (...)  
  3. self.decoder = RNN (...)  
  4. # これらに 
  5. # 各 RNN は異なる GPU セットに基づいています 
  6. self.encoder = DataParallel (self.encoder、デバイス=[0, 1, 2, 3])  
  7. self.decoder = DataParallel (self.encoder、デバイス=[4、5、6、7])  
  8. # 転送中...  
  9. 出力=自己.encoder(x.cuda(0))  
  10. # デバイスの最初の GPU の入力を通知 
  11. sout = self .decoder(out.cuda(4)) # < ---ここでの4

複数の GPU を使用する場合に考慮すべき事項:

  • モデルがすでに GPU 上にある場合、model.cuda() は何も実行しません。
  • 入力は常にデバイス リストの最初のデバイスに配置します。
  • デバイス間でデータを転送するのはコストがかかるため、最後の手段として使用してください。
  • オプティマイザーと勾配は GPU 0 に保存されるため、GPU 0 で使用されるメモリは他の GPU よりもはるかに大きくなる可能性があります。

9. マルチノードGPUトレーニング

各マシンの各 GPU ごとにモデルのコピーが存在します。各マシンはデータの一部を取得し、その部分のみをトレーニングします。各マシンは勾配を同期できます。

ここまでくれば、Imagenet をわずか数分でトレーニングできるようになります。これは思ったほど難しくはありませんが、コンピューティング クラスターに関する詳細な知識が必要になる場合があります。これらの手順では、クラスター上で SLURM を使用していることを前提としています。

Pytorch では、各ノードの各 GPU でモデルを複製し、勾配を同期することで、マルチノード トレーニングが可能になります。したがって、各モデルは各 GPU で独立して初期化され、すべてのモデルから勾配更新を受信することを除いて、基本的にデータのパーティションで独立してトレーニングされます。

概要:

  1. 各 GPU でモデルのコピーを初期化します (各モデルが同じ重みに初期化されるようにシードを設定してください。そうしないと失敗します)。
  2. データセットをサブセットに分割します (DistributedSampler を使用)。各 GPU は独自の小さなサブセットのみをトレーニングします。
  3. .backward() では、すべてのレプリカがすべてのモデルの勾配のコピーを受け取ります。これはモデル間の唯一の通信です。

Pytorch には、これを実行する DistributedDataParallel という優れた抽象化があります。 DDP を使用するには、次の 4 つのことを行う必要があります。

  1. tng_dataloader() を定義します:  
  2. d = MNIST ()  
  3. # 4: 分散サンプラーを追加する 
  4. # サンプラーはtngデータの一部を各マシンに送信します 
  5. dist_sampler =分散サンプラー(データセット)  
  6. データローダー= DataLoader (d、シャッフル= False サンプラー= dist_sampler )     
  7.   main_process_entrypoint(gpu_nb)を定義します。  
  8. # 2: すべてのマシンのすべてのGPU間の接続を設定する 
  9. # すべての GPU が単一の GPU「ルート」に接続します 
  10. # デフォルトでは env:// を使用します 
  11. ワールド= nb_gpus * nb_nodes  
  12. dist.init_process_group("nccl",ランク= gpu_nb ワールドworld_size = world)        
  13.   # 3: DPPでモデルをラップする 
  14. torch.cuda.set_device(gpu_nb)  
  15. モデル.cuda(gpu_nb)  
  16. モデル= DistributedDataParallel (モデル、デバイスID =[gpu_nb])     
  17.   # 今すぐモデルをトレーニングします...     
  18.   __name__ == '__main__' の場合:  
  19. # 1: プロセスの数を生成する 
  20. # クラスターは各マシンに対してmainを呼び出します 
  21. mp.spawn(メインプロセスエントリポイント、 nprocs = 8 )

ただし、Lightning では、ノードの数を設定するだけで、残りの作業は自動的に処理されます。

  1. # 128 ノードの 1024 GPU でトレーニング
  2. トレーナー=トレーナー( nb_gpu_nodes = 128 gpus =[0、1、2、3、4、5、6、7])

Lightning には SlurmCluster マネージャーも付属しており、SLURM ジョブの正しい詳細を送信するのに便利です。

10. メリット!単一ノードで複数のGPUを使用してトレーニングを高速化

分散データ並列は勾配同期のための通信のみを実行するため、データ並列よりもはるかに高速であることがわかります。したがって、単一のマシンでトレーニングする場合でも、DataParallel ではなく、distributedDataParallel を使用するのがよい方法です。

Lightning では、distributed_backend を ddp に設定し、GPU の数を設定することでこれを簡単に実現できます。

  1. # 同じマシン上の 4 つの GPU でトレーニングすると、DataParallel よりもはるかに高速になります 
  2. トレーナー=トレーナー(分散バックエンド= 'ddp' gpus = [0, 1, 2, 3])

モデルの加速に関する考察

このガイドでは、ネットワーク速度を向上させるためのヒントをいくつか紹介しますが、ボトルネックを見つけて問題を考える方法についても説明します。

モデルをいくつかの部分に分割しました。

まず、データの読み込みにボトルネックがないことを確認します。このために、私は説明した既存のデータ読み込みソリューションを使用しましたが、それらのソリューションのいずれもニーズを満たさない場合は、オフライン処理と h5py などの高性能データ ストアへのキャッシュを検討してください。

次に、トレーニング ステップで何を行うかを見てみましょう。フォワードパスが高速であることを確認し、過剰な計算を避け、CPU と GPU 間のデータ転送を最小限に抑えます。最後に、GPU の速度を低下させるようなことは避けてください (このガイドで説明されています)。

次に、通常は GPU メモリのサイズによって制限されるバッチ サイズを最大化しようとしました。ここで、大きなバッチ サイズを使用する場合に、複数の GPU 間でレイテンシを分散して最小限に抑える方法に焦点を当てる必要があります (たとえば、複数の GPU 間で 8000 以上の有効バッチ サイズを使用することを試みる場合があります)。

ただし、バッチサイズが大きい場合は注意が必要です。具体的な質問については、関連する文献を調べて、人々が何を見落としているかを確認してください。

<<:  アルゴリズム エンジニアはなぜ一日中データを扱うのでしょうか。また、どのような種類のデータを扱うのでしょうか。

>>:  将来の医療における人工知能の重要な役割

ブログ    
ブログ    
ブログ    

推薦する

テクノロジー大手は疑似環境の仮面を脱ぎ捨て、AIの積極的な開発によりエネルギー消費が増加している

9月4日のニュースによると、マイクロソフト、アマゾン、グーグル、フェイスブックの親会社であるMeta...

プライバシー保護における新たなブレークスルー: ガウス差分プライバシー フレームワークとディープラーニングの組み合わせ

[[324532]]人工知能におけるプライバシーの問題は、重要かつ深刻な問題として認識されています。...

人工知能の導入は、より費用対効果の高い臨床試験の新しい時代を告げるだろう

臨床試験はここ数年で大きく変化しました。医薬品や医療機器、そしてそれらが影響を与える対象となる症状が...

Daguan 推奨アルゴリズムの実装: 協調フィルタリングのアイテム埋め込み

レコメンデーションシステムの本質は、ユーザーのニーズが不明確な場合の情報過多の問題を解決し、ユーザー...

クアルコムのアモン社長:5G+AIがインテリジェントな接続の未来を切り開く

7月9日、2020年世界人工知能大会(WAIC)クラウドサミットが正式に開幕した。クアルコムのクリス...

Wu Sinan の機械学習への旅: Numpy で多次元配列を作成する

[[188605]] Numpy は Python 科学計算のコアライブラリの 1 つであり、主に多...

DIFFアルゴリズムがわからない場合は、私に連絡してください(画像付き)

序文インタビュアー: 「仮想 DOM と Diff アルゴリズムをご存知ですか? 説明してください。...

エッジウェアハウジング: 9 つの新しいウェアハウジング技術

倉庫業界はテクノロジー主導の革命の真っ只中にあり、企業はコストを削減し、業務を最適化し、サプライチェ...

SafetyNet: 自動運転における機械学習戦略のための安全な計画アプローチ

[[427712]] 2021年9月28日にarXivにアップロードされた論文「SafetyNet:...

自動車の未来:スマート製造の採用

インテリジェント製造技術の出現は自動車製造業界に大きな影響を与えました。まず、スマート製造では、自動...

2024年に期待するAI関連ニュース5選

OpenAIが2022年11月にChatGPTをリリースした後、GPT-4やEU AI法案からAI検...

...

AIチャットボットがコロナウイルスによる人員不足の問題を緩和する方法

人工知能 (AI) の最も魅力的な利点の 1 つは、人々がより多くのタスクを達成できるように支援でき...

人工知能は航空宇宙に貢献しており、我が国の有人宇宙計画の宇宙ステーションの軌道上建設ミッションは着実に前進している。

中国有人宇宙工程弁公室によると、2021年以来、我が国の有人宇宙計画は宇宙ステーションの重要技術検証...