项目场景:
在使用DataParallel
进行多GPU分布式训练时显示数据不在同一GPU上
问题描述
在搭建模型的过程中同时使用了DataParallel
,将模型副本同步到多个GPU上,训练数据已用.cuda(non_blocking=True)
处理,但是还是报错
RuntimeError: Expected all tensors to be on the same device, but found at least two devices, cuda:1 and cuda:0!
(when checking argument for argument weight in method wrapper__cudnn_convolution)
原因分析:
参考链接: https://github.com/pytorch/pytorch/issues/3174
参考链接: https://zhuanlan.zhihu.com/p/560322701
定位错误后发现Linear的weight和bias始终在 cuda:0
上面,数据并行在cuda:0
和 cuda:1
上面,说明Linear没有被Dataparallel()函数拷贝。
查找原因后发现Dataparallel()会产生多个模型副本在不同的 GPU 上进行训练。但是它的__dict__
s 只是浅拷贝。因此,它们的sub_i
模块在不同的 GPU 上会有所不同,但self.subs
会相同,并且始终指向 GPU0 上带有模块的列表。
因此,本文所使用的python的list定义的模块,即使使用add_module()手动注册了模块,但是仍旧不会被Dataparallel()所识别,定义的模块一直在GPU0上。
解决方案:
使用nn.ModuleList()或nn.Sequential()代替python的list。
补充:
1.链接: add_module函数的作用
2.链接: Pytorch中nn.ModuleList和nn.Sequential的用法和区别
3.如果使用python的list定义模块不进行add_module注册,如下代码所示,modlist中的卷积层和它们的 parameters 没有注册到网络中。
虽然可以使用 forward 来计算模型输出。但在训练时因为parameters不在网络中,所以其网络参数也不会被更新,也就是无法训练。
class net_modlist(nn.Module):
def __init__(self):
super(net_modlist, self).__init__()
self.modlist = [
nn.Conv2d(1, 20, 5),
nn.ReLU(),
nn.Conv2d(20, 64, 5),
nn.ReLU()
]
def forward(self, x):
for m in self.modlist:
x = m(x)
return x
net_modlist = net_modlist()
print(net_modlist)
#net_modlist()
for param in net_modlist.parameters():
print(type(param.data), param.size())
#None