PytorchマルチGPUの訓練 - あなたが必要 - すべての単一運用ノード

アウトライン

PytorchトレーニングマルチGPUのデータは、本質的に、各GPU上で全体モデルパラメータ、データのバッチをN個の部分に分割されると、並列データ処理GPUのそれぞれ、および各GPUの統合を横切って得られる勾配上にありますバッチ連結すべてのGPU勾配の勾配更新パラメータ、完全な反復。

前記マルチGPUトレーニングプログラム、2は、ある1が使用することですnn.DataParallel、このメソッドの実装を使用して簡単に、pytorchを導入する最初であり、複数のプロセスを必要としません。別の使用するtorch.nn.parallel.DistributedDataParallel
torch.utils.data.distributed.DistributedSampler、第二のアプローチのより高い効率を達成するために複数のプロセスを組み合わせて参照するが、実施するのが少し困難で、第二のアプローチは、マルチノード分散型の実装をサポートします。スキームIIにも単一のコンピューティングノード、基準上の実施例よりも高い効率、pytorchのDOC

単一マシン同期場合、torch.distributedまたはtorch.nn.parallel.DistributedDataParallel()ラッパーは依然としてtorch.nn.DataParallel()を含むデータ並列に他のアプローチを超える利点を有することができます。

この記事では、スタンドアローンで達成することが制限された詳細これらの2つの方法の実現は、より複雑な分散、次回の記事では紹介しますされます。
参考:

オプション1

ステップ

  • モデルnn.DataParallelラップ。
model = nn.DataParallel(model)
  • よるos.environ["CUDA_VISIBLE_DEVICES"]="0"GPUのデバイス番号を使用して指定されていないGPUデバイスは、すべての機器を使用する場合は、現在のプログラムを指定します。
os.environ["CUDA_VISIBLE_DEVICES"]="0,1,2" #使用3个GPU
  • GPUにmodel.cuda()またはmodel.to( "CUDA")とdata.cuda()またはdata.to( "CUDA")とモデルデータ。

シングルGPUの使用と一致するトレーニングプロセスは、この方法を使用して、pytorchバッチは自動的に(Nは、N個の部分にデータを破損するos.environ、前方に、それぞれ、指定された量のGPU)後方、各GPUに自動的に統合された勾配シングルGPUの更新パラメータは、最後のパラメータは、他のGPU、完全な反復に放送されます。

テスト

コード:


拡張

import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
import os

# dataset
class RandomDataset(Dataset):

    def __init__(self, size, length):
        self.len = length
        self.data = torch.randn(length, size)

    def __getitem__(self, index):
        return self.data[index]

    def __len__(self):
        return self.len

# model define
class Model(nn.Module):
    # Our model

    def __init__(self, input_size, output_size):
        super(Model, self).__init__()
        self.fc = nn.Linear(input_size, output_size)

    def forward(self, input):
        output = self.fc(input)
        print("\tIn Model: input size", input.size(),
              "output size", output.size())

        return output

