Pytorch:多块GPU调用细节问题及Pytorch的nn.DataParallel解释

我们用实验室带有多块卡的GPU服务器,当我们在上面跑程序的时候,当迭代次数或者epoch足够大的时候,我们通常会使用nn.DataParallel函数加入以下代码段来用多个GPU来加速训练。

device_ids = [0, 1]
net = torch.nn.DataParallel(net, device_ids=device_ids)

然鹅,也常常会由于正在运行程序的0卡显存不够放不下我们新来的模型初始化数据而导致显存OOM的问题不断,使得模型无法训练,同时报出警告:

UserWarning: Was asked to gather along dimension 0, but all input tensors were scalars;will instead unsqueeze and return a vector.

下面就GPU存放数据进行整理说明,我们在ternimal下执行语句下列语句后,

watch -n 1 nvidia-smi

我们会发现确实会使用多个GPU来并行训练。但是同时我们会发现其实第一块卡的显存会占用的更多一些。那是因为官方有定义,详细可以参看DataParallel的函数定义。

CLASS torch.nn.DataParallel(module, device_ids=None, output_device=None, dim=0)

在前向过程中,我们的输入数据会被划分成多个子部分(可称为副本)送到不同的device中进行计算,而你的模型module是在每个device上进行复制一份,也就是说,输入的batch是会被平均分到每个device中去,但是你的模型module是要拷贝到每个devide中去的,每个模型module只需要处理每个副本即可,当然你要保证你的batch size大于你的gpu个数。然后在反向传播过程中,每个副本的梯度被累加到原始模块中。概括来说就是:DataParallel 会自动帮我们将数据切分 load 到相应 GPU,将模型复制到相应 GPU,进行正向传播计算梯度并汇总。

注意还有一句话,官网中是这样描述的:

The parallelized module must have its parameters and buffers on device_ids[0] before running this DataParallel module.

意思就是:在运行此DataParallel模块之前,并行化模块必须在device_ids [0]上具有其参数和缓冲区。在执行DataParallel之前,会首先把其模型的参数放在device_ids[0]上,一看好像也没有什么毛病,其实有个小坑。我举个例子,服务器是八卡的服务器,刚好前面序号是0的卡被别人占用着,于是你只能用其他的卡来,比如你用2和3号卡,如果你直接指定device_ids=[2, 3]的话会出现模型初始化错误,类似于module没有复制到在device_ids[0]上去。此时需要在运行train之前需要添加如下两句话指定程序可见的devices,如下:

os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID"
os.environ["CUDA_VISIBLE_DEVICES"] = "2, 3"

当添加这两行代码后,那么device_ids[0]默认的就是第2号卡,你的模型也会初始化在第2号卡上了,而不会占用第0号卡了。这里简单说一下设置上面两行代码后,那么对这个程序而言可见的只有2和3号卡,和其他的卡没有关系,这是物理上的号卡,逻辑上来说其实是对应0和1号卡,即device_ids[0]对应的就是第2号卡,device_ids[1]对应的就是第3号卡。

上面这两行代码需要定义在

device_ids = [0, 1]
net = torch.nn.DataParallel(net, device_ids=device_ids)

这两行代码之前,一般放在train.py中import一些package之后。

那么在训练过程中,优化器同样可以使用nn.DataParallel,如下两行代码:

optimizer = torch.optim.SGD(net.parameters(), lr=lr)
optimizer = nn.DataParallel(optimizer, device_ids=device_ids)

那么使用nn.DataParallel后,事实上DataParallel也是一个Pytorch的nn.Module,那么模型和优化器都需要使用.module来得到实际的模型和优化器,如下:

保存模型:
torch.save(net.module.state_dict(), path)
加载模型:
net=nn.DataParallel(Resnet18())
net.load_state_dict(torch.load(path))
net=net.module
优化器使用:
optimizer.step() --> optimizer.module.step()

还有一个问题就是,如果直接使用nn.DataParallel的时候,训练采用多卡训练,会出现一个warning:

UserWarning: Was asked to gather along dimension 0, but all input tensors were scalars;will instead unsqueeze and return a vector.

说明一下:每张卡上的loss都是要汇总到第0张卡上求梯度,更新好以后把权重分发到其余卡。但是为什么会出现这个warning,这其实和nn.DataParallel中最后一个参数dim有关,其表示tensors被分散的维度,默认是0,nn.DataParallel将在dim0(批处理维度)中对数据进行分块,并将每个分块发送到相应的设备。单卡的没有这个warning,多卡的时候采用nn.DataParallel训练会出现这个warning,由于计算loss的时候是分别在多卡计算的,那么返回的也就是多个loss,你使用了多少个gpu,就会返回多少个loss。有人建议DataParallel类应该有reduce和size_average参数,比如用于聚合输出的不同loss函数,最终返回一个向量,有多少个gpu,返回的向量就有几维。

关于这个问题在pytorch官网上的issues上讨论过:DataParallel does not work with tensors of dimension 0 

 

 

  • 12
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: `nn.DataParallel(model)` 是一个 PyTorch 中用于数据并行的工具,可以在多个 GPU 上并行地运行神经网络模型。具体来说,`nn.DataParallel` 将模型复制到多个 GPU 上,将输入数据拆分成若干个小批次,并将每个小批次分配到不同的 GPU 上进行处理。在每个 GPU 上计算完成后,`nn.DataParallel` 会将计算结果收集并合并,最终得到整个批次的计算结果。这种方法能够提高模型的训练速度和处理能力。 例如,假设你有两个 GPU,要对一个神经网络模型 `model` 进行训练,可以使用以下代码实现数据并行: ``` import torch.nn as nn import torch.optim as optim model = MyModel() model = nn.DataParallel(model) # 使用 DataParallel 进行数据并行 optimizer = optim.SGD(model.parameters(), lr=0.001) criterion = nn.CrossEntropyLoss() # 训练代码... ``` 在这个例子中,`MyModel` 是自己定义的神经网络模型。使用 `nn.DataParallel` 将 `MyModel` 复制到两个 GPU 上,接下来可以像单 GPU 训练一样使用 `optimizer` 和 `criterion` 对模型进行训练。`nn.DataParallel` 会自动将训练数据拆分成多个小批次,并将每个小批次分配到不同的 GPU 上进行计算,最后将结果合并返回。 ### 回答2: nn.DataParallel(model)是PyTorch中的一个函数,用于并行地在多个GPU上运行模型。 在深度学习任务中,通常需要处理大规模的数据,同时训练复杂的神经网络模型。为了提高训练速度和性能,可以使用多个GPU并行地处理数据和模型。然而,直接在多个GPU上运行模型可能比较困难,需要手动编写额外的代码。 而nn.DataParallel(model)函数的目的就是简化这个过程,它接受一个模型作为参数,然后自动将模型复制到所有可用的GPU上,并在每个GPU上独立地运行模型的副本。每个GPU上的模型副本独立计算出梯度,并将梯度汇总后更新模型的参数。 在使用nn.DataParallel(model)函数之后,我们可以像通常对待单个GPU模型一样来使用它。例如,我们可以将数据传入模型,调用model.forward()进行前向传播,然后使用loss.backward()计算梯度并进行优化。 需要注意的是,在调用nn.DataParallel(model)函数之前,需要确保已经将模型移动到可用的GPU上,可以使用model.to(device)这样的操作实现。另外,nn.DataParallel(model)通常在定义好模型的结构之后,在模型训练之前调用。 总之,nn.DataParallel(model)函数简化了在多个GPU上并行运行模型的过程,提高了深度学习任务的训练速度和性能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值