添加自定义指标 Keras 子类化 API
Adding custom metric Keras Subclassing API
我正在关注 "Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow, 2nd Edition - Aurélien Geron" 第 12 章的 "Losses and Metrics Based on Model Internals" 部分,他在其中展示了如何添加不依赖于标签和预测的自定义损失和指标。
为了说明这一点,我们添加了一个自定义 "reconstruction loss",方法是在应该重现输入的上层隐藏层之上添加一个层。损失是重建损失和输入之间的均方差。
他展示了添加自定义损失的代码,效果很好,但即使按照他的描述我也无法添加指标,因为它会引发“ValueError”。他说:
Similarly, you can add a custom metric based on model internals by
computing it in any way you want, as long as the result is the output of a
metric object. For example, you can create a keras.metrics.Mean
object
in the constructor, then call it in the call()
method, passing it the
recon_loss
, and finally add it to the model by calling the model’s
add_metric()
method.
这是代码(我已经为我自己添加的行添加了#MINE)
import tensorflow as tf
from tensorflow import keras
class ReconstructingRegressor(keras.models.Model):
def __init__(self, output_dim, **kwargs):
super().__init__(**kwargs)
self.hidden = [keras.layers.Dense(30, activation="selu",
kernel_initializer="lecun_normal")
for _ in range(5)]
self.out = keras.layers.Dense(output_dim)
self.reconstruction_mean = keras.metrics.Mean(name="reconstruction_error") #MINE
def build(self, batch_input_shape):
n_inputs = batch_input_shape[-1]
self.reconstruct = keras.layers.Dense(n_inputs)
super().build(batch_input_shape)
def call(self, inputs, training=None):
Z = inputs
for layer in self.hidden:
Z = layer(Z)
reconstruction = self.reconstruct(Z)
recon_loss = tf.reduce_mean(tf.square(reconstruction - inputs))
self.add_loss(0.05 * recon_loss)
if training: #MINE
result = self.reconstruction_mean(recon_loss) #MINE
else: #MINE
result = 0. #MINE, I have also tried different things here,
#but the help showed a similar sample to this.
self.add_metric(result, name="foo") #MINE
return self.out(Z)
然后编译和拟合模型:
training_set_size=10
X_dummy = np.random.randn(training_set_size, 8)
y_dummy = np.random.randn(training_set_size, 1)
model = ReconstructingRegressor(1)
model.compile(loss="mse", optimizer="nadam")
history = model.fit(X_dummy, y_dummy, epochs=2)
抛出:
ValueError: in converted code:
<ipython-input-296-878bdeb30546>:26 call *
self.add_metric(result, name="foo") #MINE
C:\Users\Kique\Anaconda3\envs\piz3\lib\site-packages\tensorflow_core\python\keras\engine\base_layer.py:1147 add_metric
self._symbolic_add_metric(value, aggregation, name)
C:\Users\Kique\Anaconda3\envs\piz3\lib\site-packages\tensorflow_core\python\keras\engine\base_layer.py:1867 _symbolic_add_metric
'We do not support adding an aggregated metric result tensor that '
ValueError: We do not support adding an aggregated metric result tensor that is not the output of a `tf.keras.metrics.Metric` metric instance. Without having access to the metric instance we cannot reset the state of a metric after every epoch during training. You can create a `tf.keras.metrics.Metric` instance and pass the result here or pass an un-aggregated result with `aggregation` parameter set as `mean`. For example: `self.add_metric(tf.reduce_sum(inputs), name='mean_activation', aggregation='mean')`
读完之后,我尝试了类似的方法来解决这个问题,但它只是导致了不同的错误。我该如何解决这个问题? "correct" 的方法是什么?
我在 Windows 上使用 conda,安装了 tensorflow-gpu 2.1.0。
问题就在这里:
def call(self, inputs, training=None):
Z = inputs
for layer in self.hidden:
Z = layer(Z)
reconstruction = self.reconstruct(Z)
recon_loss = tf.reduce_mean(tf.square(reconstruction - inputs))
self.add_loss(0.05 * recon_loss)
if training:
result = self.reconstruction_mean(recon_loss)
else:
result = 0.#<---Here!
self.add_metric(result, name="foo")
return self.out(Z)
错误表明 add_metric 仅获取从 tf.keras.metrics.Metric
派生的度量,但 0 是标量,而不是度量类型。
我建议的解决方案是简单地这样做:
def call(self, inputs, training=None):
Z = inputs
for layer in self.hidden:
Z = layer(Z)
reconstruction = self.reconstruct(Z)
recon_loss = tf.reduce_mean(tf.square(reconstruction - inputs))
self.add_loss(0.05 * recon_loss)
if training:
result = self.reconstruction_mean(recon_loss)
self.add_metric(result, name="foo")
return self.out(Z)
这样,您的平均值 reconstruction_error 将仅在训练时间显示。
由于您使用的是急切模式,因此您应该使用 dynamic=True
创建图层,如下所示:
model = ReconstructingRegressor(1,dynamic=True)
model.compile(loss="mse", optimizer="nadam")
history = model.fit(X_dummy, y_dummy, epochs=2, batch_size=10)
P.S - 注意,在调用 model.fit or model.evaluate
时,您还应该确保批量大小划分您的训练集(因为这是一个有状态的网络)。因此,像这样调用这些函数:model.fit(X_dummy, y_dummy, epochs=2, batch_size=10)
或 model.evaluate(X_dummy,y_dummy, batch_size=10)
。
祝你好运!
我正在关注 "Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow, 2nd Edition - Aurélien Geron" 第 12 章的 "Losses and Metrics Based on Model Internals" 部分,他在其中展示了如何添加不依赖于标签和预测的自定义损失和指标。
为了说明这一点,我们添加了一个自定义 "reconstruction loss",方法是在应该重现输入的上层隐藏层之上添加一个层。损失是重建损失和输入之间的均方差。
他展示了添加自定义损失的代码,效果很好,但即使按照他的描述我也无法添加指标,因为它会引发“ValueError”。他说:
Similarly, you can add a custom metric based on model internals by computing it in any way you want, as long as the result is the output of a metric object. For example, you can create a
keras.metrics.Mean
object in the constructor, then call it in thecall()
method, passing it therecon_loss
, and finally add it to the model by calling the model’sadd_metric()
method.
这是代码(我已经为我自己添加的行添加了#MINE)
import tensorflow as tf
from tensorflow import keras
class ReconstructingRegressor(keras.models.Model):
def __init__(self, output_dim, **kwargs):
super().__init__(**kwargs)
self.hidden = [keras.layers.Dense(30, activation="selu",
kernel_initializer="lecun_normal")
for _ in range(5)]
self.out = keras.layers.Dense(output_dim)
self.reconstruction_mean = keras.metrics.Mean(name="reconstruction_error") #MINE
def build(self, batch_input_shape):
n_inputs = batch_input_shape[-1]
self.reconstruct = keras.layers.Dense(n_inputs)
super().build(batch_input_shape)
def call(self, inputs, training=None):
Z = inputs
for layer in self.hidden:
Z = layer(Z)
reconstruction = self.reconstruct(Z)
recon_loss = tf.reduce_mean(tf.square(reconstruction - inputs))
self.add_loss(0.05 * recon_loss)
if training: #MINE
result = self.reconstruction_mean(recon_loss) #MINE
else: #MINE
result = 0. #MINE, I have also tried different things here,
#but the help showed a similar sample to this.
self.add_metric(result, name="foo") #MINE
return self.out(Z)
然后编译和拟合模型:
training_set_size=10
X_dummy = np.random.randn(training_set_size, 8)
y_dummy = np.random.randn(training_set_size, 1)
model = ReconstructingRegressor(1)
model.compile(loss="mse", optimizer="nadam")
history = model.fit(X_dummy, y_dummy, epochs=2)
抛出:
ValueError: in converted code:
<ipython-input-296-878bdeb30546>:26 call *
self.add_metric(result, name="foo") #MINE
C:\Users\Kique\Anaconda3\envs\piz3\lib\site-packages\tensorflow_core\python\keras\engine\base_layer.py:1147 add_metric
self._symbolic_add_metric(value, aggregation, name)
C:\Users\Kique\Anaconda3\envs\piz3\lib\site-packages\tensorflow_core\python\keras\engine\base_layer.py:1867 _symbolic_add_metric
'We do not support adding an aggregated metric result tensor that '
ValueError: We do not support adding an aggregated metric result tensor that is not the output of a `tf.keras.metrics.Metric` metric instance. Without having access to the metric instance we cannot reset the state of a metric after every epoch during training. You can create a `tf.keras.metrics.Metric` instance and pass the result here or pass an un-aggregated result with `aggregation` parameter set as `mean`. For example: `self.add_metric(tf.reduce_sum(inputs), name='mean_activation', aggregation='mean')`
读完之后,我尝试了类似的方法来解决这个问题,但它只是导致了不同的错误。我该如何解决这个问题? "correct" 的方法是什么?
我在 Windows 上使用 conda,安装了 tensorflow-gpu 2.1.0。
问题就在这里:
def call(self, inputs, training=None):
Z = inputs
for layer in self.hidden:
Z = layer(Z)
reconstruction = self.reconstruct(Z)
recon_loss = tf.reduce_mean(tf.square(reconstruction - inputs))
self.add_loss(0.05 * recon_loss)
if training:
result = self.reconstruction_mean(recon_loss)
else:
result = 0.#<---Here!
self.add_metric(result, name="foo")
return self.out(Z)
错误表明 add_metric 仅获取从 tf.keras.metrics.Metric
派生的度量,但 0 是标量,而不是度量类型。
我建议的解决方案是简单地这样做:
def call(self, inputs, training=None):
Z = inputs
for layer in self.hidden:
Z = layer(Z)
reconstruction = self.reconstruct(Z)
recon_loss = tf.reduce_mean(tf.square(reconstruction - inputs))
self.add_loss(0.05 * recon_loss)
if training:
result = self.reconstruction_mean(recon_loss)
self.add_metric(result, name="foo")
return self.out(Z)
这样,您的平均值 reconstruction_error 将仅在训练时间显示。
由于您使用的是急切模式,因此您应该使用 dynamic=True
创建图层,如下所示:
model = ReconstructingRegressor(1,dynamic=True)
model.compile(loss="mse", optimizer="nadam")
history = model.fit(X_dummy, y_dummy, epochs=2, batch_size=10)
P.S - 注意,在调用 model.fit or model.evaluate
时,您还应该确保批量大小划分您的训练集(因为这是一个有状态的网络)。因此,像这样调用这些函数:model.fit(X_dummy, y_dummy, epochs=2, batch_size=10)
或 model.evaluate(X_dummy,y_dummy, batch_size=10)
。
祝你好运!