【问题标题】:How to use Tensorflow BatchNormalization with GradientTape?如何将 Tensorflow BatchNormalization 与 GradientTape 结合使用?
【发布时间】:2019-10-28 12:16:48
【问题描述】:

假设我们有一个使用 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)))

【问题讨论】:

  • 查看答案更新
  • 我同意你的看法(关于培训与评估的评论),这就是 .fit() 似乎有效的原因?知道如何让它在我的设置中工作吗?

标签: python tensorflow keras batch-normalization gradienttape


【解决方案1】:

使用渐变胶带模式,您通常会发现如下渐变:

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))

但是,如果您的模型包含 BatchNormalizationDropout 层(或具有不同训练/测试阶段的任何层),则 tf 将无法构建图。

从模型获取输出时,一个好的做法是显式使用trainable 参数。在优化使用 model(features, trainable=True) 和预测使用 model(features, trainable=False) 时,以便在使用此类层时明确选择训练/测试阶段。

对于PREDICTEVAL 阶段,使用

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 函数,同时在调用模型时使用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

【讨论】:

    【解决方案2】:

    使用梯度磁带模式 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)>
    

    【讨论】:

      【解决方案3】:

      我就放弃了。我花了一些时间试图理解一个看起来像这样的模型:

      model = tf.keras.Sequential([
                           tf.keras.layers.BatchNormalization(),
      ])
      

      我确实放弃了,因为那东西看起来像这样:

      我的直觉是,如今的 BatchNorm 不像以前那么直接了,这就是为什么它可以扩展原始发行版,但没有那么多新发行版(这很遗憾),但不是没有人有时间这样做.

      编辑:这种行为的原因是 BN 在训练期间只计算矩和标准化批次。在训练期间,它保持平均值和偏差的运行平均值,一旦切换到评估,参数将用作常数。即评估不应该依赖于规范化,因为评估甚至可以用于单个输入并且不能依赖批量统计。由于常数是在不同的分布上计算的,因此在评估过程中会出现更高的误差。

      【讨论】:

      • :) 感谢您的尝试。顺便说一句,你是如何制作这个图表的?
      • 使用张量板。但我不得不放弃急切的执行,并为此切换回会话
      猜你喜欢
      • 1970-01-01
      • 2020-08-29
      • 2019-11-06
      • 1970-01-01
      • 2021-10-02
      • 2018-02-23
      • 1970-01-01
      • 1970-01-01
      • 2022-10-18
      相关资源
      最近更新 更多