最靠谱的pytorch训练可复现性设置方案

原文地址:最靠谱的pytorch训练可复现性设置方案

作者:LeoLi6 (转载引用请注明出处)


保证训练结果的一致和可复现,在算法迭代训练过程中非常重要,否则由于随机性非常容易导致判断错误和干扰算法优化方向。

本文适用于单机多卡的情况。

如果有帮助到你,欢迎点赞收藏加关注!

目录

一、Pytorch seed setting method

1、随机种子

2、训练使用不确定的算法

3、数据加载DataLoader

4、其他特殊情况:lstm dropout

5、Pytorch可复现性完整设置

二、Pytorch-lightning seed setting method


一、Pytorch seed setting method

版本信息:

torch: 1.8.2/1.11.0


影响可复现的因素主要有这几个:

1、随机种子

固定的随机种子是保证可复现性最常用的手段,其中包括random、numpy、以及PyTorch自身的随机种子等,如基本种子、cuda种子、多gpu种子等,此外还需要固定环境变量中的PYTHONHASHSEED。

# seed init.
random.seed(seed)
np.random.seed(seed)
os.environ['PYTHONHASHSEED'] = str(seed)

# torch seed init.
torch.manual_seed(seed)
torch.cuda.manual_seed(seed)
torch.cuda.manual_seed_all(seed)

2、训练使用不确定的算法

使用的不确定算法算法主要包括两部分:

2.1 CUDA卷积优化——CUDA convolution benchmarking

torch.backends.cudnn.benchmark = False

当torch.backends.cudnn.benchmark选项为True时候,cuda为了提升训练效率,会自动试运行不同优化的卷积算法,以搜索最优最快的算法实现,由于不同硬件不同以及不同的版本的卷积算法实现,可能会导致训练结果不一致。所以,为了算法可复现,通常设置cudnn.benchmark = False。

那什么情况可以设置True:

不考虑可复现性,当模型的输入和结构在训练过程保持固定不变化的时候,可以实现算法加速。

否则,会因为反复的算法最优搜索导致额外的时间浪费。

2.2 Pytorch使用不确定算法——Avoiding nondeterministic algorithms

虽然禁用CUDA卷积基准测优化可以确保CUDA每次运行应用程序时选择相同的卷积算法,但其他算法本身可能是不确定的,如gather等操作,所以需要设置成固定的:

torch.backends.cudnn.deterministic = True

# https://pytorch.org/docs/stable/generated/torch.use_deterministic_algorithms.html
os.environ['CUBLAS_WORKSPACE_CONFIG'] = ':16:8'

# avoiding nondeterministic algorithms (see https://pytorch.org/docs/stable/notes/randomness.html)
torch.use_deterministic_algorithms(True)

torch.use_deterministic_algorithms(True)允许你配置PyTorch在可用的情况下使用确定性算法而不是非确定性算法,并且如果已知某个操作是不确定的(并且没有确定的替代方法),则抛出RuntimeError错误。

请查看torch.use_deterministic_algorithms()的文档,以获取受影响操作的完整列表。如果操作没有按照文档正确运行,或者如果您需要确定实现没有文档的操作,也可提交问题:Issues · pytorch/pytorch

例如,运行torch.Tensor.index_add_()的不确定CUDA实现将抛出错误:

>>> import torch
>>> torch.use_deterministic_algorithms(True)
>>> torch.randn(2, 2).cuda().index_add_(0, torch.tensor([0, 1]), torch.randn(2, 2))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: index_add_cuda_ does not have a deterministic implementation, but you set
'torch.use_deterministic_algorithms(True)'....

解决方案:

自己定义一个确定性的实现,替换调用的接口。对于torch.index_select 这个接口,可以有如下的实现。

def deterministic_index_select(input_tensor, dim, indices):
    """
    input_tensor: Tensor
    dim: dim 
    indices: 1D tensor
    """
    tensor_transpose = torch.transpose(x, 0, dim)
    return tensor_transpose[indices].transpose(dim, 0)

此外还有一种解决办法,可以尝试升级torch版本到更高版本如2.0.1。

3、数据加载DataLoader

在多进程数据加载算法中,DataLoader将根据Randomness in multi-process data loading algorithm对worker进行重新初始化种子。使用worker_init_fn()来保持可再现性:

def seed_worker(worker_id):
    worker_seed = torch.initial_seed() % 2**32
    numpy.random.seed(worker_seed)
    random.seed(worker_seed)

DataLoader(
    xxx,
    batch_size=batch_size,
    num_workers=num_workers,
    worker_init_fn=seed_worker,
)

4、其他特殊情况:lstm dropout

在某些版本的CUDA中,RNN和LSTM网络可能具有不确定性行为,如lstm中的dropout,需要注意这一特性。可以参见torch.nn.RNN()torch.nn.LSTM()了解详细信息和解决方案。

