使用 Tensorflow 2 的 Keras 功能 API 时传递 `training=true`

Posted

技术标签:

【中文标题】使用 Tensorflow 2 的 Keras 功能 API 时传递 `training=true`【英文标题】:Passing `training=true` when using Tensorflow 2's Keras Functional API 【发布时间】:2020-03-02 19:34:06 【问题描述】:

在 TF1 中以图形模式操作时,我相信我在使用函数式 API 时需要通过 feeddicts 连接 training=Truetraining=False。在 TF2 中执行此操作的正确方法是什么?

我相信这在使用tf.keras.Sequential 时会自动处理。例如,我不需要在docs的以下示例中指定training

model = tf.keras.Sequential([
    tf.keras.layers.Conv2D(32, 3, activation='relu',
                           kernel_regularizer=tf.keras.regularizers.l2(0.02),
                           input_shape=(28, 28, 1)),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dropout(0.1),
    tf.keras.layers.Dense(64, activation='relu'),
    tf.keras.layers.BatchNormalization(),
    tf.keras.layers.Dense(10, activation='softmax')
])

# Model is the full model w/o custom layers
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

model.fit(train_data, epochs=NUM_EPOCHS)
loss, acc = model.evaluate(test_data)
print("Loss :0.4f, Accuracy :0.4f".format(loss, acc))

我是否也可以假设 keras 在使用功能 api 进行训练时会自动处理这个问题?这是相同的模型,使用函数 api 重写:

inputs = tf.keras.Input(shape=((28,28,1)), name="input_image")
hid = tf.keras.layers.Conv2D(32, 3, activation='relu',
                           kernel_regularizer=tf.keras.regularizers.l2(0.02),
                           input_shape=(28, 28, 1))(inputs)
hid = tf.keras.layers.MaxPooling2D()(hid)
hid = tf.keras.layers.Flatten()(hid)
hid = tf.keras.layers.Dropout(0.1)(hid)
hid = tf.keras.layers.Dense(64, activation='relu')(hid)
hid = tf.keras.layers.BatchNormalization()(hid)
outputs = tf.keras.layers.Dense(10, activation='softmax')(hid)
model_fn = tf.keras.Model(inputs=inputs, outputs=outputs)

# Model is the full model w/o custom layers
model_fn.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

model_fn.fit(train_data, epochs=NUM_EPOCHS)
loss, acc = model_fn.evaluate(test_data)
print("Loss :0.4f, Accuracy :0.4f".format(loss, acc))

我不确定hid = tf.keras.layers.BatchNormalization()(hid) 是否需要为hid = tf.keras.layers.BatchNormalization()(hid, training)

可以在 here 找到这些模型的 colab。

【问题讨论】:

