Loops de treinamento básico

Ver no TensorFlow.org Executar no Google Colab Ver fonte no GitHub Baixar caderno

Nos guias anteriores, você aprendeu sobre tensores , variáveis , fita de gradiente e módulos . Neste guia, você encaixará todos esses itens para treinar modelos.

O TensorFlow também inclui a API tf.Keras , uma API de rede neural de alto nível que fornece abstrações úteis para reduzir o clichê. No entanto, neste guia, você usará classes básicas.

Configurar

import tensorflow as tf

Resolvendo problemas de aprendizado de máquina

Resolver um problema de aprendizado de máquina geralmente consiste nas seguintes etapas:

  • Obtenha dados de treinamento.
  • Defina o modelo.
  • Defina uma função de perda.
  • Percorra os dados de treinamento, calculando a perda a partir do valor ideal
  • Calcule gradientes para essa perda e use um otimizador para ajustar as variáveis ​​para ajustar os dados.
  • Avalie seus resultados.

Para fins de ilustração, neste guia você desenvolverá um modelo linear simples, $ f (x) = x * W + b $, que tem duas variáveis: $ W $ (pesos) e $ b $ (tendência).

Este é o mais básico dos problemas de aprendizado de máquina: dados $ x $ e $ y $, tente encontrar a inclinação e o deslocamento de uma linha por meio de regressão linear simples .

Dados

A aprendizagem supervisionada usa entradas (geralmente denotadas como x ) e saídas (denotadas y , frequentemente chamadas de rótulos ). O objetivo é aprender com entradas e saídas emparelhadas para que você possa prever o valor de uma saída a partir de uma entrada.

Cada entrada de seus dados, no TensorFlow, quase sempre é representada por um tensor e geralmente é um vetor. No treinamento supervisionado, a saída (ou valor que você gostaria de prever) também é um tensor.

Aqui estão alguns dados sintetizados adicionando ruído gaussiano (normal) a pontos ao longo de uma linha.

# The actual line
TRUE_W = 3.0
TRUE_B = 2.0

NUM_EXAMPLES = 1000

# A vector of random x values
x = tf.random.normal(shape=[NUM_EXAMPLES])

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

# Calculate y
y = x * TRUE_W + TRUE_B + noise
# Plot all the data
import matplotlib.pyplot as plt

plt.scatter(x, y, c="b")
plt.show()

png

Os tensores geralmente são reunidos em lotes ou grupos de entradas e saídas empilhadas. O batching pode conferir alguns benefícios de treinamento e funciona bem com aceleradores e computação vetorial. Dado o quão pequeno este conjunto de dados é, você pode tratar todo o conjunto de dados como um único lote.

Defina o modelo

Use tf.Variable para representar todos os pesos em um modelo. Um tf.Variable armazena um valor e o fornece na forma de tensor conforme necessário. Veja o guia de variáveis para mais detalhes.

Use tf.Module para encapsular as variáveis ​​e o cálculo. Você pode usar qualquer objeto Python, mas desta forma ele pode ser salvo facilmente.

Aqui, você define w e b como variáveis.

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

As variáveis ​​iniciais são definidas aqui de uma forma fixa, mas Keras vem com qualquer um dos vários iniciadores que você pode usar, com ou sem o resto do Keras.

Defina uma função de perda

Uma função de perda mede o quão bem a saída de um modelo para uma determinada entrada corresponde à saída alvo. O objetivo é minimizar essa diferença durante o treinamento. Defina a perda L2 padrão, também conhecida como o erro "quadrático médio":

# 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 treinar o modelo, você pode visualizar o valor da perda plotando as previsões do modelo em vermelho e os dados de treinamento em azul:

plt.scatter(x, y, c="b")
plt.scatter(x, model(x), c="r")
plt.show()

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

png

Current loss: 9.402307

Defina um ciclo de treinamento

O ciclo de treinamento consiste em fazer repetidamente três tarefas em ordem:

  • Envio de um lote de entradas por meio do modelo para gerar saídas
  • Calculando a perda comparando as saídas com a saída (ou rótulo)
  • Usando fita gradiente para encontrar os gradientes
  • Otimizando as variáveis ​​com esses gradientes

Para este exemplo, você pode treinar o modelo usando gradiente descendente .

