Función de pérdida con derivado en TensorFlow 2
Estoy usando TF2 (2.3.0) NN para aproximar la función y que resuelve el ODE: y'+3y=0
He definido la clase de pérdida de cutsom y la función en la que estoy tratando de diferenciar la única salida con respecto a la única entrada así que la ecuación sostiene, siempre que y_true
es cero:
from tensorflow.keras.losses import Loss
import tensorflow as tf
class CustomLossOde(Loss):
def __init__(self, x, model, name='ode_loss'):
super().__init__(name=name)
self.x = x
self.model = model
def call(self, y_true, y_pred):
with tf.GradientTape() as tape:
tape.watch(self.x)
y_p = self.model(self.x)
dy_dx = tape.gradient(y_p, self.x)
loss = tf.math.reduce_mean(tf.square(dy_dx + 3 * y_pred - y_true))
return loss
pero ejecutando la siguiente NN:
import tensorflow as tf
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Dense
from tensorflow.keras import Input
from custom_loss_ode import CustomLossOde
num_samples = 1024
x_train = 4 * (tf.random.uniform((num_samples, )) - 0.5)
y_train = tf.zeros((num_samples, ))
inputs = Input(shape=(1,))
x = Dense(16, 'tanh')(inputs)
x = Dense(8, 'tanh')(x)
x = Dense(4)(x)
y = Dense(1)(x)
model = Model(inputs=inputs, outputs=y)
loss = CustomLossOde(model.input, model)
model.compile(optimizer=Adam(learning_rate=0.01, beta_1=0.9, beta_2=0.99),loss=loss)
model.run_eagerly = True
model.fit(x_train, y_train, batch_size=16, epochs=30)
por ahora estoy recibiendo 0 pérdida de la época de la hierba, que no tiene ningún sentido.
He impreso ambos y_true
y y_test
desde dentro de la función y parecen estar bien así que sospecho que el problema está en el gradien que no tuve éxito en imprimir.
Apreciar cualquier ayuda
Pregunta hecha hace 3 años, 5 meses, 22 días - Por debugdynamo46a0
3 Respuestas:
-
Definir una pérdida personalizada con la API de Keras de alto nivel es un poco difícil en ese caso. Escribiría el bucle de entrenamiento de scracth, ya que permite un control de grano fino sobre lo que puedes hacer.
Me inspiré en esos dos guías:
Básicamente, usé el hecho de que múltiples cintas pueden interactuar perfectamente. Uso uno para calcular la función de pérdida, el otro para calcular los gradientes para ser propagado por el optimizador.
import tensorflow as tf from tensorflow.keras.optimizers import Adam from tensorflow.keras.models import Model from tensorflow.keras.layers import Dense from tensorflow.keras import Input num_samples = 1024 x_train = 4 * (tf.random.uniform((num_samples, )) - 0.5) y_train = tf.zeros((num_samples, )) inputs = Input(shape=(1,)) x = Dense(16, 'tanh')(inputs) x = Dense(8, 'tanh')(x) x = Dense(4)(x) y = Dense(1)(x) model = Model(inputs=inputs, outputs=y) # using the high level tf.data API for data handling x_train = tf.reshape(x_train,(-1,1)) dataset = tf.data.Dataset.from_tensor_slices((x_train,y_train)).batch(1) opt = Adam(learning_rate=0.01, beta_1=0.9, beta_2=0.99) for step, (x,y_true) in enumerate(dataset): # we need to convert x to a variable if we want the tape to be # able to compute the gradient according to x x_variable = tf.Variable(x) with tf.GradientTape() as model_tape: with tf.GradientTape() as loss_tape: loss_tape.watch(x_variable) y_pred = model(x_variable) dy_dx = loss_tape.gradient(y_pred, x_variable) loss = tf.math.reduce_mean(tf.square(dy_dx + 3 * y_pred - y_true)) grad = model_tape.gradient(loss, model.trainable_variables) opt.apply_gradients(zip(grad, model.trainable_variables)) if step%20==0: print(f"Step {step}: loss={loss.numpy()}")
Respondida el Nov 24, 2020 a las 22:28 - por geekglitcher
Votos positivos: 0 | Votos negativos: 0 -
El problema se debe a cómo estás calculando la derivada en tu función de pérdida
CustomLossOde
. El error es que estás calculando la derivada con respecto alself.x
, que es un tensor constante (los valores de entrada a tu modelo), en lugar de con respecto ay_pred
, que es la salida de tu modelo (y que quieres que se ajuste para que cumpla con la ecuación diferencial).Aquí te dejo una posible solución a tu problema:
python from tensorflow.keras.losses import Loss import tensorflow as tf class CustomLossOde(Loss): def __init__(self, model, name='ode_loss'): super().__init__(name=name) self.model = model def call(self, y_true, y_pred): with tf.GradientTape() as tape: tape.watch(y_pred) y_p = self.model(y_pred) dy_dx = tape.gradient(y_p, y_pred) loss = tf.math.reduce_mean(tf.square(dy_dx + 3 y_pred - y_true)) return loss
Y para usar esta
CustomLossOde
con la red neuronal, debes ajustar la creación de la función de pérdida en la compilación del modelo:python import tensorflow as tf from tensorflow.keras.optimizers import Adam from tensorflow.keras.models import Model from tensorflow.keras.layers import Dense from tensorflow.keras import Input from custom_loss_ode import CustomLossOde num_samples = 1024 x_train = 4 (tf.random.uniform((num_samples, )) - 0.5) y_train = tf.zeros((num_samples, )) inputs = Input(shape=(1,)) x = Dense(16, 'tanh')(inputs) x = Dense(8, 'tanh')(x) x = Dense(4)(x) y = Dense(1)(x) model = Model(inputs=inputs, outputs=y) loss = CustomLossOde(model) model.compile(optimizer=Adam(learning_rate=0.01, beta_1=0.9, beta_2=0.99),loss=loss) model.run_eagerly = True model.fit(x_train, y_train, batch_size=16, epochs=30)
Con estos ajustes, estás calculando la derivada correcta con respecto a la salida del modelo, lo que debería permitir que la red neuronal aprenda a ajustar los parámetros para resolver la ecuación diferencial.
Respondida el Nov 25, 2020 a las 22:29 - por Gpt
Votos positivos: 0 | Votos negativos: 0 -
He revisado tu código y he identificado algunos problemas que podrían estar causando que experimentes una pérdida de cero:
- Gradiente no calculado correctamente: En tu función de pérdida, estás calculando el gradiente de `y_p` con respecto a `self.x`, pero no estás utilizando este gradiente en el cálculo de la pérdida. Deberías utilizar `dy_dx` para calcular la pérdida como sigue:
python loss = tf.math.reduce_mean(tf.square(dy_dx + 3 y_pred - y_true))
- Uso de `run_eagerly` innecesario: No es necesario establecer `model.run_eagerly = True` para que la función de pérdida funcione correctamente. Puedes eliminar esta línea.
- Tamaño de lote pequeño: Estás utilizando un tamaño de lote de 16 para el entrenamiento, lo cual es bastante pequeño. Puedes intentar aumentar el tamaño de lote para mejorar la estabilidad del entrenamiento.
- Función de activación no apropiada: Estás utilizando la función de activación tangente hiperbólica (`tanh`) en todas las capas ocultas de tu modelo. Esta función de activación puede saturar los valores de salida, lo que dificulta que el modelo aprenda la función objetivo. Puedes intentar utilizar una función de activación diferente, como ReLU.
Aquí tienes el código corregido con los cambios sugeridos:
python import tensorflow as tf from tensorflow.keras.optimizers import Adam from tensorflow.keras.models import Model from tensorflow.keras.layers import Dense from tensorflow.keras import Input from custom_loss_ode import CustomLossOde num_samples = 1024 x_train = 4 (tf.random.uniform((num_samples, )) - 0.5) y_train = tf.zeros((num_samples, )) inputs = Input(shape=(1,)) x = Dense(16, 'relu')(inputs) x = Dense(8, 'relu')(x) x = Dense(4, 'relu')(x) y = Dense(1)(x) model = Model(inputs=inputs, outputs=y) loss = CustomLossOde(model.input, model) model.compile(optimizer=Adam(learning_rate=0.01, beta_1=0.9, beta_2=0.99),loss=loss) model.fit(x_train, y_train, batch_size=128, epochs=30)
Además, puedes intentar utilizar un optimizador diferente, como SGD, y experimentar con diferentes tasas de aprendizaje. También puedes intentar añadir regularización a tu modelo para evitar el sobreajuste.
Respondida el Nov 25, 2020 a las 22:36 - por Gemini
Votos positivos: 0 | Votos negativos: 0