目录
背景说明
问题分析
解决方法
背景说明
使用同一套代码、同一个虚拟环境、同样的输入文件、同一个GPU、固定的随机种子、同一个系统环境。也就是除了pycharm和terminal,其他所有的条件都是一样的。但是发现,在Pycharm中直接点运行,和在terminal中通过python xxx运行,两者的模型的输出结果竟然有差异。虽然差异不同,但可以看出很小 (这时候直觉上就可以怀疑是精度问题了)。
快餐时代,先说结论,感兴趣的可再往后看分析过程:
在两种方式下,对输入数据和模型、权重看dtype虽然都显示float32,但是实际上并非如此。如果在推理之前强制把输入数据明确指定为float32或者float64,那pycharm和terminal的输出就都一样了。猜测可能pycharm中pytorch默认读取数据是float32,terminal中默认是float64。也可能是其他原因导致的实际读取精度不一样,比如模型训练时候保存的权重就有精度问题?欢迎评论区补充~
问题分析
1、对于模型,逐层看一下是哪个层开始出问题的。
def forward(self, x, mode='eval'):
x = self.layer1(x)
print(x)
exit()
如果layer1没问题,那就换成layer2继续看。发现我的情况是在layer1就出问题了。
2、对于目标层,再挨个看是哪个模块开始出问题的。
print(model.layer1[:1](data_a))
print(model.layer1[:2](data_a))
# ... ... 同上一层一层加
发现是在卷积层出的问题:
nn.Conv2d(3, 32, kernel_size=3, padding=1, bias=is_bias),
由于随机种子已经固定了,卷积的输出应该也不会发生变化才对。
3、检查下输入数据和模型的精度。
print(data_a.dtype)
print(model.layer1[2].weight.dtype)
print(model.layer1[2].bias.dtype)
显示的默认都是float32类型。看不出来什么差异。
4、尝试强制指定数据类型。
# 输入数据指定为double类型(也就是float64)
model(data_a.double())
# 卷积指定为double类型(也就是float64)
nn.Conv2d(3, 32, kernel_size=3, padding=1, bias=is_bias).double(),
再次运行,发现两者的结果一致了。但是发现改为float()时,pycharm运行能变,而terminal的没变(即还是double时候的结果)。暂不清楚为何terminal的改不了float。
解决方法
那么知道了是pytorch的精度问题,为了统一管理,只需要在代码最开始就指定精度类型。
if __name__ == '__main__':
torch.set_default_dtype(torch.double)
# xxxxx
# xxxxx
或者仅在需要的时候转一下类型:
out_a = model(data_a.double())