Call 函数何时以及如何在 Keras 的模型子类化中工作?
When and How the Call function work in Model Subclassing of Keras?
我阅读了 Hands-on Machine Learning with Scikit-Learn, Keras, and Tensorflow 关于使用子分类 API 构建动态模型,主要涉及编写一个包含两个方法的子类:构造函数和调用函数。构造函数相当容易理解。但是,在构建模型时,我无法理解调用函数何时以及如何准确工作。
我使用了书中的代码并进行了如下实验(使用来自 sklearn 的加州住房数据集):
class WideAndDeepModel(keras.Model):
def __init__(self, units=30, activation='relu', **kwargs):
super().__init__(**kwargs)
self.hidden1 = keras.layers.Dense(units, activation=activation)
self.hidden2 = keras.layers.Dense(units, activation=activation)
self.main_output = keras.layers.Dense(1)
self.aux_output = keras.layers.Dense(1)
def call(self, inputs):
print('call function running')
input_A, input_B = inputs
hidden1 = self.hidden1(input_B)
hidden2 = self.hidden2(hidden1)
concat = keras.layers.concatenate([input_A, hidden2])
main_output = self.main_output(concat)
aux_output = self.aux_output(hidden2)
return main_output, aux_output
model = WideAndDeepModel()
model.compile(loss=['mse','mse'], loss_weights=[0.9,0.1], optimizer='sgd')
history = model.fit([X_train_A, X_train_B],[y_train, y_train], epochs=20, validation_data=([X_val_A, X_val_B], [y_val, y_val]))
下面是训练过程中的输出:
Epoch 1/20
***call function running***
***call function running***
353/363 [============================>.] - ETA: 0s - loss: 1.6398 - output_1_loss: 1.5468 - output_2_loss: 2.4769
***call function running***
363/363 [==============================] - 1s 1ms/step - loss: 1.6224 - output_1_loss: 1.5296 - output_2_loss: 2.4571 - val_loss: 4.3588 - val_output_1_loss: 4.7174 - val_output_2_loss: 1.1308
Epoch 2/20
363/363 [==============================] - 0s 1ms/step - loss: 0.6073 - output_1_loss: 0.5492 - output_2_loss: 1.1297 - val_loss: 75.1126 - val_output_1_loss: 81.6632 - val_output_2_loss: 16.1572
...
调用函数在第一个时期的训练开始时两次获得 运行,然后几乎在第一个时期结束时获得 运行。之后就再也没有运行了。
在我看来,虽然层是在构造函数的早期实例化的,但层之间的连接(在调用函数中定义)建立得相当晚(在训练开始时)。在我看来,层与层之间没有这种所谓的连接的逻辑实体,连接只是按特定顺序将一层的输出传递到另一层的过程。我的理解正确吗?
第二个问题是为什么call函数在训练初期会运行三次而不是一次
the layers are instantiated early in the constructor function
正确
the connection between the layers are established quite late
同样正确,当你调用 model.build()
或第一次调用模型时,权重会被初始化,正如你在这个 guide 中看到的对 Keras 层的子类化:
class Linear(keras.layers.Layer):
def __init__(self, units=32):
super(Linear, self).__init__()
self.units = units
def build(self, input_shape):
self.w = self.add_weight(
shape=(input_shape[-1], self.units),
initializer="random_normal",
trainable=True,
)
self.b = self.add_weight(
shape=(self.units,), initializer="random_normal", trainable=True
)
def call(self, inputs):
return tf.matmul(inputs, self.w) + self.b
why the call function gets run three times at the early stage
第一次大概是第一次调用模型的时候,实例化了权重。然后另一次构建 Tensorflow 图,它是非 Python 代码而不是运行 Tensorflow 模型。该模型被调用一次以创建此图形,进一步的调用在 Python 之外,因此您的打印功能不再是其中的一部分。您可以使用 model.compile(..., run_eagerly=True)
更改此行为。最后第三次是第一次通过验证数据
首先请注意,使用 python print 语句在使用 model.compile()
时不是一个好主意,并且 model.fit()
不是调试模型的好主意,因为 tensorflow 使用 C++至 运行 训练更快且并行,此打印语句将被省略。
但是让我回到你的问题。值得一提的是,TensorFlow 和 Keras 模型具有惰性行为,这意味着当您实例化模型时 model = WideAndDeepModel()
,模型权重尚未创建,无论您调用 model.call()
第一次还是model.build()
方法。因此,您的模型似乎已在 python 中被调用一次用于创建模型权重,一次用于启动训练过程和构建 C++ 对象(图形),一次用于启动验证。之后所有计算都将在 C++ 中执行,您看不到任何打印语句。
注意:如果你想在图形模式下打印一些东西,你可以使用tf.print
我阅读了 Hands-on Machine Learning with Scikit-Learn, Keras, and Tensorflow 关于使用子分类 API 构建动态模型,主要涉及编写一个包含两个方法的子类:构造函数和调用函数。构造函数相当容易理解。但是,在构建模型时,我无法理解调用函数何时以及如何准确工作。
我使用了书中的代码并进行了如下实验(使用来自 sklearn 的加州住房数据集):
class WideAndDeepModel(keras.Model):
def __init__(self, units=30, activation='relu', **kwargs):
super().__init__(**kwargs)
self.hidden1 = keras.layers.Dense(units, activation=activation)
self.hidden2 = keras.layers.Dense(units, activation=activation)
self.main_output = keras.layers.Dense(1)
self.aux_output = keras.layers.Dense(1)
def call(self, inputs):
print('call function running')
input_A, input_B = inputs
hidden1 = self.hidden1(input_B)
hidden2 = self.hidden2(hidden1)
concat = keras.layers.concatenate([input_A, hidden2])
main_output = self.main_output(concat)
aux_output = self.aux_output(hidden2)
return main_output, aux_output
model = WideAndDeepModel()
model.compile(loss=['mse','mse'], loss_weights=[0.9,0.1], optimizer='sgd')
history = model.fit([X_train_A, X_train_B],[y_train, y_train], epochs=20, validation_data=([X_val_A, X_val_B], [y_val, y_val]))
下面是训练过程中的输出:
Epoch 1/20
***call function running***
***call function running***
353/363 [============================>.] - ETA: 0s - loss: 1.6398 - output_1_loss: 1.5468 - output_2_loss: 2.4769
***call function running***
363/363 [==============================] - 1s 1ms/step - loss: 1.6224 - output_1_loss: 1.5296 - output_2_loss: 2.4571 - val_loss: 4.3588 - val_output_1_loss: 4.7174 - val_output_2_loss: 1.1308
Epoch 2/20
363/363 [==============================] - 0s 1ms/step - loss: 0.6073 - output_1_loss: 0.5492 - output_2_loss: 1.1297 - val_loss: 75.1126 - val_output_1_loss: 81.6632 - val_output_2_loss: 16.1572
...
调用函数在第一个时期的训练开始时两次获得 运行,然后几乎在第一个时期结束时获得 运行。之后就再也没有运行了。
在我看来,虽然层是在构造函数的早期实例化的,但层之间的连接(在调用函数中定义)建立得相当晚(在训练开始时)。在我看来,层与层之间没有这种所谓的连接的逻辑实体,连接只是按特定顺序将一层的输出传递到另一层的过程。我的理解正确吗?
第二个问题是为什么call函数在训练初期会运行三次而不是一次
the layers are instantiated early in the constructor function
正确
the connection between the layers are established quite late
同样正确,当你调用 model.build()
或第一次调用模型时,权重会被初始化,正如你在这个 guide 中看到的对 Keras 层的子类化:
class Linear(keras.layers.Layer):
def __init__(self, units=32):
super(Linear, self).__init__()
self.units = units
def build(self, input_shape):
self.w = self.add_weight(
shape=(input_shape[-1], self.units),
initializer="random_normal",
trainable=True,
)
self.b = self.add_weight(
shape=(self.units,), initializer="random_normal", trainable=True
)
def call(self, inputs):
return tf.matmul(inputs, self.w) + self.b
why the call function gets run three times at the early stage
第一次大概是第一次调用模型的时候,实例化了权重。然后另一次构建 Tensorflow 图,它是非 Python 代码而不是运行 Tensorflow 模型。该模型被调用一次以创建此图形,进一步的调用在 Python 之外,因此您的打印功能不再是其中的一部分。您可以使用 model.compile(..., run_eagerly=True)
更改此行为。最后第三次是第一次通过验证数据
首先请注意,使用 python print 语句在使用 model.compile()
时不是一个好主意,并且 model.fit()
不是调试模型的好主意,因为 tensorflow 使用 C++至 运行 训练更快且并行,此打印语句将被省略。
但是让我回到你的问题。值得一提的是,TensorFlow 和 Keras 模型具有惰性行为,这意味着当您实例化模型时 model = WideAndDeepModel()
,模型权重尚未创建,无论您调用 model.call()
第一次还是model.build()
方法。因此,您的模型似乎已在 python 中被调用一次用于创建模型权重,一次用于启动训练过程和构建 C++ 对象(图形),一次用于启动验证。之后所有计算都将在 C++ 中执行,您看不到任何打印语句。
注意:如果你想在图形模式下打印一些东西,你可以使用tf.print