程序问答   发布时间:2022-06-01  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了PyTorch DataLoader 在每个 epoch 中使用相同的随机变换 附加说明大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

如何解决PyTorch DataLoader 在每个 epoch 中使用相同的随机变换 附加说明?

开发过程中遇到PyTorch DataLoader 在每个 epoch 中使用相同的随机变换 附加说明的问题如何解决?下面主要结合日常开发的经验,给出你关于PyTorch DataLoader 在每个 epoch 中使用相同的随机变换 附加说明的解决方法建议,希望对你解决PyTorch DataLoader 在每个 epoch 中使用相同的随机变换 附加说明有所启发或帮助;

在 PyTorch/Numpy 中有一个 bug,当与 DataLoader 并行加载批次时(即设置 num_workers > 1),每个工人使用相同的 NumPy 随机种子,结果在并行化批次中应用的任何随机函数中都是相同的。这可以通过将种子生成器传递给 worker_init_fn 参数 like so 来解决。

但是这个问题在多个时代中仍然存在。

最小示例:

import numpy as np
from torch.utils.data import Dataset,DataLoader

class RandomDataset(Dataset):
    def __getitem__(self,indeX):
        return np.random.randint(0,1000,2)

    def __len__(self):
        return 4

dataset = RandomDataset()
DataLoader = DataLoader(dataset,batch_size=1,num_workers=2,worker_init_fn = lambda x: np.random.seed(X))

for epoch in range(3):
    print(f'\nEpoch {epoch}')
    for batch in DataLoader:
        print(batch)

如您所见,然一个时期内的并行化批次现在会产生不同的结果,但结果是相同的跨时期

Epoch 0
tensor([[684,559]])
tensor([[ 37,235]])
tensor([[629,192]])
tensor([[908,72]])

Epoch 1
tensor([[684,72]])

Epoch 2
tensor([[684,72]])

如何修复这种行为?


使用空参数,例如worker_init_fn = lambda _: np.random.seed() 似乎解决了这个问题 - 这种变通方法有什么问题吗?

解决方法

如您链接的博文所述,您所写的内容将为每个时代的每个工人生成相同的随机数:

在数据集上迭代 3 次会在每个 epoch 产生相同的随机数。发生这种情况是因为对随机状态的所有更改对于每个工人都是本地的。默认情况下,工作进程在每个 epoch 结束时被杀死,并且所有工作资源都将丢失。同时,主进程中的随机状态没有改变,用于再次初始化各个工作进程。

给出解决方案:

因此您需要在每个 epoch 更改 NumPy 的种子,例如通过 np.random.seed(initial_seed + epoch)。

但我个人更喜欢只使用 Torch random 而不是 Numpy 的,以避免出现问题,因为 Torch 默认处理并行代码中的随机性。


附加说明

根据博文:

PyTorch 通过自动将 [...] 种子设置为 seed + worker_id 来处理这些问题。

这意味着在您的数据集类或训练循环中使用 Pytorch random 函数不应跨批次或时期复制随机性。例如,您编写的最小示例可以像这样修复:

import torch
from torch.utils.data import Dataset,DataLoader

class RandomDataset(Dataset):
    def __getitem__(self,indeX):
        return torch.randint(0,1000,(2,))

    def __len__(self):
        return 4

dataset = RandomDataset()
dataloader = DataLoader(dataset,batch_size=1,num_workers=2)

for epoch in range(3):
    print(f'\nEpoch {epoch}')
    for batch in dataloader:
        print(batch)

大多数时候 Numpy 可以用 Torch random(或 Python random)代替。这是另一个用于图像分割的随机变换示例:

class RandomHorizontalFlip:

    def __init__(self,prob=0.5):
        self.prob = prob

    def __call__(self,input,target):
        if torch.randn(1).item() < self.prob:
            return F.hflip(input),F.hflip(target)
        else:
            return input,target
,

我能想到的最好的方法是使用pytorch为numpy和random设置的seed:

import random
import numpy as np
import torch
from torch.utils.data import Dataset,DataLoader

def worker_init_fn(worker_id):
    torch_seed = torch.initial_seed()
    random.seed(torch_seed + worker_id)
    if torch_seed >= 2**30:  # make sure torch_seed + workder_id < 2**32
        torch_seed = torch_seed % 2**30
    np.random.seed(torch_seed + worker_id)

class RandomDataset(Dataset):
    def __getitem__(self,indeX):
        return np.random.randint(0,2)

    def __len__(self):
        return 4

dataset = RandomDataset()
dataloader = DataLoader(dataset,num_workers=2,worker_init_fn = worker_init_fn)

for epoch in range(3):
    print(f'\nEpoch {epoch}')
    for batch in dataloader:
        print(batch)

输出:

Epoch 0
tensor([[593,191]])
tensor([[207,469]])
tensor([[976,714]])
tensor([[ 13,119]])

Epoch 1
tensor([[836,664]])
tensor([[138,836]])
tensor([[409,313]])
tensor([[  2,221]])

Epoch 2
tensor([[269,888]])
tensor([[315,619]])
tensor([[892,774]])
tensor([[ 70,771]])

或者,您可以使用 int(time.time()) 来播种 numpyrandom,假设每个 epoch 的运行时间超过 1 秒。

大佬总结

以上是大佬教程为你收集整理的PyTorch DataLoader 在每个 epoch 中使用相同的随机变换 附加说明全部内容,希望文章能够帮你解决PyTorch DataLoader 在每个 epoch 中使用相同的随机变换 附加说明所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。