Bucles de entrenamiento básico

Ver en TensorFlow.org Ejecutar en Google Colab Ver fuente en GitHub Descargar libreta

En las guías anteriores, aprendió sobre tensores , variables , cinta de degradado y módulos . En esta guía, los combinará para entrenar modelos.

TensorFlow también incluye la API tf.Keras , una API de red neuronal de alto nivel que proporciona abstracciones útiles para reducir la repetición. Sin embargo, en esta guía, utilizará clases básicas.

Configuración

import tensorflow as tf

import matplotlib.pyplot as plt

colors = plt.rcParams['axes.prop_cycle'].by_key()['color']

Resolución de problemas de aprendizaje automático

Resolver un problema de aprendizaje automático generalmente consta de los siguientes pasos:

  • Obtener datos de entrenamiento.
  • Defina el modelo.
  • Defina una función de pérdida.
  • Ejecute los datos de entrenamiento, calculando la pérdida a partir del valor ideal
  • Calcule gradientes para esa pérdida y use un optimizador para ajustar las variables para que se ajusten a los datos.
  • Evalúa tus resultados.

Con fines ilustrativos, en esta guía desarrollará un modelo lineal simple, \(f(x) = x * W + b\), que tiene dos variables: \(W\) (pesos) y \(b\) (sesgo).

Este es el más básico de los problemas de aprendizaje automático: dados \(x\) y \(y\), intente encontrar la pendiente y el desplazamiento de una línea a través de una regresión lineal simple .

Datos

El aprendizaje supervisado utiliza entradas (generalmente indicadas como x ) y salidas (indicadas como y , a menudo llamadas etiquetas ). El objetivo es aprender de las entradas y salidas emparejadas para poder predecir el valor de una salida a partir de una entrada.

Cada entrada de sus datos, en TensorFlow, casi siempre está representada por un tensor y, a menudo, es un vector. En el entrenamiento supervisado, la salida (o el valor que le gustaría predecir) también es un tensor.

Aquí hay algunos datos sintetizados agregando ruido gaussiano (normal) a puntos a lo largo de una línea.

# The actual line
TRUE_W = 3.0
TRUE_B = 2.0

NUM_EXAMPLES = 201

# A vector of random x values
x = tf.linspace(-2,2, NUM_EXAMPLES)
x = tf.cast(x, tf.float32)

def f(x):
  return x * TRUE_W + TRUE_B

# Generate some noise
noise = tf.random.normal(shape=[NUM_EXAMPLES])

# Calculate y
y = f(x) + noise
# Plot all the data
plt.plot(x, y, '.')
plt.show()

png

Los tensores generalmente se reúnen en lotes , o grupos de entradas y salidas apiladas juntas. El procesamiento por lotes puede conferir algunos beneficios de capacitación y funciona bien con aceleradores y computación vectorizada. Dado lo pequeño que es este conjunto de datos, puede tratar todo el conjunto de datos como un solo lote.

Definir el modelo

Use tf.Variable para representar todos los pesos en un modelo. Una tf.Variable almacena un valor y lo proporciona en forma de tensor según sea necesario. Consulte la guía de variables para obtener más detalles.

Utilice tf.Module para encapsular las variables y el cálculo. Podría usar cualquier objeto de Python, pero de esta manera se puede guardar fácilmente.

Aquí, define w y b como variables.

class MyModel(tf.Module):
  def __init__(self, **kwargs):
    super().__init__(**kwargs)
    # Initialize the weights to `5.0` and the bias to `0.0`
    # In practice, these should be randomly initialized
    self.w = tf.Variable(5.0)
    self.b = tf.Variable(0.0)

  def __call__(self, x):
    return self.w * x + self.b

model = MyModel()

# List the variables tf.modules's built-in variable aggregation.
print("Variables:", model.variables)

# Verify the model works
assert model(3.0).numpy() == 15.0
Variables: (<tf.Variable 'Variable:0' shape=() dtype=float32, numpy=0.0>, <tf.Variable 'Variable:0' shape=() dtype=float32, numpy=5.0>)
2021-12-08 17:11:44.542944: W tensorflow/python/util/util.cc:368] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.

Las variables iniciales se configuran aquí de forma fija, pero Keras viene con cualquiera de una serie de inicializadores que puede usar, con o sin el resto de Keras.

Definir una función de pérdida

Una función de pérdida mide qué tan bien la salida de un modelo para una entrada determinada coincide con la salida objetivo. El objetivo es minimizar esta diferencia durante el entrenamiento. Defina la pérdida L2 estándar, también conocida como error de "cuadrado medio":

# This computes a single loss value for an entire batch
def loss(target_y, predicted_y):
  return tf.reduce_mean(tf.square(target_y - predicted_y))

