固定随机种子使模型可复现在具体实验中是非常重要的。网上提供了许多操作方法,简单粗暴就是上如下代码:
manual_seed = config.get('manual_seed', None)
if manual_seed is not None:
logger.info(f'Seed the RNG for all devices with {manual_seed}')
os.environ['PYTHONHASHSEED'] = str(manual_seed)
torch.manual_seed(manual_seed)
torch.cuda.manual_seed(manual_seed)
torch.cuda.manual_seed_all(manual_seed) # if you are using multi-GPU
random.seed(manual_seed)
np.random.seed(manual_seed)
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
torch.backends.cudnn.enabled = True
在主函数开头运行上述代码段,固定随机种子,能解决大部分问题
为了避免重复造轮子,不可能所有模型都从0开始写,用一些public的代码是非常必要的。但不同的人习惯不一样,模型结构也不一样,不是每一个模型都能顺利的调通,特别是在遇到无法复现的时候,根本找不出问题在哪里!
例如,扒拉了一个代码,固定随机种子后,在相同的参数下重复跑两次,最初几个epoch的loss,dice都是相同的,但在某一次开始,loss的最后几位小数开始发生变化,再过几个epoch后变化越来越大,然后就变得毫无关系了。重复多次,都无法复现实验结果。
通过重复实验和测试,发现问题出在nn.upsample中
不添加upsample,使用反卷积时,模型可复现;使用upsample后,模型便出现上述不确定的情况。
也有网友指出,upsample导致模型可复现性变差,这一点在PyTorch的官方库issue#12207中有提到
原文中说you can try to make the operation deterministic ... by setting torch.backends.cudnn.deterministic = True
这里是try ***,不是you can 。
在我的实验中测试的公开代码中有如下模块
self.conv = UnetConv3(in_size + out_size, out_size, is_batchnorm, kernel_size=(3,3,3), padding_size=(1,1,1))
self.up = nn.Upsample(scale_factor=(2, 2, 2), mode='trilinear')
这是Unet的上采样过程,这里加入了deep supervision,所以和丐版Unet中的上采样有区别:
self.conv = UnetConv3(in_size, out_size, is_batchnorm)
self.up = nn.ConvTranspose3d(in_size, out_size, kernel_size=(4,4,1), stride=(2,2,1), padding=(1,1,0))
这两个地方都为了扩大featuremap,但使用了不同的层。同时,两个模型的其他地方也没明显的区别。
在之前我自己写的模块中,也用到了反卷积和上采样,那么问题就出在了mode上
将模型改为如下:
self.conv = UnetConv3(in_size + out_size, out_size, kernel_size=(3,3,3), padding_size=(1,1,1))
self.up = nn.Upsample(scale_factor=(2, 2, 2), mode='nearest')
又经过重复实验,复现成功!
参考资料:
https://www.shuzhiduo.com/A/q4zVxqbx5K/
https://pytorch.org/docs/stable/nn.functional.html?highlight=upsample#torch.nn.functional.upsample
https://zhuanlan.zhihu.com/p/109166845