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 が IoT に与える影響

ウェアラブル人工知能がモノのインターネット (IoT) の発展に与える影響を探ります。デジタル時代の...

Google Geminiはリリース直後から疑問視されていた:テスト基準に偏りがあり、エフェクトビデオは編集されている疑いがある

Google待望の大躍進、 Gemini大型モデルがついに発売!最も目を引くのは、次の写真とビデオで...

インテリジェントな顧客サービス チャット モジュールの 3 つのソリューションを比較すると、どれが気に入りましたか?

現在のインテリジェント顧客サービス市場とその NLP 分野において、チャット モジュールは非常に重要...

2050年に「電子的不死」は達成されるのか?計画が鍵

海外メディアの報道によると、有名な未来学者イアン・ピアソン博士は最近、今後数十年以内に、人間は思考と...

カナダ工学アカデミー会員のソン・リャン氏:将来の人工知能システムはネットワークの形で存在するだろう

12月5日、国務院の承認を得て、科学技術部と河南省政府の共催により、12月6日から8日まで河南省鄭州...

Google Bard が中国語をサポートするようになりました!レベル10をクリアして、ミームを理解し、無料で試してみましょう

数日前、ChatGPTの最も強力なライバルであるClaudeが第2世代にアップグレードされ、Goog...

...

自動車学校がロボットコーチカーを導入:全行程を通じて優しい音声ガイド、コーチに怒鳴られる必要はもうない

[[356945]]人工知能技術の急速な発展により、SF映画のシーンが現実のものとなった。メディアの...

住宅建設はよりスマートになる

スマートホーム革命はここしばらく本格的に始まっています。住宅所有者はデータと IoT テクノロジーを...

人工知能は人間の知能ではない。まずは人工的なもの、そして知的なもの

人工知能に関しては、インターネット企業はすべてが「魔法のようだ」とよく言います。しかし、そうではあり...

人工知能産業の急速な発展の背後にある4つの大きな無駄

[[258526]]過去7年間、中国のプライベートエクイティ投資市場における人工知能分野への投資額は...

モデルもオンライン授業を受講できますか? !サービス指向の蒸留トレーニング プログラムを 1 つの記事で理解する

この記事はAI新メディアQuantum Bit(公開アカウントID:QbitAI)より許可を得て転載...

...