Estoy usando Tensorflow/Keras 2.4.1 y tengo una métrica personalizada (no supervisada) que toma varias de las entradas de mi modelo como parámetros como:
model = build_model() # returns a tf.keras.Model object my_metric = custom_metric(model.output, model.input[0], model.input[1]) model.add_metric(my_metric) [...] model.fit([...]) # training with fit Sin embargo, sucede que custom_metric es muy costoso, por lo que me gustaría que se calcule solo durante la validación. Encontré esta respuesta , pero apenas entiendo cómo puedo adaptar la solución a mi métrica que usa varias entradas del modelo como parámetro, ya que el método update_state no parece flexible.
En mi contexto, ¿hay alguna manera de evitar calcular mi métrica durante el entrenamiento, además de escribir mi propio ciclo de entrenamiento? Además, estoy muy sorprendido de que no podamos especificar de forma nativa a Tensorflow que algunas métricas solo deben calcularse en el momento de la validación, ¿hay alguna razón para eso?
Además, dado que el modelo está entrenado para optimizar la pérdida y que el conjunto de datos de entrenamiento no debe usarse para evaluar un modelo, ni siquiera entiendo por qué, de forma predeterminada, Tensorflow calcula las métricas durante el entrenamiento.
Creo que la solución más simple para calcular una métrica solo en la validación es usar una devolución de llamada personalizada.
aquí definimos nuestra devolución de llamada ficticia:
class MyCustomMetricCallback(tf.keras.callbacks.Callback): def __init__(self, train=None, validation=None): super(MyCustomMetricCallback, self).__init__() self.train = train self.validation = validation def on_epoch_end(self, epoch, logs={}): mse = tf.keras.losses.mean_squared_error if self.train: logs['my_metric_train'] = float('inf') X_train, y_train = self.train[0], self.train[1] y_pred = self.model.predict(X_train) score = mse(y_train, y_pred) logs['my_metric_train'] = np.round(score, 5) if self.validation: logs['my_metric_val'] = float('inf') X_valid, y_valid = self.validation[0], self.validation[1] y_pred = self.model.predict(X_valid) val_score = mse(y_pred, y_valid) logs['my_metric_val'] = np.round(val_score, 5)Dado este modelo ficticio:
def build_model(): inp1 = Input((5,)) inp2 = Input((5,)) out = Concatenate()([inp1, inp2]) out = Dense(1)(out) model = Model([inp1, inp2], out) model.compile(loss='mse', optimizer='adam') return modely estos datos:
X_train1 = np.random.uniform(0,1, (100,5)) X_train2 = np.random.uniform(0,1, (100,5)) y_train = np.random.uniform(0,1, (100,1)) X_val1 = np.random.uniform(0,1, (100,5)) X_val2 = np.random.uniform(0,1, (100,5)) y_val = np.random.uniform(0,1, (100,1))puede usar la devolución de llamada personalizada para calcular la métrica tanto en el entrenamiento como en la validación:
model = build_model() model.fit([X_train1, X_train2], y_train, epochs=10, callbacks=[MyCustomMetricCallback(train=([X_train1, X_train2],y_train), validation=([X_val1, X_val2],y_val))])solo en la validación:
model = build_model() model.fit([X_train1, X_train2], y_train, epochs=10, callbacks=[MyCustomMetricCallback(validation=([X_val1, X_val2],y_val))])solo en tren:
model = build_model() model.fit([X_train1, X_train2], y_train, epochs=10, callbacks=[MyCustomMetricCallback(train=([X_train1, X_train2],y_train))]) recuerde solo que la devolución de llamada evalúa las métricas de una sola vez en los datos, como cualquier métrica/pérdida calculada de forma predeterminada por keras en la validation_data .
aquí está el código de ejecución.
Pude usar learning_phase pero solo en modo de tensor simbólico (gráfico):
Entonces, primero debemos deshabilitar el modo ansioso (esto debe hacerse justo después de importar tensorflow):
import tensorflow as tf tf.compat.v1.disable_eager_execution() Luego puede crear su métrica usando un if simbólico ( backend.switch ):
def metric_graph(in1, in2, out): actual_metric = out * (in1 + in2) return K.switch(K.learning_phase(), tf.zeros((1,)), actual_metric) El método add_metric le pedirá un nombre y un método de agregación, que puede establecer en "mean" .
Entonces, aquí hay un ejemplo:
x1 = numpy.ones((5,3)) x2 = numpy.ones((5,3)) y = 3*numpy.ones((5,1)) vx1 = numpy.ones((5,3)) vx2 = numpy.ones((5,3)) vy = 3*numpy.ones((5,1)) def metric_eager(in1, in2, out): if (K.learning_phase()): return 0 else: return out * (in1 + in2) def metric_graph(in1, in2, out): actual_metric = out * (in1 + in2) return K.switch(K.learning_phase(), tf.zeros((1,)), actual_metric) ins1 = Input((3,)) ins2 = Input((3,)) outs = Concatenate()([ins1, ins2]) outs = Dense(1)(outs) model = Model([ins1, ins2],outs) model.add_metric(metric_graph(ins1, ins2, outs), name='my_metric', aggregation='mean') model.compile(loss='mse', optimizer='adam') model.fit([x1, x2],y, validation_data=([vx1, vx2], vy), epochs=3)