批量归一化,是或不是?

6
我使用Tensorflow 1.14.0和Keras 2.2.4。以下代码实现了一个简单的神经网络:
import numpy as np
np.random.seed(1)
import random
random.seed(2)
import tensorflow as tf
tf.set_random_seed(3)

from tensorflow.keras.models import Model, Sequential
from tensorflow.keras.layers import Input, Dense, Activation


x_train=np.random.normal(0,1,(100,12))

model = Sequential()
model.add(Dense(8, input_shape=(12,)))
# model.add(tf.keras.layers.BatchNormalization())
model.add(Activation('linear'))
model.add(Dense(12))
model.add(Activation('linear'))
model.compile(optimizer='adam', loss='mean_squared_error')
model.fit(x_train, x_train,epochs=20, validation_split=0.1, shuffle=False,verbose=2)

在20个epoch之后,最终的val_loss为0.7751。当我取消唯一的注释行以添加批归一化层时,val_loss会变为1.1230。
我的主要问题更加复杂,但是发生了同样的事情。由于我的激活函数是线性的,因此无论我在激活函数之前还是之后放置批归一化,都没有关系。
问题:为什么批归一化不能帮助?有没有什么方法可以改变,使得批归一化在不改变激活函数的情况下改善结果?
在得到评论后更新:
具有一个隐藏层和线性激活的NN有点像PCA。有很多关于这个的论文。对我来说,这种设置在隐藏层和输出的所有激活函数组合中给出了最小的MSE。
一些资源表明线性激活意味着PCA:

https://arxiv.org/pdf/1702.07800.pdf

https://link.springer.com/article/10.1007/BF00275687

https://www.quora.com/How-can-I-make-a-neural-network-to-work-as-a-PCA


1
与问题无关,但仅由线性激活组成的神经网络没有太多意义(我非常怀疑有人曾尝试过在这种配置下使用BN)。 - desertnaut
@desertnaut 我坚决不同意。一个具有一层隐藏层和线性激活函数的NN有点像PCA。关于这个问题有很多论文。对我来说,这种设置在所有激活函数的组合中给出了最小的MSE。 - Albert
PCA是一种线性变换,所以这里没有什么大惊小怪的;如果你想要低MSE,可以尝试使用PCA本身,并获得完美的重建(即零MSE)。不过我不确定你在这里究竟想做什么,可能我在你的目的上有所遗漏... - desertnaut
没反对这个观点。但是,在尝试花哨的建模之前,我们首先从一个基准线开始,而在这种情况下,应该是简单的普通PCA;如果我们做不比这更好,实际上我们就没有案例了(顺便说一句,如果您不介意,请分享一些那些论文)。 - desertnaut
@desertnaut 谢谢您提供的信息。NN是我唯一可以用来解决这个问题的工具。我正在更新问题,添加PCA的参考资料。 - Albert
显示剩余9条评论
1个回答

5

是的。

你观察到的行为是一个错误 - 你不需要BN来看到它;左边的图是#V1,右边的图是#V2

enter image description here

#V1
model = Sequential()
model.add(Dense(8, input_shape=(12,)))
#model.add(Activation('linear')) <-- uncomment == #V2
model.add(Dense(12))
model.compile(optimizer='adam', loss='mean_squared_error')

显然是无意义的,因为在一个activation=None(等于'linear')的层后面使用Activation('linear')会变成一个identitymodel.layers[1].output.name == 'activation/activation/Identity:0'。可以通过获取和绘制中间层输出来进一步确认,对于'dense''activation'它们是相同的——这里将省略。

所以,激活函数实际上什么也没做,除了它确实做了些事情——在1.14.0和2.0.0之间的提交链中的某个地方,这个问题已经被解决了,尽管我不知道在哪里。下面是使用TF 2.0.0和Keras 2.3.1进行BN的结果:

val_loss = 0.840 # without BN
val_loss = 0.819 # with BN

enter image description here


解决方案: 更新至 TensorFlow 2.0.0,Keras 2.3.1。 提示: 使用 Anaconda 带虚拟环境。如果您还没有任何虚拟环境,请运行:
conda create --name tf2_env --clone base
conda activate tf2_env
conda uninstall tensorflow-gpu
conda uninstall keras
conda install -c anaconda tensorflow-gpu==2.0.0
conda install -c conda-forge keras==2.3.1

也许比这更复杂,但这是另一个问题的主题。
更新:从keras导入而不是tf.keras也可以解决这个问题。
免责声明:BN在Keras中仍然是一个“有争议的”层,尚未完全修复-请参见相关Git;我计划最终自己进行调查,但对于您的目的,本答案的修复应该足够。 我还建议您熟悉BN的基本理论,特别是关于其训练与推理操作的理论;简而言之,批量大小小于32是一个非常糟糕的想法,数据集应足够大,以便BN可以准确地逼近测试集gammabeta

使用的代码:

x_train=np.random.normal(0, 1, (100, 12))

model = Sequential()
model.add(Dense(8, input_shape=(12,)))
#model.add(Activation('linear'))
#model.add(tf.keras.layers.BatchNormalization())
model.add(Dense(12))
model.compile(optimizer='adam', loss='mean_squared_error')

W_sum_all = []  # fit rewritten to allow runtime weight collection
for _ in range(20):
    for i in range(9):
        x = x_train[i*10:(i+1)*10]
        model.train_on_batch(x, x)

        W_sum_all.append([])
        for layer in model.layers:
            if layer.trainable_weights != []:
                W_sum_all[-1] += [np.sum(layer.get_weights()[0])]
model.evaluate(x[-10:], x[-10:])

plt.plot(W_sum_all)
plt.title("Sum of weights (#V1)", weight='bold', fontsize=14)
plt.legend(labels=["dense", "dense_1"], fontsize=14)
plt.gcf().set_size_inches(7, 4)

导入/预执行:

import numpy as np
np.random.seed(1)
import random
random.seed(2)
import tensorflow as tf
if tf.__version__[0] == '2':
    tf.random.set_seed(3)
else:
    tf.set_random_seed(3)

import matplotlib.pyplot as plt
from tensorflow.keras.models import Model, Sequential
from tensorflow.keras.layers import Input, Dense, Activation

1
非常感谢您的调查,我很感激您的时间。我不知道这个错误。这很奇怪。我已经为您的答案点赞了。我也想看看其他人的答案。 - Albert
2
@Albert,你很难看到任何与这个答案深度和质量稍微接近的东西;一定要查看OP在TensorFlow 2与TensorFlow 1性能问题上的惊人调查。 - desertnaut
@desertnaut 谢谢。 - Albert
@desertnaut,我能在我的简历上使用你的评论吗?就像这样--[为什么TF2...] - "令人难以置信的调查",C.I. Tsatsoulis,Nodalpoint Systems领先的数据科学家。--这可能会为我赢得一些分数。我怀疑任何人都不会因此打扰你。 - OverLordGoldDragon
1
@OverLordGoldDragon 没问题!虽然SO的评论被认为是“二等公民”,它们可能会在没有警告的情况下被删除;所以,我刚刚为您发布了一条推文(https://twitter.com/desertnaut/status/1191045432996175872)。 - desertnaut
@desertnaut 哎呀,谢谢 - 我会截个屏的。 - OverLordGoldDragon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接