这个东西,其实是一个玄学。我已经第二次遇到这个问题了。
第一次遇到的时候就很头疼,那次我不知道是怎么地莫名奇妙的解决了。这次又来一次,没有办法莫名奇妙了。只能思考如何解决。
我这次背景是:模型主体使用了CNN架构,出现这个问题。啥问题呢?在train数据集上,使用train模式的时候准确率很高,然后用eval模式在val数据集上测试,发现效果特别差。
第一反应是什么?过拟合对不对!所以我用eval模式在train数据集上测试,发现效果特别差!!!
结论:不是过拟合,就是eval模式和train模式的问题。所以进一步验证,在train模式下,val/test数据集上测试效果都不错。
我的情况是这样的,我不算是自己设计的网络,而是官方的网络,然后我只是对层数进行删减,框架是保留的。所以网上说的,1. 不要同时使用dropout和batchnormalization 在我这里不适用,毕竟是官方网络架构,本身没有问题。我这个网络架构,他是在卷积层后面,平均池化,然后dropout,然后线性层分类。其他地方没有使用dropout,大家可以对照下自己的网络结构,也按照这个思路设计,从而有一个参考。
由于是官方网络,2. 不同地方调用了同一个batchnormalization 对我也不适用,因为官方网络不会有这个错误。不过,话说,这个调用同一个,会有这么大影响嘛?我真没试过。
也有人说,3. 输入数据没有归一化 ,如果是这个问题,那为什么train模式下对train/val/test效果都可以呢?
要明白,train和eval只有一个区别,那就是dropout和batch normalization在train/eval模式下工作方式不一样。所以,上面两个解决思路也的确是围绕这个在展开。
在train模式下好,eval模式下差,我们可以理解为:train模式下,最后一层得到的那一堆向量好,eval得到的那一堆向量差,从而逐步往前推。
batch normalization的一个好处就是使用过后,数据会服从均为0,方差为1的正态分布,比较稳定,从而使得模型比较好训练。
因此,一个简单的对比就是,你在你的模型训练了好几轮,觉得效果不错了之后,停下来,选择几个训练集数据,比如5个,分别在train模式和eval模式下,打印他们输入,中间层以及结尾的结果,这个你自己看着办即可。
至于打印什么结果,当然不是那些tensor,没有看得懂,而是打印他们的均值和方差。
x.mean()
x.var()
#数据1
1.4886/29791.4102#输入
0.0249/0.6548 0.0215/0.3354#中间某层
前面是eval模式的mean/var,后面是train模式
0.4258/13.0988 0.1238/12.9480#中间某层
#数据2
0.6043/751.1927
0.0119/0.0104 0.0333/0.3308
0.4165/7.9723 0.2913/14.2297
#数据3
1.7033/34862.4297
0.0267/0.7132 0.0223/0.3357
0.4337/11.9446 0.1300/12.6917
#数据4
0.3590/40.7517
0.0099/0.0007 0.0708/0.3518
0.4294/8.2139 0.4683/15.1485
不管怎么说吧,首先引人注目的就是,我的4个数据,输入的均值和方差都特别不一样,尤其是方差,3万多的方差,吓死。之前没有打印,说句实话我还不知道。
然后就是中间层1:我们发现,train模式下,其很稳定。均值大概就在0.02/0.03的样子,只有数据4有点反常,0.07。然后方差,他们都稳定在0.3的样子。
反观eval模式下,中间层1抖来抖去的。
中间层2,eval模式下,均值倒是很稳定,但是方差不稳定。train模式下,均值有点抖动,但是方差稳定。
总的来说,我们的数据输入方差很大,然后train模式和eval模式各层结果差别显著。
前者数据输入我们可以处理,train模式和eval模式差别很大我们只能干看着,没有办法处理哈哈。
结论:
我们将数据进行预处理,进行归一化,前面我们不是否定了归一化嘛?又屈服了?对的哈哈。我们实打实地看到了,输入方差贼大。
我当时没有归一化,因为我怕归一化会损失我地输入数据的一些细节。因为我的输入并不是真实世界的图片,里面的一些数字是有含义的,我怕归一化之后破坏了。
怎么归一化?可以对图片的各个channel进行z-标准化,也就是batch normalization那样。
成功了,同时发现,归一化之后训练得很快!!!之前要3个epoch收敛,现在1个epoch就差不多收敛了。牛逼。