Добавление пользовательского metri c API подклассов Keras - PullRequest
0 голосов
/ 12 марта 2020

Я слежу за разделом «Потери и метрики на основе внутренних данных модели» главы 12 «Практического машинного обучения с Scikit-Learn, Keras и TensorFlow, 2nd Edition - Aurélien Geron», в которой он показывает, как добавить пользовательские потери и метрики, которые не зависят от меток и прогнозов.

Чтобы проиллюстрировать это, мы добавляем пользовательские «потери при реконструкции», добавляя слой поверх верхнего скрытого слоя, который должен воспроизводить ввод. Потеря - это среднеквадратическая разница между потерями на реконструкцию и входными данными.

Он показывает код для добавления нестандартной потери, который хорошо работает, но даже после его описания я не могу добавить метри c, так как он вызывает `ValueError". Он говорит:

Аналогично, вы можете добавить пользовательский показатель c на основе внутренних данных модели, вычисляя его любым желаемым способом, если в результате получается вывод объекта metri c. Например, вы можете создать объект keras.metrics.Mean в конструкторе, затем вызвать его в методе call(), передав ему recon_loss, и, наконец, добавить его в модель, вызвав метод add_metric() модели.

Это код (я добавил #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')`

Прочитав это, я пробовал похожие вещи, чтобы решить эту проблему, но это просто привело к различным ошибкам. Как я могу решить эту проблему? "правильный путь для этого?

Я использую conda на Windows, с установленным тензор-gpu 2.1.0.

1 Ответ

0 голосов
/ 13 марта 2020

Проблема как раз здесь:

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_metri c получает только метри c, полученную из tf.keras.metrics.Metric, но 0 это скаляр, а не метри c type.

Мое предлагаемое решение состоит в том, чтобы просто сделать это:

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)

Таким образом, ваше среднее значение evolution_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)

PS - обратите внимание, что при вызове 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). Удачи!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...