tf.keras 中的批量归一化不计算平均均值和平均方差
Batch Normalization in tf.keras does not calculate average mean and average variance
有人问了一个类似的未回答的问题 。
我正在测试一种在 tensorflow 中使用 keras 后端的深度强化学习算法。我对 tf.keras 不是很熟悉,但还是想添加批量归一化层。因此,我尝试使用 tf.keras.layers.BatchNormalization()
,但它不会更新平均均值和方差,因为 update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
为空。
使用常规 tf.layers.batch_normalization
似乎工作正常。但是,由于完整的算法有些复杂,我需要想办法使用tf.keras
。
标准 tf
层 batch_normed = tf.layers.batch_normalization(hidden, training=True)
更新平均值,因为 update_ops
不为空:
[
<tf.Operation 'batch_normalization/AssignMovingAvg' type=AssignSub>,
<tf.Operation 'batch_normalization/AssignMovingAvg_1' type=AssignSub>,
<tf.Operation 'batch_normalization_1/AssignMovingAvg' type=AssignSub>,
<tf.Operation 'batch_normalization_1/AssignMovingAvg_1' type=AssignSub>
]
不的最小示例:
import tensorflow as tf
import numpy as np
tf.reset_default_graph()
graph = tf.get_default_graph()
tf.keras.backend.set_learning_phase(True)
input_shapes = [(3, )]
hidden_layer_sizes = [16, 16]
inputs = [
tf.keras.layers.Input(shape=input_shape)
for input_shape in input_shapes
]
concatenated = tf.keras.layers.Lambda(
lambda x: tf.concat(x, axis=-1)
)(inputs)
out = concatenated
for units in hidden_layer_sizes:
hidden = tf.keras.layers.Dense(
units, activation=None
)(out)
batch_normed = tf.keras.layers.BatchNormalization()(hidden, training=True)
#batch_normed = tf.layers.batch_normalization(hidden, training=True)
out = tf.keras.layers.Activation('relu')(batch_normed)
out = tf.keras.layers.Dense(
units=1, activation='linear'
)(out)
data = np.random.rand(100,3)
with tf.Session(graph=graph) as sess:
sess.run(tf.global_variables_initializer())
for i in range(10):
update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
sess.run(update_ops, {inputs[0]: data})
sess.run(out, {inputs[0]: data})
variables = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,
scope='batch_normalization')
bn_gamma, bn_beta, bn_moving_mean, bn_moving_variance = [], [], [], []
for variable in variables:
val = sess.run(variable)
nv = np.linalg.norm(val)
if 'gamma' in variable.name:
bn_gamma.append(nv)
if 'beta' in variable.name:
bn_beta.append(nv)
if 'moving_mean' in variable.name:
bn_moving_mean.append(nv)
if 'moving_variance' in variable.name:
bn_moving_variance.append(nv)
diagnostics = {
'bn_Q_gamma': np.mean(bn_gamma),
'bn_Q_beta': np.mean(bn_beta),
'bn_Q_moving_mean': np.mean(bn_moving_mean),
'bn_Q_moving_variance': np.mean(bn_moving_variance),
}
print(diagnostics)
输出如下(可以看到moving_mean和moving_variance没有变化):
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}
虽然预期的输出类似于以下内容(使用 tf.keras
注释带有 batch_normed
微积分的行并取消注释下面的行):
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0148749575, 'bn_Q_moving_variance': 3.966927}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.029601166, 'bn_Q_moving_variance': 3.934192}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.04418011, 'bn_Q_moving_variance': 3.9017918}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.05861327, 'bn_Q_moving_variance': 3.8697228}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0729021, 'bn_Q_moving_variance': 3.8379822}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.08704803, 'bn_Q_moving_variance': 3.8065662}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.10105251, 'bn_Q_moving_variance': 3.7754717}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.11491694, 'bn_Q_moving_variance': 3.7446957}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.12864274, 'bn_Q_moving_variance': 3.7142346}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.14223127, 'bn_Q_moving_variance': 3.6840856}
备注
即使 tf.layers.batch_normalization
也有一些可疑之处。 tf.control_dependencies
的标准 tf
方法:
with tf.control_dependencies(update_ops):
sess.run(out, {inputs[0]: data})
我在上面的代码中放置了以下两行:
sess.run(update_ops, {inputs[0]: data})
sess.run(out, {inputs[0]: data})
产生 bn_Q_moving_mean = 0.0
和 bn_Q_moving_variance = 4.0
这是因为 tf.keras.layers.BatchNormalization
继承自 tf.keras.layers.Layer
。 Keras API 将更新操作作为其拟合和评估循环的一部分进行处理。这反过来意味着没有它就不会更新 tf.GraphKeys.UPDATE_OPS
集合。
所以为了让它工作,你需要手动更新它
hidden = tf.keras.layers.Dense(units, activation=None)(out)
batch_normed = tf.keras.layers.BatchNormalization(trainable=True)
layer = batch_normed(hidden)
这会创建单独的 class 实例
tf.add_to_collection(tf.GraphKeys.UPDATE_OPS, batch_normed.updates)
并且此更新需要收集。也看看https://github.com/tensorflow/tensorflow/issues/25525
tf.add_to_collection(tf.GraphKeys.UPDATE_OPS, bn1.updates[0])
tf.add_to_collection(tf.GraphKeys.UPDATE_OPS, bn1.updates[1])
updates_op = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
这个可以解决
tf.control_dependencies(update_ops)
错误问题。
如果使用
tf.add_to_collection(tf.GraphKeys.UPDATE_OPS, batch_normed.updates)
的return
tf.get_collection(tf.GraphKeys.UPDATE_OPS)
是列表中的列表,就像 [[something]]
并使用
tf.add_to_collection(tf.GraphKeys.UPDATE_OPS, bn1.updates[0])
tf.add_to_collection(tf.GraphKeys.UPDATE_OPS, bn1.updates[1])
updates_op = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
的return
tf.get_collection(tf.GraphKeys.UPDATE_OPS)
是 [something1,something2,...]
我认为这是解决方案。
但是输出结果不一样,我不知道哪个是真的。
有人问了一个类似的未回答的问题 tf.keras.layers.BatchNormalization()
,但它不会更新平均均值和方差,因为 update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
为空。
使用常规 tf.layers.batch_normalization
似乎工作正常。但是,由于完整的算法有些复杂,我需要想办法使用tf.keras
。
标准 tf
层 batch_normed = tf.layers.batch_normalization(hidden, training=True)
更新平均值,因为 update_ops
不为空:
[
<tf.Operation 'batch_normalization/AssignMovingAvg' type=AssignSub>,
<tf.Operation 'batch_normalization/AssignMovingAvg_1' type=AssignSub>,
<tf.Operation 'batch_normalization_1/AssignMovingAvg' type=AssignSub>,
<tf.Operation 'batch_normalization_1/AssignMovingAvg_1' type=AssignSub>
]
不的最小示例:
import tensorflow as tf
import numpy as np
tf.reset_default_graph()
graph = tf.get_default_graph()
tf.keras.backend.set_learning_phase(True)
input_shapes = [(3, )]
hidden_layer_sizes = [16, 16]
inputs = [
tf.keras.layers.Input(shape=input_shape)
for input_shape in input_shapes
]
concatenated = tf.keras.layers.Lambda(
lambda x: tf.concat(x, axis=-1)
)(inputs)
out = concatenated
for units in hidden_layer_sizes:
hidden = tf.keras.layers.Dense(
units, activation=None
)(out)
batch_normed = tf.keras.layers.BatchNormalization()(hidden, training=True)
#batch_normed = tf.layers.batch_normalization(hidden, training=True)
out = tf.keras.layers.Activation('relu')(batch_normed)
out = tf.keras.layers.Dense(
units=1, activation='linear'
)(out)
data = np.random.rand(100,3)
with tf.Session(graph=graph) as sess:
sess.run(tf.global_variables_initializer())
for i in range(10):
update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
sess.run(update_ops, {inputs[0]: data})
sess.run(out, {inputs[0]: data})
variables = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,
scope='batch_normalization')
bn_gamma, bn_beta, bn_moving_mean, bn_moving_variance = [], [], [], []
for variable in variables:
val = sess.run(variable)
nv = np.linalg.norm(val)
if 'gamma' in variable.name:
bn_gamma.append(nv)
if 'beta' in variable.name:
bn_beta.append(nv)
if 'moving_mean' in variable.name:
bn_moving_mean.append(nv)
if 'moving_variance' in variable.name:
bn_moving_variance.append(nv)
diagnostics = {
'bn_Q_gamma': np.mean(bn_gamma),
'bn_Q_beta': np.mean(bn_beta),
'bn_Q_moving_mean': np.mean(bn_moving_mean),
'bn_Q_moving_variance': np.mean(bn_moving_variance),
}
print(diagnostics)
输出如下(可以看到moving_mean和moving_variance没有变化):
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0, 'bn_Q_moving_variance': 4.0}
虽然预期的输出类似于以下内容(使用 tf.keras
注释带有 batch_normed
微积分的行并取消注释下面的行):
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0148749575, 'bn_Q_moving_variance': 3.966927}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.029601166, 'bn_Q_moving_variance': 3.934192}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.04418011, 'bn_Q_moving_variance': 3.9017918}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.05861327, 'bn_Q_moving_variance': 3.8697228}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.0729021, 'bn_Q_moving_variance': 3.8379822}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.08704803, 'bn_Q_moving_variance': 3.8065662}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.10105251, 'bn_Q_moving_variance': 3.7754717}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.11491694, 'bn_Q_moving_variance': 3.7446957}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.12864274, 'bn_Q_moving_variance': 3.7142346}
{'bn_Q_gamma': 4.0, 'bn_Q_beta': 0.0, 'bn_Q_moving_mean': 0.14223127, 'bn_Q_moving_variance': 3.6840856}
备注
即使 tf.layers.batch_normalization
也有一些可疑之处。 tf.control_dependencies
的标准 tf
方法:
with tf.control_dependencies(update_ops):
sess.run(out, {inputs[0]: data})
我在上面的代码中放置了以下两行:
sess.run(update_ops, {inputs[0]: data})
sess.run(out, {inputs[0]: data})
产生 bn_Q_moving_mean = 0.0
和 bn_Q_moving_variance = 4.0
这是因为 tf.keras.layers.BatchNormalization
继承自 tf.keras.layers.Layer
。 Keras API 将更新操作作为其拟合和评估循环的一部分进行处理。这反过来意味着没有它就不会更新 tf.GraphKeys.UPDATE_OPS
集合。
所以为了让它工作,你需要手动更新它
hidden = tf.keras.layers.Dense(units, activation=None)(out)
batch_normed = tf.keras.layers.BatchNormalization(trainable=True)
layer = batch_normed(hidden)
这会创建单独的 class 实例
tf.add_to_collection(tf.GraphKeys.UPDATE_OPS, batch_normed.updates)
并且此更新需要收集。也看看https://github.com/tensorflow/tensorflow/issues/25525
tf.add_to_collection(tf.GraphKeys.UPDATE_OPS, bn1.updates[0])
tf.add_to_collection(tf.GraphKeys.UPDATE_OPS, bn1.updates[1])
updates_op = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
这个可以解决
tf.control_dependencies(update_ops)
错误问题。
如果使用
tf.add_to_collection(tf.GraphKeys.UPDATE_OPS, batch_normed.updates)
的return
tf.get_collection(tf.GraphKeys.UPDATE_OPS)
是列表中的列表,就像 [[something]]
并使用
tf.add_to_collection(tf.GraphKeys.UPDATE_OPS, bn1.updates[0])
tf.add_to_collection(tf.GraphKeys.UPDATE_OPS, bn1.updates[1])
updates_op = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
的return
tf.get_collection(tf.GraphKeys.UPDATE_OPS)
是 [something1,something2,...]
我认为这是解决方案。
但是输出结果不一样,我不知道哪个是真的。