批量归一化,是还是不是?
batch normalization, yes or no?
我使用 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.
我的主要问题要复杂得多,但同样的事情发生了。由于我的激活是线性的,所以我将批归一化放在激活之后还是之前都没有关系。
问题: 为什么批量标准化无济于事?有什么我可以改变的,以便批量归一化在不改变激活函数的情况下改善结果吗?
收到评论后更新:
具有一个隐藏层和线性激活的神经网络有点像 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
是。
您观察到的行为是一个错误 - 您不需要 BN 也能看到它;左边的图是 #V1
,右边的图是 #V2
:
#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('linear')
在具有 activation=None
(=='linear'
) 的层之后是一个 identity:model.layers[1].output.name == 'activation/activation/Identity:0'
.这可以通过获取和绘制中间层输出来进一步确认,'dense'
和 'activation'
是相同的 - 将在此处省略。
所以,激活几乎什么都不做,除了它没有 - 在 1.14.0 和 2.0.0 之间的提交链中的某个地方,这是固定的,虽然我不知道在哪里。使用带有 Keras 2.3.1 的 TF 2.0.0 的 BN 结果如下:
val_loss = 0.840 # without BN
val_loss = 0.819 # with BN
解决方案:更新到 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 中的一个 'controversial' 层,尚未完全修复 - 参见 Relevant Git;我计划最终自己调查它,但出于您的目的,这个答案的修复应该足够了。
我还建议您熟悉 BN 的基础理论,特别是关于它的训练与推理操作;简而言之,批量小于 32 是一个非常糟糕的主意,数据集应该足够大以允许 BN 准确地近似测试集 gamma
和 beta
.
使用代码:
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)
Imports/pre-executions:
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
我使用 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.
我的主要问题要复杂得多,但同样的事情发生了。由于我的激活是线性的,所以我将批归一化放在激活之后还是之前都没有关系。
问题: 为什么批量标准化无济于事?有什么我可以改变的,以便批量归一化在不改变激活函数的情况下改善结果吗?
收到评论后更新:
具有一个隐藏层和线性激活的神经网络有点像 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
是。
您观察到的行为是一个错误 - 您不需要 BN 也能看到它;左边的图是 #V1
,右边的图是 #V2
:
#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('linear')
在具有 activation=None
(=='linear'
) 的层之后是一个 identity:model.layers[1].output.name == 'activation/activation/Identity:0'
.这可以通过获取和绘制中间层输出来进一步确认,'dense'
和 'activation'
是相同的 - 将在此处省略。
所以,激活几乎什么都不做,除了它没有 - 在 1.14.0 和 2.0.0 之间的提交链中的某个地方,这是固定的,虽然我不知道在哪里。使用带有 Keras 2.3.1 的 TF 2.0.0 的 BN 结果如下:
val_loss = 0.840 # without BN
val_loss = 0.819 # with BN
解决方案:更新到 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 中的一个 'controversial' 层,尚未完全修复 - 参见 Relevant Git;我计划最终自己调查它,但出于您的目的,这个答案的修复应该足够了。
我还建议您熟悉 BN 的基础理论,特别是关于它的训练与推理操作;简而言之,批量小于 32 是一个非常糟糕的主意,数据集应该足够大以允许 BN 准确地近似测试集 gamma
和 beta
.
使用代码:
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)
Imports/pre-executions:
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