Layer.call 没有急切地执行

Posted

技术标签:

【中文标题】Layer.call 没有急切地执行【英文标题】:Layer.call is not executed eagerly 【发布时间】:2022-01-13 09:37:28 【问题描述】:

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

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

问题:

    为什么要在模型实例化时执行 build 和 call 方法? 如果急切执行是默认执行方法,为什么不急切执行调用方法?

【问题讨论】:

【参考方案1】:

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

【讨论】:

【参考方案2】:

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

默认情况下启用急切执行,并且此 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>

似乎与文档一致。

【讨论】:

你知道吗,为什么在模型描述之后调用 build 和 call。不仅在fit方法之后? 查看文档tensorflow.org/api_docs/python/tf/keras/… 和源代码。在model.compile 期间,模型将被包裹在一个 tf.function 中。 我在编译之前有顺序之后的构建/调用输出。我用的是 Jupyter Notebook。 是的,这里也记录了这种行为:tensorflow.org/api_docs/python/tf/keras/layers/…。可选的build() 方法由层的第一个__call__() 调用。

以上是关于Layer.call 没有急切地执行的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法通过 QueryDSL 中的谓词 API 急切地获取惰性关系?

如何让我的Jersey 2端点在启动时急切地初始化?

LINQ中的First()会导致急切或延迟加载吗?

急切模式下的RunMetadata

GridSearch:必须始终传递“Layer.call”的第一个参数

急切地收集包含其他急切地得到的集合的实体