您是否有特定的理由想要控制训练标志,或者您是否在询问是否需要它? 我想我希望能够在model_fn() (tf.keras.Model#call) 上将其设置为正向传递,以便 BatchNormalization 行为正确。我假设我需要对模型进行子类化并明确定义前向传递调用,以便我可以将training 传递给 BN 调用,类似于tensorflow.org/api_docs/python/tf/keras/Model 中的示例。我还想知道在使用model_fn.fit()时是否需要它。 @cosentiyes:你提到了我相信这在使用tf.keras.Sequential时会自动处理。你确定这是真的吗?你有什么可以证明这一点的参考资料吗? 【参考方案1】:

我意识到BatchNormalization 文档 [1] 中存在一个错误,其中 TRAINABLE_ATTRIBUTE_NOTE 实际上并未替换为预期的注释 [2]:

关于在BatchNormalization 层上设置layer.trainable = False 设置layer.trainable = False的意思是冻结图层, 即它的内部状态在训练期间不会改变: 它的可训练权重不会更新 在fit()train_on_batch() 期间,不会运行其状态更新。 通常,这并不一定意味着该层在推理中运行 模式(通常由training 参数控制,可以 调用层时传递)。 “冻结状态”和“推理模式” 是两个独立的概念。

但是,对于BatchNormalization 层,设置 图层上的trainable = False 表示该图层将 随后在推理模式下运行(意味着它将使用 对当前批次进行归一化的移动均值和移动方差, 而不是使用当前批次的均值和方差)。 此行为已在 TensorFlow 2.0 中引入,以便 使layer.trainable = False能够产生最常见的 convnet 微调用例中的预期行为。 请注意:

此行为仅从 TensorFlow 2.0 开始出现。在 1.* 中, 设置layer.trainable = False 会冻结图层,但会 不要将其切换到推理模式。 在包含其他层的模型上设置 trainable 将 递归设置所有内层的trainable值。 如果trainable的值 在模型上调用 compile() 后属性发生更改, 新值对此模型不生效 直到再次调用 compile()

[1]https://www.tensorflow.org/api_docs/python/tf/keras/layers/BatchNormalization?version=stable

[2]https://github.com/tensorflow/tensorflow/blob/r2.0/tensorflow/python/keras/layers/normalization_v2.py#L26-L65

【讨论】:

感谢您的提问和回答,我正在寻找完全相同的内容。 Dropout 层呢?它有点不同,因为它是关于打开(训练)和关闭(推理)。您知道这是默认情况下(以某种方式)处理还是需要您自己处理?【参考方案2】:

至于在使用 Keras 功能 API 时是否必须手动传递 training 标志这一更广泛的问题,official docs 中的这个示例建议您不应该

# ...

x = Dropout(0.5)(x)
outputs = Linear(10)(x)
model = tf.keras.Model(inputs, outputs)

# ...

# You can pass a `training` argument in `__call__`
# (it will get passed down to the Dropout layer).
y = model(tf.ones((2, 16)), training=True)

【讨论】:

【参考方案3】:

批量归一化应用一种变换,使平均输出接近 0,输出标准差接近 1。重要的是,批量归一化在训练和推理期间的工作方式不同。根据kerasdocumentation,

训练期间(即使用fit() 或使用参数training=True 调用层/模型时),层使用当前批次的均值和标准差对其输出进行归一化输入。也就是说,对于每个被归一化的通道,该层返回(batch - mean(batch)) / (var(batch) + epsilon) * gamma + beta,其中:

epsilon 是一个小常量(可配置为构造函数参数的一部分) gamma 是一个学习的缩放因子(初始化为 1),可以通过将 scale=False 传递给构造函数来禁用它。 beta 是一个学习的偏移因子(初始化为 0),可以通过将 center=False 传递给构造函数来禁用它。

推理期间(即当使用evaluate()predict() 或使用参数 training=False(这是默认值)调用层/模型时,层使用它在训练过程中看到的批次的均值和标准差的移动平均值。也就是说,它返回(batch - self.moving_mean) / (self.moving_var + epsilon) * gamma + beta

self.moving_meanself.moving_var 是不可训练的变量,每次在训练模式下调用层时都会更新,如下所示:

moving_mean = moving_mean * momentum + mean(batch) * (1 - momentum) moving_var = moving_var * momentum + var(batch) * (1 - momentum) 因此,该层仅在使用与推理数据具有相似统计数据的数据进行训练后,才会在推理期间对其输入进行归一化。

【讨论】:

以上是关于使用 Tensorflow 2 的 Keras 功能 API 时传递 `training=true`的主要内容,如果未能解决你的问题,请参考以下文章

TensorFlow2.0TensorFlow 2.0高阶API: Keras—使用Keras基于Squential的序列编排模式创建神经网络过程(附带源码)

在没有 Keras 的情况下使用 Tensorflow 2.0 和急切执行

TensorFlow 2.0 Keras:如何为 TensorBoard 编写图像摘要

如何使用 gpu 并行训练 tensorflow.keras 模型? TensorFlow 版本 2.5.0

使用Keras的模型类将Tensorflow 1.x代码迁移到Tensorflow 2.x

Keras TensorFlow 2.0 精华资源