【PyTorch教程】07-PyTorch如何使用多块GPU训练神经网络模型



1. 绪论

在本篇博文中,你将学习到在PyTorch中如何使用多GPU进行并行训练。在PyTorch中使用多GPU训练神经网络模型是非常简单的,PyTorch已经为我们封装好一个 nn.DataParallel 类来进行多GPU训练。


先来回顾一下在单GPU中如何训练,首先,我们可以把张量复制到GPU上:

import torch
import torch.nn as nn

device = torch.device("cuda:0")  # 创建单GPU对象
mytensor = my_tensor.to(device)  # 复制到该GPU上

我们还可以把神经网络模型放到GPU上:

model.to(device)

【注意】

原本在CPU的张量只是复制了一个副本过去GPU,其本身仍旧存储在CPU上。因此使用 torch.to('cuda') 函数时,要声明一个新的变量来接收GPU张量。


但是,PyTorch默认只会使用一张GPU。因此我们可以使用 nn.DataParallel 在多块GPU上并行地运行你的神经网络模型:

model = nn.DataParallel(model)

下面将是本篇教程的核心内容。我会用一个简单的数据集和神经网络模型来详细演示如何使用多GPU进行运算。


2. 导入Pytoch模块并声明参数


为了简单起见,我们声明一个输入尺寸为5,输出尺寸为2的张量。批量大小设置为30,数据集大小为100:

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

# 声明参数
input_size = 5
output_size = 2

batch_size = 30
data_size = 100

声明GPU:

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

3. 创建虚拟数据集


为了简单起见,创建一个简单的虚拟数据集,由随机值张量生成。

# 创建虚拟数据集类
class RandomDataset(Dataset):  # 继承torch.utils.data.Dataset抽象父类,要实现getitem抽象方法
    
    def __init__(self, size, length):  # RandomDataset类的构造器
        self.len = length
        self.data = torch.randn(length, size)  # 创建一个length×size大小的张量
        
    def __getitem__(self, index):  # 实现父类中的抽象方法
        return self.data[index]
    
    def __len__(self):
        return self.len

# 实例化数据集加载器
rand_loader = DataLoader(dataset=RandomDataset(input_size, data_size),
                         batch_size=batch_size, shuffle=True, num_workers=12)

4. 搭建一个简单的模型


简单起见,搭建一个只有一层全连接层的神经网络模型。为了方便大家观察到输入输出变化,我在模型中插入了 print 语句,在实际使用中不需要加。当然,你可以把本文的多GPU运算应用到任何复杂的神经网络模型中。

class Model(nn.Module):

    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

5. 多GPU并行计算


这部分内容是本文的核心内容。首先,我们实例化模型对象,然后检查我们的电脑是否有多块GPU。如果有多块GPU,就可以使用 nn.DataParallel 打包我们的模型,最后用 nn.DataParallelmodel.to(device) 把模型传送到多块GPU中进行运算。

model = Model(input_size, output_size)  # 实例化模型对象
if torch.cuda.device_count() > 1:  # 检查电脑是否有多块GPU
    print(f"Let's use {torch.cuda.device_count()} GPUs!")
    model = nn.DataParallel(model)  # 将模型对象转变为多GPU并行运算的模型

model.to(device)  # 把并行的模型移动到GPU上

6. 运行模型

现在,我们可以让模型跑起来,同时大家可以观察一下输入输出张量的形状变化:

for data in rand_loader:
    input = data.to(device)  # 把输入张量移到GPU
    output = model(input)
    print(f"Outside: input size {input.size()},"
          f"output_size {output.size()}")

博主服务器里有8张GPU,所以可以看到如下输出:

Let's use 8 GPUs!
	In Model: input size torch.Size([4, 5])	output size torch.Size([4, 2])
	In Model: input size torch.Size([4, 5])	output size torch.Size([4, 2])
	In Model: input size torch.Size([4, 5])	output size torch.Size([4, 2])
	In Model: input size torch.Size([4, 5])	output size torch.Size([4, 2])
	In Model: input size torch.Size([4, 5])	output size torch.Size([4, 2])
	In Model: input size torch.Size([4, 5])	output size torch.Size([4, 2])
	In Model: input size torch.Size([4, 5])	output size torch.Size([4, 2])
	In Model: input size torch.Size([2, 5])	output size torch.Size([2, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
	In Model: input size torch.Size([4, 5])	output size torch.Size([4, 2])
	In Model: input size torch.Size([4, 5])	output size torch.Size([4, 2])
	In Model: input size torch.Size([4, 5])	output size torch.Size([4, 2])
	In Model: input size torch.Size([4, 5])	output size torch.Size([4, 2])
	In Model: input size torch.Size([4, 5])	output size torch.Size([4, 2])
	In Model: input size torch.Size([4, 5])	output size torch.Size([4, 2])
	In Model: input size torch.Size([4, 5])	output size torch.Size([4, 2])
	In Model: input size torch.Size([2, 5])	output size torch.Size([2, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
	In Model: input size torch.Size([4, 5])	output size torch.Size([4, 2])
	In Model: input size torch.Size([4, 5])	output size torch.Size([4, 2])
	In Model: input size torch.Size([4, 5])	output size torch.Size([4, 2])
	In Model: input size torch.Size([4, 5])	output size torch.Size([4, 2])
	In Model: input size torch.Size([4, 5])	output size torch.Size([4, 2])
	In Model: input size torch.Size([4, 5])	output size torch.Size([4, 2])
	In Model: input size torch.Size([4, 5])	output size torch.Size([4, 2])
	In Model: input size torch.Size([2, 5])	output size torch.Size([2, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
	In Model: input size torch.Size([2, 5])	output size torch.Size([2, 2])
	In Model: input size torch.Size([2, 5])	output size torch.Size([2, 2])
	In Model: input size torch.Size([2, 5])	output size torch.Size([2, 2])
	In Model: input size torch.Size([2, 5])	output size torch.Size([2, 2])
	In Model: input size torch.Size([2, 5])	output size torch.Size([2, 2])
Outside: input size torch.Size([10, 5]) output_size torch.Size([10, 2])

可以看到,我们一共有100个数据,每个批量30个。PyTorch会自动帮我们尽可能地把每批次数据平均分到8张卡上。因此在前3个批次中,前7张卡都分到了4个数据,而最后一张卡分剩下的2个数据,一共是
7 × 4 + 2 = 30 7\times 4 + 2=30 7×4+2=30
个数据。刚好等于 batch_size = 30


而前3个批次已经计算完 30 × 3 = 90 30\times3=90 30×3=90 个数据了,自然剩下的 100 − 90 = 10 100-90=10 10090=10 个数据就分到了5张卡上,每张2个数据。100个数据分4个批次就运算完毕了,每个批次8张GPU都是并行地运算。



至此,恭喜你已经掌握了如何使用PyTorch进行多GPU运算了。

  • 25
    点赞
  • 97
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论
以下是使用 PyTorch 进行多 GPU 训练的示例代码: ```python import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader from torchvision.datasets import CIFAR10 from torchvision.transforms import transforms # 定义模型 class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.conv1 = nn.Conv2d(3, 6, 5) self.pool = nn.MaxPool2d(2, 2) self.conv2 = nn.Conv2d(6, 16, 5) self.fc1 = nn.Linear(16 * 5 * 5, 120) self.fc2 = nn.Linear(120, 84) self.fc3 = nn.Linear(84, 10) def forward(self, x): x = self.pool(torch.relu(self.conv1(x))) x = self.pool(torch.relu(self.conv2(x))) x = x.view(-1, 16 * 5 * 5) x = torch.relu(self.fc1(x)) x = torch.relu(self.fc2(x)) x = self.fc3(x) return x # 定义训练函数 def train(model, device, train_loader, optimizer, criterion): model.train() for batch_idx, (data, target) in enumerate(train_loader): data, target = data.to(device), target.to(device) optimizer.zero_grad() output = model(data) loss = criterion(output, target) loss.backward() optimizer.step() # 定义测试函数 def test(model, device, test_loader, criterion): model.eval() test_loss = 0 correct = 0 with torch.no_grad(): for data, target in test_loader: data, target = data.to(device), target.to(device) output = model(data) test_loss += criterion(output, target).item() pred = output.argmax(dim=1, keepdim=True) correct += pred.eq(target.view_as(pred)).sum().item() test_loss /= len(test_loader.dataset) accuracy = 100. * correct / len(test_loader.dataset) print('Test set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)'.format( test_loss, correct, len(test_loader.dataset), accuracy)) # 定义主函数 def main(): # 设置超参数 batch_size = 128 epochs = 10 lr = 0.01 momentum = 0.9 num_workers = 4 num_gpus = torch.cuda.device_count() # 加载数据集 transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) ]) train_dataset = CIFAR10(root='./data', train=True, download=True, transform=transform) test_dataset = CIFAR10(root='./data', train=False, download=True, transform=transform) train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=num_workers) test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False, num_workers=num_workers) # 初始化模型和优化器 model = Net() if num_gpus > 1: model = nn.DataParallel(model) device = torch.device("cuda" if torch.cuda.is_available() else "cpu") model.to(device) optimizer = optim.SGD(model.parameters(), lr=lr, momentum=momentum) criterion = nn.CrossEntropyLoss() # 训练和测试 for epoch in range(1, epochs + 1): train(model, device, train_loader, optimizer, criterion) test(model, device, test_loader, criterion) if __name__ == '__main__': main() ``` 这个示例代码可以在多个 GPU 上并行训练模型,如果只有一个 GPU,可以将 `num_gpus` 设置为 1。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

自牧君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值