Primeros experimentos: inversión lineal en TensorFlow

0
1045

Os recuerdo que este tutorial forma parte de una colección de apuntes personales que estoy tomando de auto-formación en aprendizaje automático (no es una conclusión sino un camino).

  1. Instalación de TensorFlow y entorno de desarrollo Python en Mac.
  2. Revisión de ejemplos de TensorFlow.
  3. Principios de aprendizaje automático supervisado.
  4. Primeros pasos con Python: los tipos de datos básicos.
  5. Entendiendo un modelo de regresión lineal con TensorBoard.

En el anterior tutorial os presentaba cómo funciona TensorFlow y TensorBoard a través de un ejemplo muy simple de regresión lineal.

Ahora es el momento de empezar a complicar un poquito la cosa y hacer mis propios ejemplos para sacar conclusiones propias (y os quedaré agradecidos por la crítica constructiva).

Os recuerdo que partiremos de este tutorial y que no tenéis que instalar nada para poder utilizar estos ejemplos: Simplemente, ir a Colab y partiendo del ejemplo base, ir cambiando lo que os interese.

Vamos a mostrar poco a poco el código. Lo primero que hacemos es inicializar TensorFlow y TensorBoard. Esto no tiene nada de particular.

Inicializar TensorFlow y TensorBoard

Y ahora vamos a hacer una pequeña variación. En vez de generar datos de una recta vamos a generar dos rectas, con pendientes distintas, y ver cómo se comporta en modelo de entrenamiento, para posteriormente tratar de realizar modificaciones y conseguir que las predicciones cuadren con lo esperado.

data_size = 1000
train_pct = 0.8
train_size = int(data_size * train_pct)
# Creamos un espacio lineal de datos entre -3 y 3
x = np.linspace(-3, 3, data_size)

#creamos una array vacio donde asignar los calores de Y
y = np.array([])

#desordenamos los valores de X para que la muestra tenga de las dos gráficas no consecutivas
np.random.shuffle(x)

from numpy import random
valor = 0

#Si el valor de X es 1 usamos una ecuación para Y y sino usamos otra
for elemento in x: 
  ruido = random.uniform(0, 0.05)

  if elemento <= 1:
       valor = 0.5 * elemento + 2 + ruido
  else:
       valor = 2 * elemento + 4 + ruido

  y = np.append (y,valor)

# Generamos los juegos de datos de entrenamiento y prueba
x_train, y_train = x[:train_size], y[:train_size]
x_test, y_test = x[train_size:], y[train_size:]

# Importamos la librería de gráficos 
import matplotlib.pyplot as plt

#plt.hist (y_test)

# Mostramos los datos 
plt.scatter(x_train,y_train)
plt.title('Datos de entrenamiento') 
plt.grid(alpha =.9, linestyle ='--') 
plt.plot([-1, 1], [0.5 * -1 + 2 , 0.5 * 1 + 2],color ='red')
plt.show()

Resultados

Y vemos los datos de Test también:

Test

Voy a hacer un pequeño lapsus de teoría aquí.

Os invito a revisar este enlace sobre regresión polinómica en TensorFlow para entender los grados.

Gráficas de funciones polinómicas

 

En el siguiente enlace podéis ver la representación de múltiples ecuaciones (que muchos hemos estudiado pero a ver quién se acuerda).

Gráficas de funciones polinómicas

Y utilizamos el mismo código para hacer el entrenamiento:

# Configuramos los logs para poderlos utilizar en TensorBoard mediante callbacks
logdir = "logs/scalars/" + datetime.now().strftime("%Y%m%d-%H%M%S")
tensorboard_callback = keras.callbacks.TensorBoard(log_dir=logdir)

# Revisar los tipos de callbacks https://www.tensorflow.org/api_docs/python/tf/keras/callbacks/

Ahora sería buen momento para estudiar cómo construir un modelo secuencial

When to use a sequential model

# Configuramos un modelo secuencia básico
model = keras.models.Sequential([
    keras.layers.Dense(16, input_dim=1),
    keras.layers.Dense(1),
])

