Layer.call 不会急切地执行



我写了一个层,它什么都不做

class Fractal2D(tf.keras.layers.Layer):
def __init__(self, kernel_size_range):
super(Fractal2D, self).__init__()
self.kernel_size_range = kernel_size_range

def build(self, inputs):
print(f'build executes eagerly: {tf.executing_eagerly()}')
return inputs

def call(self, inputs):
print(f'call executes eagerly: {tf.executing_eagerly()}')
return inputs

并制作了一个模型

model = tf.keras.Sequential([
tf.keras.layers.InputLayer(input_shape=(224, 224, 3), batch_size=32),
Fractal2D(kernel_size_range=(3, 41)),
hub.KerasLayer("https://tfhub.dev/google/tf2-preview/mobilenet_v2/feature_vector/4", output_shape=[1280],
trainable=False),
tf.keras.layers.Dense(DIAGNOSIS_NUMBER, activation='softmax')
])

单元格的输出为

build executes eagerly: True 
call executes eagerly: False

当我训练模型时

model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
model.fit(training_set, validation_data=validation_set, epochs=20)

我得到

Epoch 1/20
call executes eagerly: False
call executes eagerly: False

问题:

  1. 为什么在实例化模型时执行生成和调用方法?
  2. 如果预先执行是默认的执行方法,为什么调用方法不急切执行?

自定义层的call方法会自动用@tf.function修饰,它实质上是在第一次调用时创建一个数据流图,然后在所有后续调用上执行此图。为什么这与你的问题有关?因为根据tf.executing_eagerly()上的文档:

默认情况下启用预先执行,在大多数情况下,此 API 返回 True。但是,在以下用例中,此 API 可能会返回 False。

  • 在 tf.function 内部执行,除非在 tf.init_scope 或 tf.config.run_functions_eagerly(True) 下被调用。
  • 在 tf.dataset 的转换函数中执行。
  • 调用 tf.compat.v1.disable_eager_execution()。

所以让我们试着看看使用tf.init_scope时会发生什么:

import tensorflow_hub as hub
import tensorflow as tf
class Fractal2D(tf.keras.layers.Layer):
def __init__(self, kernel_size_range):
super(Fractal2D, self).__init__()
self.kernel_size_range = kernel_size_range

def build(self, inputs):
print(f'build executes eagerly: {tf.executing_eagerly()}')
return inputs

def call(self, inputs):
with tf.init_scope():
print(f'call executes eagerly: {tf.executing_eagerly()}')
return inputs
model = tf.keras.Sequential([
tf.keras.layers.InputLayer(input_shape=(224, 224, 3), batch_size=1),
Fractal2D(kernel_size_range=(3, 41)),
hub.KerasLayer("https://tfhub.dev/google/tf2-preview/mobilenet_v2/feature_vector/4", output_shape=[1280],
trainable=False),
tf.keras.layers.Dense(1, activation='sigmoid')
])
training_set = tf.random.normal((1, 224, 224, 3))
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
model.fit(training_set, tf.random.normal((1, 1)), epochs=2)
build executes eagerly: True
call executes eagerly: True
Epoch 1/2
call executes eagerly: True
call executes eagerly: True
1/1 [==============================] - 4s 4s/step - loss: 0.2856 - accuracy: 0.0000e+00
Epoch 2/2
1/1 [==============================] - 0s 36ms/step - loss: 0.1641 - accuracy: 0.0000e+00
<keras.callbacks.History at 0x7f8836515710>

似乎与文档一致。

我认为即使您使用自定义层,您也可以运行渴望模式。由于"model.fit()"方法,您的模型在图形模式下运行,要在渴望模式下运行,您必须从头开始编写自己的训练循环,您可以使用 GradientTape。 [1]: https://www.tensorflow.org/guide/keras/customizing_what_happens_in_fit

最新更新