Antes de entrenar el modelo, puede visualizar el valor de pérdida trazando las predicciones del modelo en rojo y los datos de entrenamiento en azul:

plt.plot(x, y, '.', label="Data")
plt.plot(x, f(x), label="Ground truth")
plt.plot(x, model(x), label="Predictions")
plt.legend()
plt.show()

print("Current loss: %1.6f" % loss(y, model(x)).numpy())

png

Current loss: 10.301712

Definir un ciclo de entrenamiento

El ciclo de entrenamiento consiste en realizar repetidamente tres tareas en orden:

  • Envío de un lote de entradas a través del modelo para generar salidas
  • Cálculo de la pérdida comparando las salidas con la salida (o etiqueta)
  • Usar cinta de degradado para encontrar los degradados
  • Optimizando las variables con esos gradientes

Para este ejemplo, puede entrenar el modelo mediante el descenso de gradiente .

Hay muchas variantes del esquema de descenso de gradiente que se capturan en tf.keras.optimizers . Pero con el espíritu de construir a partir de los primeros principios, aquí implementará las matemáticas básicas usted mismo con la ayuda de tf.GradientTape para la diferenciación automática y tf.assign_sub para disminuir un valor (que combina tf.assign y tf.sub ):

# Given a callable model, inputs, outputs, and a learning rate...
def train(model, x, y, learning_rate):

  with tf.GradientTape() as t:
    # Trainable variables are automatically tracked by GradientTape
    current_loss = loss(y, model(x))

  # Use GradientTape to calculate the gradients with respect to W and b
  dw, db = t.gradient(current_loss, [model.w, model.b])

  # Subtract the gradient scaled by the learning rate
  model.w.assign_sub(learning_rate * dw)
  model.b.assign_sub(learning_rate * db)

Para ver el entrenamiento, puede enviar el mismo lote de x e y a través del ciclo de entrenamiento y ver cómo evolucionan W y b .

model = MyModel()

# Collect the history of W-values and b-values to plot later
weights = []
biases = []
epochs = range(10)

# Define a training loop
def report(model, loss):
  return f"W = {model.w.numpy():1.2f}, b = {model.b.numpy():1.2f}, loss={current_loss:2.5f}"


def training_loop(model, x, y):

  for epoch in epochs:
    # Update the model with the single giant batch
    train(model, x, y, learning_rate=0.1)

    # Track this before I update
    weights.append(model.w.numpy())
    biases.append(model.b.numpy())
    current_loss = loss(y, model(x))

    print(f"Epoch {epoch:2d}:")
    print("    ", report(model, current_loss))

hacer el entrenamiento

current_loss = loss(y, model(x))

print(f"Starting:")
print("    ", report(model, current_loss))

training_loop(model, x, y)
Starting:
     W = 5.00, b = 0.00, loss=10.30171
Epoch  0:
     W = 4.46, b = 0.40, loss=10.30171
Epoch  1:
     W = 4.06, b = 0.72, loss=10.30171
Epoch  2:
     W = 3.77, b = 0.97, loss=10.30171
Epoch  3:
     W = 3.56, b = 1.18, loss=10.30171
Epoch  4:
     W = 3.40, b = 1.34, loss=10.30171
Epoch  5:
     W = 3.29, b = 1.47, loss=10.30171
Epoch  6:
     W = 3.21, b = 1.58, loss=10.30171
Epoch  7:
     W = 3.15, b = 1.66, loss=10.30171
Epoch  8:
     W = 3.10, b = 1.73, loss=10.30171
Epoch  9:
     W = 3.07, b = 1.78, loss=10.30171

Graficar la evolución de los pesos en el tiempo:

plt.plot(epochs, weights, label='Weights', color=colors[0])
plt.plot(epochs, [TRUE_W] * len(epochs), '--',
         label = "True weight", color=colors[0])

plt.plot(epochs, biases, label='bias', color=colors[1])
plt.plot(epochs, [TRUE_B] * len(epochs), "--",
         label="True bias", color=colors[1])

plt.legend()
plt.show()

png

Visualice cómo se desempeña el modelo entrenado

plt.plot(x, y, '.', label="Data")
plt.plot(x, f(x), label="Ground truth")
plt.plot(x, model(x), label="Predictions")
plt.legend()
plt.show()

print("Current loss: %1.6f" % loss(model(x), y).numpy())

png

Current loss: 0.897898

La misma solución, pero con Keras

Es útil contrastar el código anterior con el equivalente en Keras.

La definición del modelo se ve exactamente igual si subclasifica tf.keras.Model . Recuerde que los modelos de Keras heredan en última instancia del módulo.

class MyModelKeras(tf.keras.Model):
  def __init__(self, **kwargs):
    super().__init__(**kwargs)
    # Initialize the weights to `5.0` and the bias to `0.0`
    # In practice, these should be randomly initialized
    self.w = tf.Variable(5.0)
    self.b = tf.Variable(0.0)

  def call(self, x):
    return self.w * x + self.b

keras_model = MyModelKeras()

# Reuse the training loop with a Keras model
training_loop(keras_model, x, y)

# You can also save a checkpoint using Keras's built-in support
keras_model.save_weights("my_checkpoint")
Epoch  0:
     W = 4.46, b = 0.40, loss=10.30171
Epoch  1:
     W = 4.06, b = 0.72, loss=10.30171
Epoch  2:
     W = 3.77, b = 0.97, loss=10.30171
Epoch  3:
     W = 3.56, b = 1.18, loss=10.30171
Epoch  4:
     W = 3.40, b = 1.34, loss=10.30171
Epoch  5:
     W = 3.29, b = 1.47, loss=10.30171
Epoch  6:
     W = 3.21, b = 1.58, loss=10.30171
Epoch  7:
     W = 3.15, b = 1.66, loss=10.30171
Epoch  8:
     W = 3.10, b = 1.73, loss=10.30171
Epoch  9:
     W = 3.07, b = 1.78, loss=10.30171

En lugar de escribir nuevos bucles de entrenamiento cada vez que crea un modelo, puede utilizar las funciones integradas de Keras como acceso directo. Esto puede ser útil cuando no desea escribir o depurar bucles de entrenamiento de Python.

Si lo hace, deberá usar model.compile() para establecer los parámetros y model.fit() para entrenar. Puede ser menos código usar las implementaciones de Keras de pérdida de L2 y descenso de gradiente, nuevamente como un atajo. Las pérdidas y los optimizadores de Keras también se pueden usar fuera de estas funciones de conveniencia, y el ejemplo anterior podría haberlas usado.

keras_model = MyModelKeras()

# compile sets the training parameters
keras_model.compile(
    # By default, fit() uses tf.function().  You can
    # turn that off for debugging, but it is on now.
    run_eagerly=False,

    # Using a built-in optimizer, configuring as an object
    optimizer=tf.keras.optimizers.SGD(learning_rate=0.1),

    # Keras comes with built-in MSE error
    # However, you could use the loss function
    # defined above
    loss=tf.keras.losses.mean_squared_error,
)

Keras fit espera datos por lotes o un conjunto de datos completo como una matriz NumPy. Las matrices NumPy se dividen en lotes y por defecto tienen un tamaño de lote de 32.

En este caso, para que coincida con el comportamiento del bucle escrito a mano, debe pasar x como un solo lote de tamaño 1000.

print(x.shape[0])
keras_model.fit(x, y, epochs=10, batch_size=1000)
201
Epoch 1/10
1/1 [==============================] - 0s 242ms/step - loss: 10.3017
Epoch 2/10
1/1 [==============================] - 0s 3ms/step - loss: 6.3148
Epoch 3/10
1/1 [==============================] - 0s 3ms/step - loss: 4.0341
Epoch 4/10
1/1 [==============================] - 0s 3ms/step - loss: 2.7191
Epoch 5/10
1/1 [==============================] - 0s 3ms/step - loss: 1.9548
Epoch 6/10
1/1 [==============================] - 0s 2ms/step - loss: 1.5068
Epoch 7/10
1/1 [==============================] - 0s 3ms/step - loss: 1.2422
Epoch 8/10
1/1 [==============================] - 0s 2ms/step - loss: 1.0845
Epoch 9/10
1/1 [==============================] - 0s 2ms/step - loss: 0.9899
Epoch 10/10
1/1 [==============================] - 0s 3ms/step - loss: 0.9327
<keras.callbacks.History at 0x7f02ad940050>

Tenga en cuenta que Keras imprime la pérdida después del entrenamiento, no antes, por lo que la primera pérdida aparece más baja, pero por lo demás muestra esencialmente el mismo rendimiento de entrenamiento.

Próximos pasos

En esta guía, ha visto cómo usar las clases principales de tensores, variables, módulos y cinta de degradado para construir y entrenar un modelo, y cómo esas ideas se asignan a Keras.

Este es, sin embargo, un problema extremadamente simple. Para obtener una introducción más práctica, consulte Tutorial de capacitación personalizada .

Para obtener más información sobre el uso de bucles de entrenamiento de Keras integrados, consulte esta guía . Para obtener más información sobre bucles de entrenamiento y Keras, consulte esta guía . Para escribir bucles de entrenamiento distribuidos personalizados, consulte esta guía .