Existem muitas variantes do esquema de descida gradiente que são capturadas em tf.keras.optimizers . Mas no espírito de construir a partir dos primeiros princípios, aqui você implementará a matemática básica com a ajuda detf.GradientTape para diferenciação automática e tf.assign_sub para decrementar um valor (que combina tf.assign e 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 um olhar para treinamento, você pode enviar o mesmo lote de x e y através do laço de treinamento, e ver como W e b evoluir.

model = MyModel()

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

# Define a training loop
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
    Ws.append(model.w.numpy())
    bs.append(model.b.numpy())
    current_loss = loss(y, model(x))

    print("Epoch %2d: W=%1.2f b=%1.2f, loss=%2.5f" %
          (epoch, Ws[-1], bs[-1], current_loss))
print("Starting: W=%1.2f b=%1.2f, loss=%2.5f" %
      (model.w, model.b, loss(y, model(x))))

# Do the training
training_loop(model, x, y)

# Plot it
plt.plot(epochs, Ws, "r",
         epochs, bs, "b")

plt.plot([TRUE_W] * len(epochs), "r--",
         [TRUE_B] * len(epochs), "b--")

plt.legend(["W", "b", "True W", "True b"])
plt.show()
Starting: W=5.00 b=0.00, loss=9.40231
Epoch  0: W=4.58 b=0.41, loss=6.29136
Epoch  1: W=4.25 b=0.74, loss=4.32347
Epoch  2: W=3.98 b=1.00, loss=3.07861
Epoch  3: W=3.77 b=1.21, loss=2.29113
Epoch  4: W=3.61 b=1.38, loss=1.79297
Epoch  5: W=3.47 b=1.51, loss=1.47783
Epoch  6: W=3.37 b=1.62, loss=1.27846
Epoch  7: W=3.29 b=1.70, loss=1.15233
Epoch  8: W=3.22 b=1.77, loss=1.07254
Epoch  9: W=3.17 b=1.82, loss=1.02206

png

# Visualize how the trained model performs
plt.scatter(x, y, c="b")
plt.scatter(x, model(x), c="r")
plt.show()

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

png

Current loss: 1.022059

A mesma solução, mas com Keras

É útil comparar o código acima com o equivalente em Keras.

Definir o modelo parece exatamente o mesmo se você subclasse tf.keras.Model . Lembre-se de que os modelos Keras herdam basicamente do 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.58 b=0.41, loss=6.29136
Epoch  1: W=4.25 b=0.74, loss=4.32347
Epoch  2: W=3.98 b=1.00, loss=3.07861
Epoch  3: W=3.77 b=1.21, loss=2.29113
Epoch  4: W=3.61 b=1.38, loss=1.79297
Epoch  5: W=3.47 b=1.51, loss=1.47783
Epoch  6: W=3.37 b=1.62, loss=1.27846
Epoch  7: W=3.29 b=1.70, loss=1.15233
Epoch  8: W=3.22 b=1.77, loss=1.07254
Epoch  9: W=3.17 b=1.82, loss=1.02206

Em vez de escrever novos loops de treinamento cada vez que você cria um modelo, você pode usar os recursos integrados do Keras como um atalho. Isso pode ser útil quando você não deseja escrever ou depurar loops de treinamento em Python.

Se fizer isso, você precisará usar model.compile() para definir os parâmetros e model.fit() para treinar. Pode ser menos código usar as implementações Keras de perda de L2 e descida de gradiente, novamente como um atalho. As perdas e otimizadores Keras também podem ser usados ​​fora dessas funções de conveniência, e o exemplo anterior poderia tê-los 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 dados em lote ou um conjunto de dados completo como uma matriz NumPy. As matrizes NumPy são divididas em lotes e o tamanho de lote padrão é 32.

Nesse caso, para corresponder ao comportamento do loop escrito à mão, você deve passar x como um único lote de tamanho 1000.

print(x.shape[0])
keras_model.fit(x, y, epochs=10, batch_size=1000)
1000
Epoch 1/10
1/1 [==============================] - 0s 204ms/step - loss: 9.4023
Epoch 2/10
1/1 [==============================] - 0s 2ms/step - loss: 6.2914
Epoch 3/10
1/1 [==============================] - 0s 2ms/step - loss: 4.3235
Epoch 4/10
1/1 [==============================] - 0s 2ms/step - loss: 3.0786
Epoch 5/10
1/1 [==============================] - 0s 2ms/step - loss: 2.2911
Epoch 6/10
1/1 [==============================] - 0s 2ms/step - loss: 1.7930
Epoch 7/10
1/1 [==============================] - 0s 2ms/step - loss: 1.4778
Epoch 8/10
1/1 [==============================] - 0s 2ms/step - loss: 1.2785
Epoch 9/10
1/1 [==============================] - 0s 2ms/step - loss: 1.1523
Epoch 10/10
1/1 [==============================] - 0s 2ms/step - loss: 1.0725
<tensorflow.python.keras.callbacks.History at 0x7f85ec096bd0>

Observe que Keras imprime a perda após o treinamento, não antes, então a primeira perda parece menor, mas por outro lado, isso mostra essencialmente o mesmo desempenho de treinamento.

Próximos passos

Neste guia, você viu como usar as classes principais de tensores, variáveis, módulos e fita de gradiente para construir e treinar um modelo e ainda como essas ideias são mapeadas para Keras.

Este é, no entanto, um problema extremamente simples. Para obter uma introdução mais prática, consulte Passo a passo de treinamento personalizado .

Para obter mais informações sobre o uso de loops de treinamento Keras integrados, consulte este guia . Para obter mais informações sobre loops de treinamento e Keras, consulte este guia . Para escrever loops de treinamento distribuídos personalizados, consulte este guia .