今天小编给大家分享一下怎么用Pytorch进行多卡训练的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。
Python PyTorch深度学习框架
PyTorch是一个基于Python的深度学习框架,它支持使用CPU和GPU进行高效的神经网络训练。
在大规模任务中,需要使用多个GPU来加速训练过程。
数据并行
“数据并行”是一种常见的使用多卡训练的方法,它将完整的数据集拆分成多份,每个GPU负责处理其中一份,在完成前向传播和反向传播后,把所有GPU的误差累积起来进行更新。数据并行的代码结构如下:
import torch.nn as nn import torch.optim as optim import torch.utils.data as data import torch.distributed as dist import torch.multiprocessing as mp # 定义网络模型 class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.conv1 = nn.Conv2d(3, 32, kernel_size=5) self.relu = nn.ReLU() self.pool = nn.MaxPool2d(kernel_size=2, stride=2) self.fc1 = nn.Linear(4608, 64) self.fc2 = nn.Linear(64, 10) def forward(self, x): x = self.conv1(x) x = self.relu(x) x = self.pool(x) x = x.view(-1, 4608) x = self.fc1(x) x = self.relu(x) x = self.fc2(x) return x # 定义训练函数 def train(gpu, args): rank = gpu dist.init_process_group(backend='nccl', init_method='env://', world_size=args.world_size, rank=rank) torch.cuda.set_device(gpu) train_loader = data.DataLoader(...) model = Net() model = nn.parallel.DistributedDataParallel(model, device_ids=[gpu]) criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(model.parameters(), lr=0.01) for epoch in range(args.epochs): epoch_loss = 0.0 for i, (inputs, labels) in enumerate(train_loader): inputs, labels = inputs.cuda(), labels.cuda() optimizer.zero_grad() outputs = model(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() epoch_loss += loss.item() print('GPU %d Loss: %.3f' % (gpu, epoch_loss)) # 主函数 if __name__ == '__main__': mp.set_start_method('spawn') args = parser.parse_args() args.world_size = args.num_gpus * args.nodes mp.spawn(train, args=(args,), nprocs=args.num_gpus, join=True)
首先,我们需要在主进程中使用torch.distributed.launch启动多个子进程。每个子进程被分配一个GPU,并调用train函数进行训练。
在train函数中,我们初始化进程组,并将模型以及优化器包装成DistributedDataParallel对象,然后像CPU上一样训练模型即可。在数据并行的过程中,模型和优化器都会被复制到每个GPU上,每个GPU只负责处理一部分的数据。所有GPU上的模型都参与误差累积和梯度更新。
模型并行
“模型并行”是另一种使用多卡训练的方法,它将同一个网络分成多段,不同段分布在不同的GPU上。每个GPU只运行其中的一段网络,并利用前后传播相互连接起来进行训练。代码结构如下:
import torch.nn as nn import torch.optim as optim import torch.multiprocessing as mp import torch.distributed as dist # 定义模型段 class SubNet(nn.Module): def __init__(self, in_features, out_features): super(SubNet, self).__init__() self.linear = nn.Linear(in_features, out_features) def forward(self, x): return self.linear(x) # 定义整个模型 class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.subnets = nn.ModuleList([ SubNet(1024, 512), SubNet(512, 256), SubNet(256, 100) ]) def forward(self, x): for subnet in self.subnets: x = subnet(x) return x # 定义训练函数 def train(subnet_id, args): dist.init_process_group(backend='nccl', init_method='env://', world_size=args.world_size, rank=subnet_id) torch.cuda.set_device(subnet_id) train_loader = data.DataLoader(...) model = Net().cuda() criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(model.parameters(), lr=0.01) for epoch in range(args.epochs): epoch_loss = 0.0 for i, (inputs, labels) in enumerate(train_loader): inputs, labels = inputs.cuda(), labels.cuda() optimizer.zero_grad() outputs = model(inputs) loss = criterion(outputs, labels) loss.backward(retain_graph=True) # 梯度保留,用于后续误差传播 optimizer.step() epoch_loss += loss.item() if subnet_id == 0: print('Epoch %d Loss: %.3f' % (epoch, epoch_loss)) # 主函数 if __name__ == '__main__': mp.set_start_method('spawn') args = parser.parse_args() args.world_size = args.num_gpus * args.subnets tasks = [] for i in range(args.subnets): tasks.append(mp.Process(target=train, args=(i, args))) for task in tasks: task.start() for task in tasks: task.join()
在模型并行中,网络被分成多个子网络,并且每个GPU运行一个子网络。在训练期间,每个子网络的输出会作为下一个子网络的输入。这需要在误差反向传播时,将不同GPU上计算出来的梯度加起来,并再次分发到各个GPU上。
在代码实现中,我们定义了三个子网(SubNet),每个子网有不同的输入输出规模。在train函数中,我们初始化进程组和模型,然后像CPU上一样进行多次迭代训练即可。在反向传播时,将梯度保留并设置retain_graph为True,用于后续误差传播。