model.summary()

Podemos visualizar el sumario de la red que tenemos:

sumario de la red

Y también podemos visualizar el modelo gráficamente.

keras.utils.plot_model(model, 'representacion.png', show_shapes=True)

visualización el modelo gráficamente

Ahora compilamos el modelo y lo entrenamos.

# Configuramos y establecemos un modelo de regresión lineal con un salto de 0.2 
model.compile(   
    loss='mse', # keras.losses.mean_squared_error   
    optimizer=keras.optimizers.SGD(lr=0.02), ) 

# Configuramos y entrenamos el modelo 
    training_history = model.fit(   
    x_train, # input   
    y_train, # output   
    batch_size=train_size,   
    verbose=2, # Suppress chatty output; use Tensorboard instead   
    epochs=10,   
    validation_data=(x_test, y_test),   
    callbacks=[tensorboard_callback], ) 

# Mostramos la perdida media 
print("Average test loss: ", np.average(training_history.history['loss'])) 

# Visualizamos el error 
    print(training_history.params) 
    print(training_history.history['loss']) 
    print(training_history.history['val_loss']) 

plt.plot(training_history.history['loss']) 
plt.plot(training_history.history['val_loss']) 

# Pintamos la gráfica 
plt.show()

Vemos las gráficas sobre cómo funciona la pérdida… que se nos va.

Gráfica Pérdida

Y podemos ver que algo no funciona demasiado bien porque el error tiende a infinito.

Por tanto, los parámetros de configuración que podemos manejar hasta ahora no son satisfactorios.

Gráfica Pérdida

El sistema no es capaz de predecir los resultados.datos predecidos

Podemos actuar por ahora en varios puntos:

  • El coeficiente de aprendizaje.
  • Las capas de la red.
  • La ecuación de activación.

Vamos a empezar a actuar en el coeficiente de aprendizaje que nos hace sospechar que es muy grande y lo podemos intentar bajar.

Se pueden hacer varias pruebas: bajándolo una dimensión y luego ir incrementándolo contrastando resultados de pérdida.

Model.Compile

Y obtenemos el siguiente resultado:

Resultados

Superados ciertos intentos la cosa no mejora y la perdida total media es de 5.5.

pérdida Total Media

Epoch_loss

Vemos los valores que obtenemos de la predicción.

Predicción

Vamos a buscar un método simple para pintar un espacio vectorial de 25 puntos y calcular su predicción, para ver visualmente la relación que hay entre los datos iniciales de entrenamiento y predicciones.

datosPrediccion = np.linspace(-3, 3, 25)
resPrediccion = model.predict(datosPrediccion)

print("x",datosPrediccion)   
print("y",resPrediccion)

plt.title('Datos de predecidos')
plt.grid(alpha =.9, linestyle ='--')
plt.scatter(x_train,y_train)

plt.scatter(datosPrediccion,resPrediccion)
plt.show()

Esta gráfica nos va a permitir hacernos una mejor idea de lo que está pasando:

espacio vectorial de 25 puntos

Podemos continuar con el ejemplo cambiando los coeficientes de entrenamiento a mano subiéndolos y bajándolos un poco. También podemos cambiar el ratio de entrenamiento dinámicamente.

logdir = "logs/scalars/" + datetime.now().strftime("%Y%m%d-%H%M%S")
file_writer = tf.summary.create_file_writer(logdir + "/metrics")
file_writer.set_as_default()

def lr_schedule(epoch):
"""
Returns a custom learning rate that decreases as epochs progress.
"""
learning_rate = 0.05
if epoch > 10:
  learning_rate = 0.02
if epoch > 20:
  learning_rate = 0.001
if epoch > 50:
  learning_rate = 0.001

  tf.summary.scalar('learning rate', data=learning_rate, step=epoch)
return learning_rate

lr_callback = keras.callbacks.LearningRateScheduler(lr_schedule)
tensorboard_callback = keras.callbacks.TensorBoard(log_dir=logdir)

model = keras.models.Sequential([
  keras.layers.Dense(16, input_dim=1),
  keras.layers.Dense(1),
])

