Pytorch的坑之 为什么模型参数和输入不变,输出结果却变了(BN的坑)

引言

不知道小伙伴使用pytorch时是否遇到了下面的情况:我们加载训练好的参数,然后输入经过网络,但是不经过梯度下降训练,也就是说模型的参数没有更改,这是我们仍然会发现我们输出的指标(例如准确率)下降。

现象

最近跑的一个程序,加载好预训练模型

checkpoint = load_checkpoint(args.resume)
model.load_state_dict(checkpoint['state_dict'])

随后我们加载好预训练模型,然后执行下面一段代码: 模型设为train模式,然后经过网络得到输出,此时我们并没有对输出的结果进行训练,参数没有变。然后模式设为eval模式,我们得到的最终评价指标res。

self.model.train()
input_dict = self._parse_data(inputs)
output_dict = self._forward(input_dict)
self.model.eval()
res = evaluator.evaluate(test_loader,  dataset=test_dataset)

但是不同的res实验结果却不一样。

分析

经过查阅资料发现模型内部有一个”捣蛋鬼“。那就是Batch Normalization。Batch Normalization(简称为BN),中文翻译成批规范化,是在深度学习中普遍使用的一种技术,通常用于解决多层神经网络中间层的协方差偏移(Internal Covariate Shift)问题,类似于网络输入进行零均值化和方差归一化的操作,不过是在中间层的输入中操作而已,具体原理不累述了。
题的关键 BN层的统计数据更新是在每一次训练阶段model.train()后的forward()方法中自动实现的,而不是在梯度计算与反向传播中更新optim.step()中完成。
所以说即使不梯度计算,整个模型还是由于BN的变化更新了。

拓展

BN其实坑还是不少的,还有两个值得小伙伴们注意的坑。

第一个是我们如果加载一个与训练好包括BN的模型迁移到其他任务,例如想着直接在物体检测框架的模型上添加mask分支,冻结detection参数,只训练mask相关的参数。那么如果不冻结BN的话,detection相关的性能指标一直在变!如何冻结呢?
正确打开方式如下:

def fix_bn(m):
    classname = m.__class__.__name__
    if classname.find('BatchNorm') != -1:
        m.eval()

model = models.resnet50(pretrained=True)
model.cuda()
model.train()
model.apply(fix_bn) 

第二个坑就是当使用网络进行推理的时候,发现每次更改测试集的batch size大小竟然会导致推理结果不同,甚至产生错误结果,后来发现在网络中定义了BN层,BN层在训练过程中,会将一个Batch的中的数据转变成正太分布,在推理过程中使用训练过程中的参数对数据进行处理,然而网络并不知道你是在训练还是测试阶段,因此,需要手动的加上:

model.train()
model.eval()
    原文作者:fond_dependent
    原文地址: https://blog.csdn.net/qq_38556984/article/details/105662342
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