if __name__=="__main__":
    # Parameters
    input_size = 5
    output_size = 2

    batch_size = 30
    data_size = 100

    dataset = RandomDataset(input_size, data_size)
    # dataloader define
    rand_loader = DataLoader(dataset=dataset,
                            batch_size=batch_size, shuffle=True)

    # model init
    model = Model(input_size, output_size)

    # cuda devices
    os.environ["CUDA_VISIBLE_DEVICES"]="0,1"
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

    if torch.cuda.device_count() > 1:
        print("Let's use", torch.cuda.device_count(), "GPUs!")
        # dim = 0 [30, xxx] -> [10, ...], [10, ...], [10, ...] on 3 GPUs
        model = nn.DataParallel(model)

    model.to(device)

    for data in rand_loader:
        input = data.to(device)
        output = model(input)
        # loss

        # backward

        #update
        
        time.sleep(1)#模拟一个比较长的batch时间
        print("Outside: input size", input.size(),
            "output_size", output.size())

    torch.save(model.module.state_dict(), "model.pth")
  • GPUは、以下の試験結果は、あなたが外部入力と出力と内部モデルを見ることができる場合と同じです。
        In Model: input size torch.Size([30, 5]) output size torch.Size([30, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
        In Model: input size torch.Size([30, 5]) output size torch.Size([30, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
        In Model: input size torch.Size([30, 5]) output size torch.Size([30, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
        In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
Outside: input size torch.Size([10, 5]) output_size torch.Size([10, 2])
  • 2 GPU場合は、以下の試験結果は、それが自動ロット分割を見ることができます。
        In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
        In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
        In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
        In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
        In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
        In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
        In Model: input size torch.Size([5, 5]) output size torch.Size([5, 2])
        In Model: input size torch.Size([5, 5]) output size torch.Size([5, 2])
Outside: input size torch.Size([10, 5]) output_size torch.Size([10, 2])

注意を払います

  • そこ複数のGPUをプライマリとセカンダリのポイント、更新が​​あるとGPU責任勾配の主なパラメータ、他のGPUに渡された更新されたパラメータ、完全に一回の反復を統合、プロセスパラメータは、両方のGPU間で渡されるので、パラメータがあります勾配移転。
  • バッチノームについての質問を参照大規模なバッチが複数minibatchに分割されているので、唯一の最終的な試験パラメータNoramlization層で行うminibatchに正規にメインGPUです。あなたは、複数のGPU同期正規化を実装する場合は必要な同期ノルムの実装を。問題の正常化は、まだ分散トレーニングに存在します。

オプションII

オプションIIは、マルチプロセスを達成することで、マルチプロセスは、実際に複数のマシンに分散処理の意味を配布され、相互にネットワーク通信コーディネーションの使用。物品上に分散処理をした後に詳細に説明。これは、スタンドアロンプ​​ログラム2とプログラムの違いを紹介します。まず、各プロセスは、最初の反復シェア勾配、勾配統合、独立した更新パラメータの後に、独自のトレーニングプロセスを持っています。パラメータ(すべての同期の初期化プロセスにパラメータ)を渡すと、反復的なプロセスではありません。第二に、当然のNCCLを使用してプロセス間の通信、すでにNCCL内部pytorchのサポートは、その通常の状況下では、このために気にしません。分散型の詳細は、ここでの唯一の最も簡単な実装を提供し、記事を参照してください。

ステップ

  • あなたはそれが、互いの位置、ステータスやその他の情報を知っている、これは狙いを初期化するための最も便利な方法があるため、これは互いに接触を確立するために、初期化プロセスの全てを取得することで、単一のノードに対して、デフォルトモードの初期化を使用して、プロセスグループを初期化する必要があります。
  • データセットは、増加を準備torch.utils.data.distributed.DistributedSampler。具体的には、試験セクションのコードを使用して参照します。
  • モデルは、増加を準備torch.nn.parallel.DistributedDataParallel。具体的には、試験セクションのコードを使用して参照します。
  • 一貫性のあるプログラムでトレーニングプロセスにコードを訓練し、同時に複数のプロセスが稼働している想像してみてください。

テスト

同様のプログラムコードは、プロセスグループを初期化する必要があり、このプログラムは、訓練を分散していることを述べています。指定することにより、複数のプロセスを作成python -m torch.distributed.launch --nproc_per_node=2 --nnodes=1ここでは、計算ノードであるので、1 NNODES、実現nproc_per_node=2それは訓練するために2つのプロセスを作成する必要性を表明し、それをランク数を取得するために、各プロセスに割り当てられ、ランクは、プロセスを一意に識別し、ランクマスタ0、他方がスレーブです。もちろん、一般に二つのGPUを必要とし、試験プログラム、すなわち、ランク0 GPU0の使用、ランクGPUに応じた処理を指定するために使用され、ランク1のプロセスは、GPU1を使用します。私たちは、応じサンプラー分散データセット、サンプラーを指定するためのデータローダーの初期化時間を作成する必要があり、パッケージ分散モデルのコードを参照してください。
コード:


拡張

import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
import os
import torch.distributed as dist
import torch.utils.data.distributed
import sys
import time


# dataset
class RandomDataset(Dataset):

    def __init__(self, size, length):
        self.len = length
        self.data = torch.randn(length, size)

    def __getitem__(self, index):
        return self.data[index]

    def __len__(self):
        return self.len

# model define
class Model(nn.Module):
    # Our model

    def __init__(self, input_size, output_size):
        super(Model, self).__init__()
        self.fc = nn.Linear(input_size, output_size)

    def forward(self, input):
        output = self.fc(input)
        # print("\tIn Model: input size", input.size(),
        #       "output size", output.size())

        return output

if __name__=="__main__":
    # Parameters
    input_size = 5
    output_size = 2

    batch_size = 30
    data_size = 100

    # check the nccl backend
    if not dist.is_nccl_available():
        print("Error: nccl backend not available.")
        sys.exit(1)

    # init group
    dist.init_process_group(backend="nccl", init_method="env://")

    # get the process rank and the world size
    rank = dist.get_rank()
    world_size = dist.get_world_size()

    # prepare the dataset
    dataset = RandomDataset(input_size, data_size)
    train_sampler = torch.utils.data.distributed.DistributedSampler(dataset)
    

    rand_loader = DataLoader(dataset, batch_size=batch_size//world_size, 
                              shuffle=(train_sampler is None), 
                              sampler=train_sampler)

    # dataloader define
    # rand_loader = DataLoader(dataset=dataset,
    #                         batch_size=batch_size, shuffle=True)

    # model init
    model = Model(input_size, output_size)

    # cuda devices
    # os.environ["CUDA_VISIBLE_DEVICES"]="0"
    # device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    # if torch.cuda.device_count() > 1:
    #     print("Let's use", torch.cuda.device_count(), "GPUs!")
    #     # dim = 0 [30, xxx] -> [10, ...], [10, ...], [10, ...] on 3 GPUs
    #     model = nn.DataParallel(model)
    # model.to(device)

    # distribute model define
    device = torch.device('cuda', rank)
    model = model.to(device)
    model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[rank], output_device=rank)
    print("From rank %d: start training, time:%s"%(rank, time.strftime("%Y-%m-%d %H:%M:%S")))
    for data in rand_loader:
        input = data.to(device)
        output = model(input)
        # loss

        # backward

        #update
        
        time.sleep(1)#模拟一个比较长的batch时间
        print("From rank %d: Outside: input size %s, output size %s"%(rank, str(input.size()), str(output.size())),flush=True)
    torch.save(model.module.state_dict(), "model_%d.pth"%rank)
    print("From rank %d: end training, time: %s"%(rank, time.strftime("%Y-%m-%d %H:%M:%S")))
  • コマンドを実行します。
    python -m torch.distributed.launch --nproc_per_node=2 --nnodes=1 simple_test.py
  • 結果
From rank 0: start training, time:2019-09-26 13:20:13
From rank 1: start training, time:2019-09-26 13:20:13
From rank 0: Outside: input size torch.Size([15, 5]), output size torch.Size([15, 2])
From rank 1: Outside: input size torch.Size([15, 5]), output size torch.Size([15, 2])
From rank 0: Outside: input size torch.Size([15, 5]), output size torch.Size([15, 2])
From rank 1: Outside: input size torch.Size([15, 5]), output size torch.Size([15, 2])
From rank 1: Outside: input size torch.Size([15, 5]), output size torch.Size([15, 2])From rank 0: Outside: input size torch.Size([15, 5]), output size torch.Size([15, 2])

From rank 0: Outside: input size torch.Size([5, 5]), output size torch.Size([5, 2])
From rank 0: end training, time: 2019-09-26 13:20:17
From rank 1: Outside: input size torch.Size([5, 5]), output size torch.Size([5, 2])
From rank 1: end training, time: 2019-09-26 13:20:17
*****************************************
Setting OMP_NUM_THREADS environment variable for each process to be 1 in default, to avoid your system being overloaded, please further tune the variable for optimal performance in your application as needed.
*****************************************

私は、直接私たちが問題を並行して複数のプロセスによって引き起こされているので、少し混沌と見ることができ、見て慎重にトレーニングの2つの並列プロセス、各プロセスの半分バッチ処理のデータがある見ることができ、結果Tieshanglaiをテストします。最終OMP_NUM_THREADS情報は、印刷時pytorch lanchは、私がために、システムの過負荷を防止するためにマルチスレッドOMPの数を指定するので、図1に示すように、元のコードのために私に親密な設定を支援しなかったことを翻訳している参照

もう一つ

方法で、単一GPUの違いは、モデルを保存し、ロード。ここでは仕方のCPU負荷のすべてのパラメータメモリに、ために保存された時点で、GPUを置くために、後続の必要性は、パラメータGPUに格納されている場合、パラメータの数に属するGPUのPTHファイルのレコードは、それぞれの荷重にロードされていますGPUの数がこのように、あなたの場合は十分でない場合にモデルを読み込む際にエラーにつながるGPU上で次のようになります。

RuntimeError: Attempting to deserialize object on CUDA device 1 but torch.cuda.device_count() is 1. Please use torch.load with map_location to map your storages to an existing device.

モデルを保存同じですが、いつもあなたが問題に注意を払う必要があり、共有ストレージのファイル名を使用している場合、それはもちろん、一般的にのみrank0、店舗に複数のモデルが保存されます、あなたは第2の方式は、同時に複数のプロセスを実行したことを覚えておいてくださいすべてのプロセスのモデルパラメータが同期されるためのパラメータは、プロセス上に保存することができます。

torch.save(model.module.state_dict(), "model.pth")

負荷モデル:

param=torch.load("model.pth",map_location="cpu")

まあ、私は私が真剣にブログのアップを記述していない、今日ここに書いています。もちろん、いくつかの場所が残っていますこのようなモデルの同期のパラメトリック検定として、完璧ではありません。ご質問がある、または何か問題がある場合と考えられる場合は、^ = ^、コメント領域にカニをお願いします。

おすすめ

転載: www.cnblogs.com/walter-xh/p/11586507.html