RuntimeError Expected tensor for argument #1 'input' to have the same device as tensor for argument
RuntimeError: The tensors for arguments #1 'input' and #2 'weight' must be consistent in terms of their devices; however, they differ specifically between device 1 and 0 when verifying the parameters for the custom convolution operation.
记录该处出现的bug,并将详细信息保存下来。最初认为问题出在gpu并行设备配置上,但经过进一步排查发现原因在于模型内部函数调用流程出了问题。
主要整理内容源自pytorch的issue
以两个模块为例,在采用DataParallel的情况下会出现错误;而testModule2则不会出现错误。
import torch
from torch import nn
class testModule(nn.Module):
def __init__(self):
super(testModule, self).__init__()
self.g = nn.Conv2d(in_channels=1, out_channels=1,
kernel_size=1, stride=1, padding=0)
self.operation_function = self._realOperation
def forward(self, x):
output = self.operation_function(x)
return output
def _realOperation(self, x):
x = self.g(x)
return x
class testModule2(nn.Module):
def __init__(self):
super(testModule2, self).__init__()
self.g = nn.Conv2d(in_channels=1, out_channels=1,
kernel_size=1, stride=1, padding=0)
def forward(self, x):
x = self.g(x)
return x
if __name__ == '__main__':
input = torch.rand(4, 1, 1, 1).cuda()
net = testModule()
net2 = testModule2()
gpu_num = torch.cuda.device_count()
print('GPU NUM: {:2d}'.format(gpu_num))
if gpu_num > 1:
net = torch.nn.DataParallel(net, list(range(gpu_num))).cuda()
net2 = torch.nn.DataParallel(net2, list(range(gpu_num))).cuda()
out2 = net2(input)
print(out2.size())
out = net(input)
print(out.size())
self.operation_function = self._realOperation
原因不言而喻,在上述论述中涉及到了一种属性绑定对应的方式。当我们把模块分配到不同的GPU时,该属性(非张量)仅进行复制。这表明该模块的所有广播副本均引用了相同的绑定方法属性,并且该方法被绑定到同一个实例。因而采用相同的self.g后,在GPU 0上即可拥有所有参数。这会导致在GPU 1上出现错误。
在testModule2中,在每个广播副本之前执行操作时,程序会通过动态查找确定self.g为该副本的g属性,并将其参数传递到对应的GPU上处理。
解决方法:
涉及方法绑定的地方被放置到forward中执行 即被放置到forward中的操作包括自定义的操作如self._realOperation
除此之外,还可以将self.operation_function表示为该类的另一种方法(尝试了一下似乎不适用),并发现这一做法可能存在一定的问题(不确定是否存在问题)。