model.compile(
  loss='mse', # keras.losses.mean_squared_error
  optimizer=keras.optimizers.SGD(),
)

training_history = model.fit(
  x_train, # input
  y_train, # output
  batch_size=train_size,
  verbose=2, # Suppress chatty output; use Tensorboard instead
  epochs=100,
  validation_data=(x_test, y_test),
  callbacks=[tensorboard_callback, lr_callback],
)

Y comprobamos que hemos mejorado la pérdida media.

Mejora perdida media

Por lo que parece que modificando el ratio de aprendizaje, solamente obtenemos una mejora parcial. Ahora vamos a modificar la complejidad de la red para ver si mejoramos.

Intuitivamente si añadimos una capa más se produce una “multiplicación de una transformación por otra” lo que provocará una adaptación a las rectas con una curva más compleja”.

Os invito a que re-visitéis ahora el entrenador de TensorFlow para comprender mejor cómo afecta introducir complejidad en la red neuronal y modificar la función de activación y ratio de aprendizaje.

TensorFlow

Modificamos la red.

logdir = "logs/scalars/" + datetime.now().strftime("%Y%m%d-%H%M%S") 
tensorboard_callback = keras.callbacks.TensorBoard(log_dir=logdir) 

from tensorflow.keras import layers 

# Define Sequential model with 3 layers model = keras.Sequential(   
    [       
      layers.Dense(2, activation="relu", name="layer1"),       
      layers.Dense(3, activation="relu", name="layer2"),       
      layers.Dense(1, name="layer3"),   
    ] 
)

Comprobamos la nueva red montada.

Nueva red

Compilamos y revisamos.

model.compile(   
    loss='mse', # keras.losses.mean_squared_error   
    optimizer=keras.optimizers.SGD(lr=0.04), 
) 
training_history = model.fit(   
    x_train, # input   y_train, # output   
    batch_size=train_size,   
    verbose=2, # Suppress chatty output; use Tensorboard instead   
    epochs=100,   validation_data=(x_test, y_test),   
    callbacks=[tensorboard_callback], ) 

print("Average test loss: ", np.average(training_history.history['loss']))

Y vemos que la curva tiene otra pinta.

Nueva curva

Y esta es la adaptación entre la predicción y las muestras, que va teniendo ya mejor aspecto (considerando que siempre hará un error o sobre-adaptación).

adaptación entre la predicción y las muestras

Ahora vamos a aumentar en número de núcleos a 16 en la segunda capa.

aumentar en número de núcleos

Visualizando la nueva red.

Nueva red

Y comprovamos que se suaviza la curva anterior.

Nueva curva

Aunque la adaptación no es la más satisfactoria.

Nueva gráfica puntos

Pero ahora vamos a hacer un pequeño cambio, modificar la función de activación de la segunda capa a “selu”.

from tensorflow.keras import layers 
# Define Sequential model with 3 layers 
model = keras.Sequential(   
    [       
       layers.Dense(4, activation="relu", name="layer1"),       
       layers.Dense(16, activation="selu", name="layer2"),       
       layers.Dense(1, name="layer3"),   
    ] 
) 

model.compile(     
    loss='mse', # keras.losses.mean_squared_error     
    optimizer=keras.optimizers.SGD(lr=0.04), 
)

Os invito ahora a visitar este enlace donde podéis ver ejemplos de código con regresión polinomial.

ejemplos de código con regresión polinomial

Podemos ver la función de activación selu en el siguiente enlace.  

Selu

Y podemos comprobar con TensorBoard la pérdida, que es la mejor que hemos podido obtener hasta ahora.

tensorBoard

Y esta es la adaptación de la curva a las rectas.

adaptación de la curva a las rectas

Con el nivel de conocimiento que tengo hasta ahora, es lo mejor que he podido conseguir. Tendremos que seguir investigando sobre los posibles parámetros a actuar y posibles soluciones a un mismo problema.

Os invito a tocar y proponerme mejoras sobre el modelo que es cómo se aprende.

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad