Boucles d'entraînement de base

Voir sur TensorFlow.org Exécuter dans Google Colab Voir la source sur GitHub Télécharger le cahier

Dans les guides précédents, vous avez découvert les tenseurs , les variables , le ruban dégradé et les modules . Dans ce guide, vous les assemblerez tous pour former des modèles.

TensorFlow inclut également l' API tf.Keras , une API de réseau neuronal de haut niveau qui fournit des abstractions utiles pour réduire le passe-partout. Cependant, dans ce guide, vous utiliserez des classes de base.

Installer

import tensorflow as tf

import matplotlib.pyplot as plt

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

Résoudre les problèmes d'apprentissage automatique

La résolution d'un problème d'apprentissage automatique comprend généralement les étapes suivantes :

  • Obtenir des données d'entraînement.
  • Définissez le modèle.
  • Définir une fonction de perte.
  • Parcourir les données d'entraînement, en calculant la perte à partir de la valeur idéale
  • Calculez les gradients pour cette perte et utilisez un optimiseur pour ajuster les variables en fonction des données.
  • Évaluez vos résultats.

À des fins d'illustration, dans ce guide, vous allez développer un modèle linéaire simple, \(f(x) = x * W + b\), qui comporte deux variables : \(W\) (pondérations) et \(b\) (biais).

Il s'agit du problème d'apprentissage automatique le plus élémentaire : étant donné \(x\) et \(y\), essayez de trouver la pente et le décalage d'une ligne via une simple régression linéaire .

Données

L'apprentissage supervisé utilise des entrées (généralement notées x ) et des sorties (notées y , souvent appelées labels ). L'objectif est d'apprendre à partir d'entrées et de sorties appariées afin de pouvoir prédire la valeur d'une sortie à partir d'une entrée.

Chaque entrée de vos données, dans TensorFlow, est presque toujours représentée par un tenseur et est souvent un vecteur. Dans l'entraînement supervisé, la sortie (ou la valeur que vous souhaitez prédire) est également un tenseur.

Voici quelques données synthétisées en ajoutant du bruit gaussien (normal) aux points le long d'une ligne.

# 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

Les tenseurs sont généralement rassemblés en lots ou en groupes d'entrées et de sorties empilées ensemble. Le traitement par lots peut conférer certains avantages en matière de formation et fonctionne bien avec les accélérateurs et le calcul vectorisé. Étant donné la petite taille de cet ensemble de données, vous pouvez traiter l'intégralité de l'ensemble de données comme un seul lot.

Définir le modèle

Utilisez tf.Variable pour représenter tous les poids dans un modèle. Un tf.Variable stocke une valeur et la fournit sous forme de tenseur selon les besoins. Voir le guide des variables pour plus de détails.

Utilisez tf.Module pour encapsuler les variables et le calcul. Vous pouvez utiliser n'importe quel objet Python, mais de cette façon, il peut être facilement enregistré.

Ici, vous définissez à la fois w et b comme 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.

Les variables initiales sont définies ici de manière fixe, mais Keras est livré avec un certain nombre d' initialiseurs que vous pouvez utiliser, avec ou sans le reste de Keras.

Définir une fonction de perte

Une fonction de perte mesure à quel point la sortie d'un modèle pour une entrée donnée correspond à la sortie cible. Le but est de minimiser cette différence lors de l'entraînement. Définissez la perte L2 standard, également connue sous le nom d'erreur "moyenne quadratique":

# 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))

Avant d'entraîner le modèle, vous pouvez visualiser la valeur de perte en traçant les prédictions du modèle en rouge et les données d'entraînement en bleu :

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

Définir une boucle d'entraînement

La boucle d'entraînement consiste à effectuer de manière répétée trois tâches dans l'ordre :

  • Envoi d'un lot d'entrées via le modèle pour générer des sorties
  • Calcul de la perte en comparant les sorties à la sortie (ou étiquette)
  • Utiliser du ruban dégradé pour trouver les dégradés
  • Optimiser les variables avec ces gradients

Pour cet exemple, vous pouvez entraîner le modèle à l'aide de la descente de gradient .

Il existe de nombreuses variantes du schéma de descente de gradient capturées dans tf.keras.optimizers . Mais dans l'esprit de la construction à partir des premiers principes, vous implémenterez ici vous-même les mathématiques de base à l'aide de tf.GradientTape pour la différenciation automatique et tf.assign_sub pour décrémenter une valeur (qui combine tf.assign et 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)

Pour un aperçu de la formation, vous pouvez envoyer le même lot de x et y à travers la boucle de formation et voir comment W et b évoluent.

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))

Faire la formation

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

Tracez l'évolution des poids dans le temps :

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

Visualisez les performances du modèle formé

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 même solution, mais avec Keras

Il est utile de comparer le code ci-dessus avec l'équivalent dans Keras.

La définition du modèle est exactement la même si vous sous- tf.keras.Model . Rappelez-vous que les modèles Keras héritent finalement de module.

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

Plutôt que d'écrire de nouvelles boucles d'entraînement chaque fois que vous créez un modèle, vous pouvez utiliser les fonctionnalités intégrées de Keras comme raccourci. Cela peut être utile lorsque vous ne souhaitez pas écrire ou déboguer des boucles de formation Python.

Si vous le faites, vous devrez utiliser model.compile() pour définir les paramètres et model.fit() pour l'entraînement. Il peut y avoir moins de code pour utiliser les implémentations Keras de la perte L2 et de la descente de gradient, encore une fois comme raccourci. Les pertes et les optimiseurs de Keras peuvent également être utilisés en dehors de ces fonctions de commodité, et l'exemple précédent aurait pu les utiliser.

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 attend des données par lots ou un ensemble de données complet sous forme de tableau NumPy. Les tableaux NumPy sont découpés en lots et ont par défaut une taille de lot de 32.

Dans ce cas, pour correspondre au comportement de la boucle manuscrite, vous devez transmettre x en un seul lot de taille 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>

Notez que Keras imprime la perte après l'entraînement, pas avant, donc la première perte apparaît plus faible, mais sinon cela montre essentiellement les mêmes performances d'entraînement.

Prochaines étapes

Dans ce guide, vous avez vu comment utiliser les classes de base des tenseurs, des variables, des modules et du ruban de dégradé pour créer et former un modèle, et comment ces idées sont mappées à Keras.

Il s'agit pourtant d'un problème extrêmement simple. Pour une introduction plus pratique, consultez Présentation de la formation personnalisée .

Pour en savoir plus sur l'utilisation des boucles d'entraînement Keras intégrées, consultez ce guide . Pour en savoir plus sur les boucles d'entraînement et les Keras, consultez ce guide . Pour écrire des boucles de formation distribuées personnalisées, consultez ce guide .