Dataloader的num_work污染模型参数

模型的默认参数会dataloder被刷成0

下面的代码看起来只是在获取dataloder的迭代对象,和model毫无关系,但是实际上model里面的weight会被刷掉变成0

arr = []
model = Model()
set = dataset()
dataloder = DataLoader(set, batch_size=1, shuffle=False, num_workers=3)
arr.append(dataloder)
arr.append(model)
x = arr[0].__iter__()
print(model.weight)

首先我们先从最简单的情况看起

tensor = torch.tensor(5).cuda()
storage = tensor.storage()
storage._share_cuda_()

上面的代码定义了一个tensor变量,然后调用_share_cuda_方法,这个方法有什么用我也不知道,看起来要共享内存。但是执行完之后tensor会变成0在这里插入图片描述
上面的情况看起来没什么,毕竟大家也用不到这个代码,但是接下来可就涉及到大家常用的操作了

dataset内的参数被刷

我们先简单定义一个dataset,里面有一个tensor变量。然后我们获取dataloder的迭代对象,这个过程看起来不修改dataset里面的tensor,但是它会被刷成0。由于代码简单,退出的快引发了报错,在复杂的模型里是没有这个错误的,他只会偷偷改变你的参数,不仔细看还以为你设计的模型不好。

class dataset(Dataset):
    def __init__(self):
        super().__init__()
        self.tensor = torch.tensor(5).cuda()

    def __getitem__(self, index):
        time.sleep(1000)
        return 0

    def __len__(self):
        return 1000

my_set = dataset()
dataloder = DataLoader(my_set, batch_size=10, shuffle=False, num_workers=1)
x = dataloder.__iter__()
print(my_set.tensor)   

在这里插入图片描述
关于这个bug,我们需要一步一步找到里面出错的地方,我们先进入__iter__里面,然后再进入里面的_get_iterator,再进入_MultiProcessingDataLoaderIter。这里面定义了一个多进程对象w = multiprocessing_context.Process,他有一个方法 w.start(),进去之后经过一层又一层的代码会有一个reduction.dump(process_obj, to_child),这里面其实重载了pickle,搞了个新的dump。如果你的dataset里面有在cuda的tensor那么就会调用reduce_tensor()方法,下面是他的部分代码,这里面就有了刚才提到的storage.share_cuda(), 这个代码非常邪恶,会把你的tensor刷成0,因此执行完之后你的dataset里面的tensor会被刷成0。试想一下一个模型初始参数为0,那输出可想而知。这还不算最坑的,他还有更隐蔽的污染方式。

def reduce_tensor(tensor):
    storage = tensor.storage()

    if tensor.requires_grad and not tensor.is_leaf:
        raise RuntimeError("Cowardly refusing to serialize non-leaf tensor which requires_grad, "
                           "since autograd does not support crossing process boundaries.  "
                           "If you just want to transfer the data, call detach() on the tensor "
                           "before serializing (e.g., putting it on the queue).")

    check_serializing_named_tensor(tensor)
    torch.utils.hooks.warn_if_has_hooks(tensor)
    if storage.is_cuda:
        (device,
         handle,
         storage_size_bytes,
         storage_offset_bytes,
         ref_counter_handle,
         ref_counter_offset,
         event_handle,
         event_sync_required) = storage._share_cuda_()

顺着list刷掉模型参数

这里面定义了一个模型和一个datasset,他们之间没有任何关联,除了在同一个list里面,但即便如此,model里面的weight也会被刷成0,单纯看代码完全想不到 x = arr[0].iter()会把model里面的weight刷掉。尤其是复制模型里面代码层次封装,很难发现这种隐蔽的污染方式。实际上除了list类对象,字典什么的都可以传导这种污染。他不但污染自己的孩子对象,甚至兄弟,父母,舅舅外甥之类的远亲都能污染。

class dataset(Dataset):
    def __init__(self):
        super().__init__()
        self.tensor = torch.tensor(5).cuda()

    def __getitem__(self, index):
        time.sleep(1000)
        return 0

    def __len__(self):
        return 1000


class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.weight = torch.tensor(5).cuda()

arr = []
model = Model()
set = dataset()
dataloder = DataLoader(set, batch_size=1, shuffle=False, num_workers=3)
arr.append(dataloder)
arr.append(model)

x = arr[0].__iter__()
print(model.weight)

在这里插入图片描述

最后提一下解决方法,最简单的就是numwork设置成0,那么他就不会启动多进程,创建w = multiprocessing_context.Process,也就不会有后面的一系列bug。
第二个就是dataset里面不要有张量,或者有张量也不要在cuda,这样 if storage.is_cuda:也会被跳过,就不会有后面的bug了。
第三个就是dataloder最好和其他东西隔离开,隔离之后就不会顺着列表或者其他关系把模型刷了

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值