如何将 Tensorflow BatchNormalization 与 GradientTape 一起使用?
How to use Tensorflow BatchNormalization with GradientTape?
假设我们有一个使用 BatchNormalization 的简单 Keras 模型:
model = tf.keras.Sequential([
tf.keras.layers.InputLayer(input_shape=(1,)),
tf.keras.layers.BatchNormalization()
])
GradientTape实际如何使用?以下似乎不起作用,因为它不更新移动平均线?
# model training... we want the output values to be close to 150
for i in range(1000):
x = np.random.randint(100, 110, 10).astype(np.float32)
with tf.GradientTape() as tape:
y = model(np.expand_dims(x, axis=1))
loss = tf.reduce_mean(tf.square(y - 150))
grads = tape.gradient(loss, model.variables)
opt.apply_gradients(zip(grads, model.variables))
特别是,如果您检查移动平均线,它们将保持不变(检查 model.variables,平均值始终为 0 和 1)。我知道可以使用 .fit() 和 .predict(),但我想使用 GradientTape,但我不确定该怎么做。某些版本的文档建议更新 update_ops,但这在 eager 模式下似乎不起作用。
特别是,经过上述训练后,以下代码不会输出任何接近 150 的值。
x = np.random.randint(200, 210, 100).astype(np.float32)
print(model(np.expand_dims(x, axis=1)))
我只好放弃了。我花了一些时间试图理解一个看起来像这样的模型:
model = tf.keras.Sequential([
tf.keras.layers.BatchNormalization(),
])
我确实放弃了,因为那东西看起来像这样:
我的直觉是 BatchNorm 现在不像以前那么直接了,这就是为什么它扩展了原始分布但没有那么多新分布(这是一种耻辱),但没有人有时间为此。
编辑: 出现这种行为的原因是 BN 仅在训练期间计算矩并标准化批次。在训练期间,它保持 运行 均值和偏差的平均值,一旦您切换到评估,参数将用作常量。即评估不应该依赖于规范化,因为评估甚至可以用于单个输入并且不能依赖于批统计。由于常量是根据不同的分布计算的,因此您在评估期间会遇到更高的错误。
使用梯度带模式 BatchNormalization 层应该使用参数 training=True 来调用
示例:
inp = KL.Input( (64,64,3) )
x = inp
x = KL.Conv2D(3, kernel_size=3, padding='same')(x)
x = KL.BatchNormalization()(x, training=True)
model = KM.Model(inp, x)
然后移动变量被正确更新
>>> model.layers[2].weights[2]
<tf.Variable 'batch_normalization/moving_mean:0' shape=(3,) dtype=float32, numpy
=array([-0.00062087, 0.00015137, -0.00013239], dtype=float32)>
使用渐变带模式,您通常会发现如下渐变:
with tf.GradientTape() as tape:
y_pred = model(features)
loss = your_loss_function(y_pred, y_true)
gradients = tape.gradient(loss, model.trainable_variables)
train_op = model.optimizer.apply_gradients(zip(gradients, model.trainable_variables))
但是,如果您的模型包含 BatchNormalization
或 Dropout
层(或具有不同 train/test 阶段的任何层),则 tf 将无法构建图形。
一个好的做法是在从模型获取输出时显式使用 trainable
参数。优化时使用 model(features, trainable=True)
和预测时使用 model(features, trainable=False)
,以便在使用此类层时明确选择 train/test 阶段。
对于PREDICT
和EVAL
阶段,使用
training = (mode == tf.estimator.ModeKeys.TRAIN)
y_pred = model(features, trainable=training)
对于TRAIN
阶段,使用
with tf.GradientTape() as tape:
y_pred = model(features, trainable=training)
loss = your_loss_function(y_pred, y_true)
gradients = tape.gradient(loss, model.trainable_variables)
train_op = model.optimizer.apply_gradients(zip(gradients, model.trainable_variables))
请注意,iperov 的答案同样有效,只是您需要为这些层手动设置训练阶段。
x = BatchNormalization()(x, training=True)
x = Dropout(rate=0.25)(x, training=True)
x = BatchNormalization()(x, training=False)
x = Dropout(rate=0.25)(x, training=False)
我建议使用一个 get_model
函数来 returns 模型,同时在调用模型时使用 training
参数更改相位。
注:
如果您在查找渐变时使用 model.variables
,您将收到此警告
Gradients do not exist for variables
['layer_1_bn/moving_mean:0',
'layer_1_bn/moving_variance:0',
'layer_2_bn/moving_mean:0',
'layer_2_bn/moving_variance:0']
when minimizing the loss.
这可以通过仅针对可训练变量计算梯度来解决。将 model.variables
替换为 model.trainable_variables
假设我们有一个使用 BatchNormalization 的简单 Keras 模型:
model = tf.keras.Sequential([
tf.keras.layers.InputLayer(input_shape=(1,)),
tf.keras.layers.BatchNormalization()
])
GradientTape实际如何使用?以下似乎不起作用,因为它不更新移动平均线?
# model training... we want the output values to be close to 150
for i in range(1000):
x = np.random.randint(100, 110, 10).astype(np.float32)
with tf.GradientTape() as tape:
y = model(np.expand_dims(x, axis=1))
loss = tf.reduce_mean(tf.square(y - 150))
grads = tape.gradient(loss, model.variables)
opt.apply_gradients(zip(grads, model.variables))
特别是,如果您检查移动平均线,它们将保持不变(检查 model.variables,平均值始终为 0 和 1)。我知道可以使用 .fit() 和 .predict(),但我想使用 GradientTape,但我不确定该怎么做。某些版本的文档建议更新 update_ops,但这在 eager 模式下似乎不起作用。
特别是,经过上述训练后,以下代码不会输出任何接近 150 的值。
x = np.random.randint(200, 210, 100).astype(np.float32)
print(model(np.expand_dims(x, axis=1)))
我只好放弃了。我花了一些时间试图理解一个看起来像这样的模型:
model = tf.keras.Sequential([
tf.keras.layers.BatchNormalization(),
])
我确实放弃了,因为那东西看起来像这样:
我的直觉是 BatchNorm 现在不像以前那么直接了,这就是为什么它扩展了原始分布但没有那么多新分布(这是一种耻辱),但没有人有时间为此。
编辑: 出现这种行为的原因是 BN 仅在训练期间计算矩并标准化批次。在训练期间,它保持 运行 均值和偏差的平均值,一旦您切换到评估,参数将用作常量。即评估不应该依赖于规范化,因为评估甚至可以用于单个输入并且不能依赖于批统计。由于常量是根据不同的分布计算的,因此您在评估期间会遇到更高的错误。
使用梯度带模式 BatchNormalization 层应该使用参数 training=True 来调用
示例:
inp = KL.Input( (64,64,3) )
x = inp
x = KL.Conv2D(3, kernel_size=3, padding='same')(x)
x = KL.BatchNormalization()(x, training=True)
model = KM.Model(inp, x)
然后移动变量被正确更新
>>> model.layers[2].weights[2]
<tf.Variable 'batch_normalization/moving_mean:0' shape=(3,) dtype=float32, numpy
=array([-0.00062087, 0.00015137, -0.00013239], dtype=float32)>
使用渐变带模式,您通常会发现如下渐变:
with tf.GradientTape() as tape:
y_pred = model(features)
loss = your_loss_function(y_pred, y_true)
gradients = tape.gradient(loss, model.trainable_variables)
train_op = model.optimizer.apply_gradients(zip(gradients, model.trainable_variables))
但是,如果您的模型包含 BatchNormalization
或 Dropout
层(或具有不同 train/test 阶段的任何层),则 tf 将无法构建图形。
一个好的做法是在从模型获取输出时显式使用 trainable
参数。优化时使用 model(features, trainable=True)
和预测时使用 model(features, trainable=False)
,以便在使用此类层时明确选择 train/test 阶段。
对于PREDICT
和EVAL
阶段,使用
training = (mode == tf.estimator.ModeKeys.TRAIN)
y_pred = model(features, trainable=training)
对于TRAIN
阶段,使用
with tf.GradientTape() as tape:
y_pred = model(features, trainable=training)
loss = your_loss_function(y_pred, y_true)
gradients = tape.gradient(loss, model.trainable_variables)
train_op = model.optimizer.apply_gradients(zip(gradients, model.trainable_variables))
请注意,iperov 的答案同样有效,只是您需要为这些层手动设置训练阶段。
x = BatchNormalization()(x, training=True)
x = Dropout(rate=0.25)(x, training=True)
x = BatchNormalization()(x, training=False)
x = Dropout(rate=0.25)(x, training=False)
我建议使用一个 get_model
函数来 returns 模型,同时在调用模型时使用 training
参数更改相位。
注:
如果您在查找渐变时使用 model.variables
,您将收到此警告
Gradients do not exist for variables
['layer_1_bn/moving_mean:0',
'layer_1_bn/moving_variance:0',
'layer_2_bn/moving_mean:0',
'layer_2_bn/moving_variance:0']
when minimizing the loss.
这可以通过仅针对可训练变量计算梯度来解决。将 model.variables
替换为 model.trainable_variables