5、Pytorch可复现性完整设置

最终,一个完整的可复现性设置如下:

def set_seed(seed):
    # seed init.
    random.seed(seed)
    np.random.seed(seed)
    os.environ['PYTHONHASHSEED'] = str(seed)

    # torch seed init.
    torch.manual_seed(seed)
    torch.cuda.manual_seed(seed)
    torch.cuda.manual_seed_all(seed)

    torch.backends.cudnn.deterministic = True
    torch.backends.cudnn.benchmark = False
    torch.backends.cudnn.enabled = False # train speed is slower after enabling this opts.

    # https://pytorch.org/docs/stable/generated/torch.use_deterministic_algorithms.html
    os.environ['CUBLAS_WORKSPACE_CONFIG'] = ':16:8'

    # avoiding nondeterministic algorithms (see https://pytorch.org/docs/stable/notes/randomness.html)
    torch.use_deterministic_algorithms(True)

set_seed(11)

值得注意的是,

  • 如果没有使能torch.backends.cudnn.enabled = False 这一行,我这边测试发现是无法保证训练结果可复现性。但添加这行后会导致训练速度很慢。很多博客没有讲到或者解决这一问题。
  • 此外,如果CUDA版本是10.2或更高版本,少数CUDA操作是不确定的,除非设置了环境变量CUBLAS_WORKSPACE_CONFIG=:4096:8或CUBLAS_WORKSPACE_CONFIG=:16:8。了解更多细节https://docs.nvidia.com/cuda/cublas/index.html#cublasApi_reproducibility。如果没有设置这些环境变量配置之一,当使用CUDA张量调用这些操作时将引发RuntimeError:

方法优缺点:

第一种方法保证了可复现但会较大降低训练速度。我测试了很多办法,终于解决了这一问题,见第二部分。


二、Pytorch-lightning seed setting method

pytorch-lightning是对pytorch的封装,省去很多繁琐的中间过程,使用起来非常方便。为了确保可复现性,pl提供了设置接口,非常简洁方便。

版本情况:

pytorch-lightning                  2.0.2
torch                              2.0.1

设置:

from lightning.pytorch import Trainer, seed_everything

seed_everything(42, workers=True)
# sets seeds for numpy, torch and python.random.

trainer = Trainer(deterministic=True)
To ensure full reproducibility from run to run you need to set seeds for pseudo-random generators, and set deterministic flag in Trainer.
By setting  workers=True in  seed_everything(), Lightning derives unique seeds across all dataloader workers and processes for  torchnumpy and stdlib  random number generators. When turned on, it ensures that e.g. data augmentations are not repeated across workers.

设置workers=True会确保在dataloader中的多线程的可复现性, 并且确保各线程得到各自独特的不会重复的种子,避免因为相同的种子导致相同的数据增强。

deterministic决定pytorch是否使用确定性算法,默认False, 设置True会使pytorch使用确定性算法。

通过上述pl设置,主要有以下优点(重点!):

  • 保证了可复现的同时,没有牺牲任何训练速度
  • 设置非常简单方便

PyTorch Lightning Reproducibility 2.0.2 documentation

References:

Alxander:PyTorch可复现/重复实验的相关设置
torch.backends.cudnn.enabled - PyTorch 2.0 documentation
xiaopl:torch.backends.cudnn.benchmark ?!
有关于pytorch模型训练的可复现性_set_deterministic_Reza.的博客-CSDN博客
  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
对于复现经典的 PyTorch 模型,你可以按照以下步骤进行操作: 1. 确定要复现的经典模型:选择你想要复现的经典模型,可以是在计算机视觉、自然语言处理或其他领域中广泛应用的模型,例如 AlexNet、VGG、ResNet 等。 2. 收集模型的相关论文和代码:查找并阅读原始论文,以了解模型的具体结构和训练方法。同时,搜索已经实现的代码库或者 GitHub 上的开源项目,以获取相关的参考代码。 3. 创建 PyTorch 模型:根据论文中描述的模型结构,使用 PyTorch 创建一个对应的模型类。这包括定义模型的网络层、初始化权重等操作。 4. 实现模型训练:根据论文中描述的训练方法,实现相应的训练步骤。这可能包括数据预处理、损失函数定义、优化器选择以及训练循环等。 5. 数据准备:准备用于训练和测试模型的数据集。这可能涉及数据下载、数据预处理、数据划分等。 6. 训练模型:使用准备好的数据集对模型进行训练。根据需要,可以设置训练超参数、学习率调度器等。 7. 模型评估:使用测试集或交叉验证集对训练好的模型进行评估。可以计算准确率、损失值等指标。 8. 模型优化(可选):根据需要,尝试改进模型的能。这可能包括调整超参数、尝试不同的优化器、增加正则化等。 9. 结果分析:分析模型的能和训练过程,比较实现的模型与原论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值