Rumah > Artikel > Peranti teknologi > Latihan selari PyTorch Contoh kod lengkap DistributedDataParallel
Masalah melatih rangkaian neural dalam yang besar (DNN) menggunakan set data yang besar merupakan cabaran utama dalam bidang pembelajaran mendalam. Apabila saiz DNN dan set data meningkat, begitu juga keperluan pengiraan dan memori untuk melatih model ini. Ini menjadikannya sukar atau bahkan mustahil untuk melatih model ini pada satu mesin dengan sumber pengkomputeran yang terhad. Beberapa cabaran utama melatih DNN besar menggunakan set data yang besar termasuk:
Untuk menangani cabaran ini, pelbagai teknik telah dibangunkan untuk meningkatkan latihan DNN besar dengan set data yang besar, termasuk keselarian model, keselarian data dan keselarian hibrid, serta perkakasan, perisian dan Pengoptimuman algoritma.
Dalam artikel ini kami akan menunjukkan keselarian data dan keselarian model menggunakan PyTorch.
Apa yang kami panggil selari secara amnya merujuk kepada melatih rangkaian saraf dalam (dnn) pada berbilang GPU atau berbilang mesin untuk mencapai Kurang masa latihan. Idea asas di sebalik keselarian data adalah untuk membahagikan data latihan kepada bahagian yang lebih kecil dan membiarkan setiap GPU atau mesin memproses sebahagian data yang berasingan. Keputusan untuk setiap nod kemudiannya digabungkan dan digunakan untuk mengemas kini parameter model. Dalam keselarian data, seni bina model adalah sama pada setiap nod, tetapi parameter model dipisahkan antara nod. Setiap nod melatih model setempatnya sendiri menggunakan ketulan data yang diperuntukkan, dan pada penghujung setiap lelaran latihan, parameter model disegerakkan merentas semua nod. Proses ini diulang sehingga model menumpu kepada hasil yang memuaskan.
Di bawah kami menggunakan set data ResNet50 dan CIFAR10 untuk contoh kod lengkap:
Dalam keselarian data, seni bina model kekal sama pada setiap nod, tetapi parameter model adalah berbeza antara nod. Pembahagian dilakukan, dan setiap nod melatih model setempatnya sendiri menggunakan ketulan data yang diperuntukkan.
Pustaka DistributedDataParallel PyTorch boleh berkomunikasi dan menyegerakkan kecerunan dan parameter model dengan cekap merentas nod untuk mencapai latihan teragih. Artikel ini memberikan contoh cara untuk melaksanakan keselarian data dengan PyTorch menggunakan set data ResNet50 dan CIFAR10, di mana kod dijalankan pada berbilang GPU atau mesin, dengan setiap mesin memproses subset data latihan. Proses latihan diselaraskan menggunakan perpustakaan DistributedDataParallel PyTorch.
import os from datetime import datetime from time import time import argparse import torchvision import torchvision.transforms as transforms import torch import torch.nn as nn import torch.distributed as dist from torch.nn.parallel import DistributedDataParallel
Seterusnya, kami akan menyemak GPU.
import subprocess result = subprocess.run(['nvidia-smi'], stdout=subprocess.PIPE) print(result.stdout.decode())
Oleh kerana kita perlu menjalankan berbilang pelayan, adalah tidak praktikal untuk melaksanakannya satu demi satu secara manual, jadi penjadual diperlukan. Di sini kami menggunakan fail SLURM untuk menjalankan kod (slurmpenjadual kerja sumber terbuka dan percuma untuk Linux dan kernel seperti Unix),
def main(): # get distributed configuration from Slurm environment parser = argparse.ArgumentParser() parser.add_argument('-b', '--batch-size', default=128, type =int, help='batch size. it will be divided in mini-batch for each worker') parser.add_argument('-e','--epochs', default=2, type=int, metavar='N', help='number of total epochs to run') parser.add_argument('-c','--checkpoint', default=None, type=str, help='path to checkpoint to load') args = parser.parse_args() rank = int(os.environ['SLURM_PROCID']) local_rank = int(os.environ['SLURM_LOCALID']) size = int(os.environ['SLURM_NTASKS']) master_addr = os.environ["SLURM_SRUN_COMM_HOST"] port = "29500" node_id = os.environ['SLURM_NODEID'] ddp_arg = [rank, local_rank, size, master_addr, port, node_id] train(args, ddp_arg)
Kemudian, kami menggunakan perpustakaan DistributedDataParallel untuk melaksanakan latihan yang diedarkan.
def train(args, ddp_arg): rank, local_rank, size, MASTER_ADDR, port, NODE_ID = ddp_arg # display info if rank == 0: #print(">>> Training on ", len(hostnames), " nodes and ", size, " processes, master node is ", MASTER_ADDR) print(">>> Training on ", size, " GPUs, master node is ", MASTER_ADDR) #print("- Process {} corresponds to GPU {} of node {}".format(rank, local_rank, NODE_ID)) print("- Process {} corresponds to GPU {} of node {}".format(rank, local_rank, NODE_ID)) # configure distribution method: define address and port of the master node and initialise communication backend (NCCL) #dist.init_process_group(backend='nccl', init_method='env://', world_size=size, rank=rank) dist.init_process_group( backend='nccl', init_method='tcp://{}:{}'.format(MASTER_ADDR, port), world_size=size, rank=rank ) # distribute model torch.cuda.set_device(local_rank) gpu = torch.device("cuda") #model = ResNet18(classes=10).to(gpu) model = torchvision.models.resnet50(pretrained=False).to(gpu) ddp_model = DistributedDataParallel(model, device_ids=[local_rank]) if args.checkpoint is not None: map_location = {'cuda:%d' % 0: 'cuda:%d' % local_rank} ddp_model.load_state_dict(torch.load(args.checkpoint, map_location=map_location)) # distribute batch size (mini-batch) batch_size = args.batch_size batch_size_per_gpu = batch_size // size # define loss function (criterion) and optimizer criterion = nn.CrossEntropyLoss() optimizer = torch.optim.SGD(ddp_model.parameters(), 1e-4) transform_train = transforms.Compose([ transforms.RandomCrop(32, padding=4), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)), ]) # load data with distributed sampler #train_dataset = torchvision.datasets.CIFAR10(root='./data', # train=True, # transform=transform_train, # download=False) # load data with distributed sampler train_dataset = torchvision.datasets.CIFAR10(root='./data', train=True, transform=transform_train, download=False) train_sampler = torch.utils.data.distributed.DistributedSampler(train_dataset, num_replicas=size, rank=rank) train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=batch_size_per_gpu, shuffle=False, num_workers=0, pin_memory=True, sampler=train_sampler) # training (timers and display handled by process 0) if rank == 0: start = datetime.now() total_step = len(train_loader) for epoch in range(args.epochs): if rank == 0: start_dataload = time() for i, (images, labels) in enumerate(train_loader): # distribution of images and labels to all GPUs images = images.to(gpu, non_blocking=True) labels = labels.to(gpu, non_blocking=True) if rank == 0: stop_dataload = time() if rank == 0: start_training = time() # forward pass outputs = ddp_model(images) loss = criterion(outputs, labels) # backward and optimize optimizer.zero_grad() loss.backward() optimizer.step() if rank == 0: stop_training = time() if (i + 1) % 10 == 0 and rank == 0: print('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}, Time data load: {:.3f}ms, Time training: {:.3f}ms'.format(epoch + 1, args.epochs, i + 1, total_step, loss.item(), (stop_dataload - start_dataload)*1000, (stop_training - start_training)*1000)) if rank == 0: start_dataload = time() #Save checkpoint at every end of epoch if rank == 0: torch.save(ddp_model.state_dict(), './checkpoint/{}GPU_{}epoch.checkpoint'.format(size, epoch+1)) if rank == 0: print(">>> Training complete in: " + str(datetime.now() - start)) if __name__ == '__main__': main()
Kod membahagikan data dan model kepada berbilang GPU dan mengemas kini model dengan cara yang diedarkan. Berikut ialah beberapa penjelasan kod:
train(args, ddp_arg) mempunyai dua parameter, args dan ddp_arg, dengan args ialah parameter baris arahan yang dihantar kepada skrip dan ddp_arg mengandungi parameter berkaitan latihan teragih.
kedudukan, peringkat_tempatan, saiz, MASTER_ADDR, port, NODE_ID = ddp_arg: Buka pek parameter berkaitan latihan yang diedarkan dalam ddp_arg.
Jika kedudukan ialah 0, cetak bilangan GPU yang sedang digunakan dan maklumat alamat IP nod induk.
dist.init_process_group(backend='nccl', init_method='tcp://{}:{}'.format(MASTER_ADDR, port), world_size=size, rank=rank): Gunakan bahagian belakang NCCL Initialize kumpulan proses yang diedarkan.
torch.cuda.set_device(local_rank): Pilih GPU yang ditentukan untuk proses ini.
model = torchvision.models. ResNet50 (pretrained=False).to(gpu): Muatkan model ResNet50 daripada model torchvision dan alihkannya ke gpu yang ditentukan.
ddp_model = DistributedDataParallel(model, device_ids=[local_rank]): Balut model dalam modul DistributedDataParallel, yang bermaksud bahawa kita boleh melakukan latihan teragih
Muatkan data CIFAR-10 Kumpul dan gunakan data transformasi pembesaran.
train_sampler=torch.utils.data.distributed.DistributedSampler(train_dataset,num_replicas=size,rank=rank): Buat objek DistributedSampler untuk memisahkan set data kepada berbilang GPU.
train_loader =torch.utils.data.DataLoader(dataset=train_dataset,batch_size=batch_size_per_gpu,shuffle=False,num_workers=0,pin_memory=True,sampler=train_sampler): Buat objek DataLoa akan dimuatkan dalam kelompok Dalam model, ini konsisten dengan langkah latihan biasa kami, kecuali pensampelan data teragih DistributedSampler ditambah.
Latih model untuk bilangan zaman yang ditentukan dan kemas kini pemberat menggunakan optimizer.step() dalam cara yang diedarkan.
pangkat0 menyimpan pusat pemeriksaan pada penghujung setiap pusingan.
pangkat0 menunjukkan kehilangan dan masa latihan setiap 10 kelompok.
Pada akhir latihan, jumlah masa yang dihabiskan untuk mencetak model latihan juga berada dalam kedudukan0.
telah dilatih pada 1 nod dengan 1/2/3/4 GPU, 2 nod dengan 6/8 GPU dan setiap nod dengan 3/4 GPU Ujian Resnet50 dihidupkan Cifar10 ditunjukkan dalam rajah di bawah Saiz kelompok setiap ujian tetap sama. Masa yang diambil untuk menyelesaikan setiap ujian direkodkan dalam beberapa saat. Apabila bilangan GPU yang digunakan meningkat, masa yang diperlukan untuk menyelesaikan ujian berkurangan. Apabila menggunakan 8 GPU, ia mengambil masa 320 saat untuk diselesaikan, yang merupakan masa terpantas yang direkodkan. Ini sudah pasti, tetapi kita dapat melihat bahawa kelajuan latihan tidak meningkat secara linear dengan peningkatan bilangan GPU Ini mungkin kerana Resnet50 adalah model yang agak kecil dan tidak memerlukan latihan selari.
Menggunakan keselarian data pada berbilang GPU boleh mengurangkan dengan ketara masa yang diperlukan untuk melatih rangkaian saraf dalam (DNN) pada set data tertentu. Apabila bilangan GPU meningkat, masa yang diperlukan untuk menyelesaikan proses latihan berkurangan, menunjukkan bahawa DNN boleh dilatih dengan lebih cekap secara selari.
Pendekatan ini amat berguna apabila berurusan dengan set data yang besar atau seni bina DNN yang kompleks. Dengan memanfaatkan berbilang GPU, proses latihan boleh dipercepatkan, membolehkan lelaran dan percubaan model yang lebih pantas. Walau bagaimanapun, perlu diingatkan bahawa peningkatan prestasi yang dicapai melalui Data Parallelism mungkin dihadkan oleh faktor seperti overhed komunikasi dan had memori GPU, dan memerlukan penalaan berhati-hati untuk mendapatkan hasil yang terbaik.
Atas ialah kandungan terperinci Latihan selari PyTorch Contoh kod lengkap DistributedDataParallel. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!