使用 Tensorflow 2 的 Keras Functional 时传递 `training=true` API
Passing `training=true` when using Tensorflow 2's Keras Functional API
在 TF1 中以图形模式运行时,我认为我在使用函数式 API 时需要通过 feeddicts 连接 training=True
和 training=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)
?
可以找到这些模型的 Colab here。
我意识到 BatchNormalization
文档 [1] 中存在错误,其中 {{TRAINABLE_ATTRIBUTE_NOTE}}
实际上并未替换为预期的注释 [2]:
关于在BatchNormalization
层上设置layer.trainable = False
:
设置layer.trainable = False
的意思是冻结图层,
即它的内部状态在训练期间不会改变:
它的可训练权重不会更新
在 fit()
或 train_on_batch()
期间,其状态更新不会 运行。
通常,这并不一定意味着该层在推理中是 运行
模式(通常由 training
参数控制
在调用层时被传递)。 "Frozen state" 和 "inference mode"
是两个不同的概念。
然而,在BatchNormalization
层的情况下,设置
trainable = False
on the layer 表示该层将
随后 运行 在推理模式 (意味着它将使用
移动均值和移动方差以归一化当前批次,
而不是使用当前批次的均值和方差)。
此行为已在 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
至于使用 Keras Functional 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)
批量归一化 应用一种转换,使平均输出保持接近 0,输出标准差接近 1。重要的是,批量归一化在训练和推理期间的工作方式不同。根据 keras documentation,
在训练期间(即当使用fit()
或使用参数training=True
调用layer/model时),该层对其输出进行归一化使用当前批次输入的均值和标准差。也就是说,对于每个被归一化的通道,层 returns (batch - mean(batch)) / (var(batch) + epsilon) * gamma + beta
,其中:
epsilon
是一个小常量(可配置为构造函数参数的一部分)
gamma
是学习的缩放因子(初始化为 1),可以通过将 scale=False
传递给构造函数来禁用它。
beta
是学习的偏移因子(初始化为0),可以通过将center=False
传递给构造函数来禁用它。
在推理过程中(即当使用 evaluate()
或 predict()
或使用参数 training=False 调用 layer/model 时(即默认值),该层使用它在训练期间看到的批次的均值和标准差的移动平均值对其输出进行归一化。也就是说,它 returns (batch - self.moving_mean) / (self.moving_var + epsilon) * gamma + beta
.
self.moving_mean
和 self.moving_var
是 non-trainable 变量,每次在训练模式下调用层时都会更新,因此:
moving_mean = moving_mean * momentum + mean(batch) * (1 - momentum)
moving_var = moving_var * momentum + var(batch) * (1 - momentum)
因此,该层仅在对与推理数据具有相似统计数据的数据进行训练后,才会在推理期间对其输入进行归一化。
在 TF1 中以图形模式运行时,我认为我在使用函数式 API 时需要通过 feeddicts 连接 training=True
和 training=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)
?
可以找到这些模型的 Colab here。
我意识到 BatchNormalization
文档 [1] 中存在错误,其中 {{TRAINABLE_ATTRIBUTE_NOTE}}
实际上并未替换为预期的注释 [2]:
关于在BatchNormalization
层上设置layer.trainable = False
:
设置layer.trainable = False
的意思是冻结图层,
即它的内部状态在训练期间不会改变:
它的可训练权重不会更新
在 fit()
或 train_on_batch()
期间,其状态更新不会 运行。
通常,这并不一定意味着该层在推理中是 运行
模式(通常由 training
参数控制
在调用层时被传递)。 "Frozen state" 和 "inference mode"
是两个不同的概念。
然而,在BatchNormalization
层的情况下,设置
trainable = False
on the layer 表示该层将
随后 运行 在推理模式 (意味着它将使用
移动均值和移动方差以归一化当前批次,
而不是使用当前批次的均值和方差)。
此行为已在 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
至于使用 Keras Functional 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)
批量归一化 应用一种转换,使平均输出保持接近 0,输出标准差接近 1。重要的是,批量归一化在训练和推理期间的工作方式不同。根据 keras documentation,
在训练期间(即当使用fit()
或使用参数training=True
调用layer/model时),该层对其输出进行归一化使用当前批次输入的均值和标准差。也就是说,对于每个被归一化的通道,层 returns (batch - mean(batch)) / (var(batch) + epsilon) * gamma + beta
,其中:
epsilon
是一个小常量(可配置为构造函数参数的一部分)gamma
是学习的缩放因子(初始化为 1),可以通过将scale=False
传递给构造函数来禁用它。beta
是学习的偏移因子(初始化为0),可以通过将center=False
传递给构造函数来禁用它。
在推理过程中(即当使用 evaluate()
或 predict()
或使用参数 training=False 调用 layer/model 时(即默认值),该层使用它在训练期间看到的批次的均值和标准差的移动平均值对其输出进行归一化。也就是说,它 returns (batch - self.moving_mean) / (self.moving_var + epsilon) * gamma + beta
.
self.moving_mean
和 self.moving_var
是 non-trainable 变量,每次在训练模式下调用层时都会更新,因此:
moving_mean = moving_mean * momentum + mean(batch) * (1 - momentum)
moving_var = moving_var * momentum + var(batch) * (1 - momentum)
因此,该层仅在对与推理数据具有相似统计数据的数据进行训练后,才会在推理期间对其输入进行归一化。