解决了PyTorch 使用torch.nn.DataParallel 进行多GPU训练的一个BUG:模型(参数)和数据不在相同设备上

解决了PyTorch 使用torch.nn.DataParallel 进行多GPU训练的一个BUG:模型(参数)和数据不在相同设备上

博客地址:https://senyang-ml.github.io/2019/07/20/pytorch-multigpu/

解决了PyTorch 使用torch.nn.DataParallel 进行多GPU训练的一个BUG:

模型(参数)和数据不在相同设备上

使用torch.nn.DataParallel进行多GPU训练时出现了一个BUG, 困扰许久:

RuntimeError: Expected tensor for argument #1 'input' to have the same device as tensor for argument #2 'weight'; but device 1 does not equal 0 (while checking arguments for cudnn_convolution)

这个错误表明, input数据在device 1 上, 而模型的参数在device 0 上 (暗示数据是被拆分到了各个GPU上,但是BUG出现位置的此处参数可能没有成功复制到其他GPU上, 或者说, 还是调用了复制前的那个参数地址)

之前调试了好久, 也没有解决掉, 在Github上有一个issue和我的问题很像:
https://github.com/pytorch/pytorch/issues/8637
但是我还是没有找到自己的问题在哪里.

今天, 我又准备再此挑战这个难题

经过6个小时的print调试法以及后面关键的VScode的Debug功能, 我大功告成,找到了问题所在!!!

在我的A(nn.Module)类的forward 前向计算函数里面, 有一处调用了一个该类的列表self.cell_fabrics属性, 其中,列表中的元素是直接通过

self.cell_fabrics = [self.cell_1,...,self.cell_n]

这个语句来赋值的, 其中每个self.cell也是nn.Module类。

当我们使用torch.nn.DataParallel复制了A(nn.Module)类的模型放到设备cuda:1上后(注意:cuda:0上始终保存着原始的模型),并且当多GPU并行处理的程序,运行到下面代码段时:

def forward(self,x)
    for layer in self.cell_fabrics:
        for cell in layer:
           y = cell (x,)

debug输出显示:x是在设备cuda:1 上面, 而 cell 中的参数明显都在 cuda:0上。

也就是说:

此时,前传调用的self.cell_fabrics 列表中的各个元素 (self.cell)的地址,还是指向在没有进行torch.nn.DataParallel之前的nn.module 的那些self.cell, 而nn.DataParallel类的nn.module的参数都默认存放在device(type='cuda',index=0)上 .

torch.nn.Module的广播机制和列表赋值方式

torch.nn.DataParallel(model,device_ids=[range(len(gpus))])的机制是, 将属于nn.module类的model以及其广播的所有nn.module子类的上的所有参数,复制成len(gpus)份,送到各个GPU上. 这种传播必须通过其子类通过注册(register)成为nn.module的属性才得以完成, 其属性为列表中的元素是不会被复制的, 所以其属性中的元素还是存放在默认设备device 0

所以 在使用torch.nn.DataParallel进行多GPU训练的时候, 请注意:所有属于模型参数的模块以及其子模块必须以nn.Module的类型注册为模型的属性, 如果需要一个列表来批量存放子模块或者参数时, 请采用nn.ModuleList或者nn.ModuleDict这样的继承了nn.Module的类来进行定义, 并且在forward(self,)前向传播的过程中,需要直接调用属于 nn.Module,nn.ModuleList或者nn.ModuleDict 这样的属性。

那么torch.nn.DataParallel将会正常地将模型参数准确复制到多个GPU上, 并默认根据数据的batchsize的大小平分成GPU的数量分别送到相应的GPU设备上,

运用多线程的方式, 同时对这些数据进行加工处理, 然后收集各个GPU上最终产生对模型的参数的梯度, 汇总到一起更新原模型的参数!

参考:

  1. https://github.com/pytorch/pytorch/issues/8637
  2. https://pytorch.org/docs/stable/nn.html#dataparallel-layers-multi-gpu-distributed
  • 42
    点赞
  • 66
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
使用 `nn.DataParallel` 模块进行GPU 训练的方法主要包括以下几个步骤: 1. 定义模型并将其放置到 GPU 上 ```python import torch.nn as nn import torch device = torch.device("cuda" if torch.cuda.is_available() else "cpu") model = MyModel() model.to(device) ``` 2. 使用 `nn.DataParallel` 包装模型 ```python model = nn.DataParallel(model) ``` 3. 定义损失函数和优化器 ```python criterion = nn.CrossEntropyLoss() optimizer = torch.optim.Adam(model.parameters(), lr=0.001) ``` 4. 加载数据并将其分配到不同的 GPU 上 ```python train_dataset = MyDataset() train_sampler = torch.utils.data.distributed.DistributedSampler(train_dataset) train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=32, sampler=train_sampler) val_dataset = MyDataset() val_sampler = torch.utils.data.distributed.DistributedSampler(val_dataset) val_loader = torch.utils.data.DataLoader(val_dataset, batch_size=32, sampler=val_sampler) ``` 5. 训练模型 ```python for epoch in range(num_epochs): running_loss = 0.0 for i, data in enumerate(train_loader, 0): inputs, labels = data[0].to(device), data[1].to(device) optimizer.zero_grad() outputs = model(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() running_loss += loss.item() # 在每个 epoch 结束时输出训练损失 print("Epoch {} loss: {:.4f}".format(epoch+1, running_loss / len(train_loader))) ``` 在训练过程中,`nn.DataParallel` 会自动将模型复制到每个可用的 GPU 上,并在这些 GPU进行前向计算和反向传播,并将梯度聚合到主 GPU 上,更新模型参数。需要注意的是,在训练过程中,需要将数据分配到不同的 GPU 上,可以使用 PyTorch 的 `DistributedSampler` 进行数据分配。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值