Se usó la API de Cloud Translation para traducir esta página.
Switch to English

Ejecución ansiosa

Ver en TensorFlow.org Ver fuente en GitHub Descargar cuaderno

La ávida ejecución de TensorFlow es un entorno de programación imperativo que evalúa las operaciones de inmediato, sin crear gráficos: las operaciones devuelven valores concretos en lugar de construir un gráfico computacional para ejecutar más tarde. Esto hace que sea fácil comenzar con TensorFlow y los modelos de depuración, y también reduce la repetición. Para seguir esta guía, ejecute los ejemplos de código a continuación en un intérprete interactivo de python .

La ejecución ávida es una plataforma flexible de aprendizaje automático para la investigación y la experimentación, que proporciona:

  • Una interfaz intuitiva: estructura tu código de forma natural y usa estructuras de datos de Python. Repetir rápidamente en modelos pequeños y datos pequeños.
  • Depuración más sencilla: llame a operaciones directamente para inspeccionar los modelos en ejecución y probar los cambios. Utilice herramientas de depuración estándar de Python para generar informes de errores inmediatos.
  • Flujo de control natural: utilice el flujo de control de Python en lugar del flujo de control de gráficos, lo que simplifica la especificación de modelos dinámicos.

La ejecución ávida admite la mayoría de las operaciones de TensorFlow y la aceleración de GPU.

Configuración y uso básico

import os

import tensorflow as tf

import cProfile

En Tensorflow 2.0, la ejecución ansiosa está habilitada de forma predeterminada.

tf.executing_eagerly()
True

Ahora puede ejecutar operaciones de TensorFlow y los resultados se mostrarán de inmediato:

x = [[2.]]
m = tf.matmul(x, x)
print("hello, {}".format(m))
hello, [[4.]]

Habilitar la ejecución ávida cambia la forma en que se comportan las operaciones de TensorFlow; ahora evalúan y devuelven sus valores a Python de inmediato. tf.Tensor objetos tf.Tensor referencia a valores concretos en lugar de identificadores simbólicos a los nodos en un gráfico computacional. Dado que no hay un gráfico computacional para construir y ejecutar más tarde en una sesión, es fácil inspeccionar los resultados usando print() o un depurador. La evaluación, impresión y verificación de los valores tensoriales no interrumpe el flujo para calcular gradientes.

La ejecución ávida funciona muy bien con NumPy . Las operaciones NumPy aceptan argumentos tf.Tensor . Las operaciones tf.math TensorFlow convierten los objetos Python y las matrices NumPy en objetos tf.Tensor . El método tf.Tensor.numpy devuelve el valor del objeto como un ndarray NumPy.

a = tf.constant([[1, 2],
                 [3, 4]])
print(a)
tf.Tensor(
[[1 2]
 [3 4]], shape=(2, 2), dtype=int32)

# Broadcasting support
b = tf.add(a, 1)
print(b)
tf.Tensor(
[[2 3]
 [4 5]], shape=(2, 2), dtype=int32)

# Operator overloading is supported
print(a * b)
tf.Tensor(
[[ 2  6]
 [12 20]], shape=(2, 2), dtype=int32)

# Use NumPy values
import numpy as np

c = np.multiply(a, b)
print(c)
[[ 2  6]
 [12 20]]

# Obtain numpy value from a tensor:
print(a.numpy())
# => [[1 2]
#     [3 4]]
[[1 2]
 [3 4]]

Flujo de control dinámico

Un beneficio importante de la ejecución ávida es que toda la funcionalidad del lenguaje anfitrión está disponible mientras se ejecuta su modelo. Entonces, por ejemplo, es fácil escribir fizzbuzz :

def fizzbuzz(max_num):
  counter = tf.constant(0)
  max_num = tf.convert_to_tensor(max_num)
  for num in range(1, max_num.numpy()+1):
    num = tf.constant(num)
    if int(num % 3) == 0 and int(num % 5) == 0:
      print('FizzBuzz')
    elif int(num % 3) == 0:
      print('Fizz')
    elif int(num % 5) == 0:
      print('Buzz')
    else:
      print(num.numpy())
    counter += 1
fizzbuzz(15)
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz

Esto tiene condicionales que dependen de los valores del tensor e imprime estos valores en tiempo de ejecución.

Entrenamiento ansioso

Gradientes de computación

La diferenciación automática es útil para implementar algoritmos de aprendizaje automático como la retropropagación para entrenar redes neuronales. Durante la ejecución ansiosa, use tf.GradientTape para rastrear operaciones para calcular gradientes más tarde.

Puede usar tf.GradientTape para entrenar y / o calcular gradientes en ansioso. Es especialmente útil para ciclos de entrenamiento complicados.

Dado que pueden ocurrir diferentes operaciones durante cada llamada, todas las operaciones de paso hacia adelante se graban en una "cinta". Para calcular el gradiente, reproduzca la cinta al revés y luego deséchela. Un tf.GradientTape particular tf.GradientTape puede calcular un gradiente; las llamadas posteriores arrojan un error de tiempo de ejecución.

w = tf.Variable([[1.0]])
with tf.GradientTape() as tape:
  loss = w * w

grad = tape.gradient(loss, w)
print(grad)  # => tf.Tensor([[ 2.]], shape=(1, 1), dtype=float32)
tf.Tensor([[2.]], shape=(1, 1), dtype=float32)

Entrena un modelo

El siguiente ejemplo crea un modelo multicapa que clasifica los dígitos manuscritos estándar de MNIST. Demuestra el optimizador y las API de capas para crear gráficos entrenables en un entorno de ejecución ávido.

# Fetch and format the mnist data
(mnist_images, mnist_labels), _ = tf.keras.datasets.mnist.load_data()

dataset = tf.data.Dataset.from_tensor_slices(
  (tf.cast(mnist_images[...,tf.newaxis]/255, tf.float32),
   tf.cast(mnist_labels,tf.int64)))
dataset = dataset.shuffle(1000).batch(32)
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] - 0s 0us/step

# Build the model
mnist_model = tf.keras.Sequential([
  tf.keras.layers.Conv2D(16,[3,3], activation='relu',
                         input_shape=(None, None, 1)),
  tf.keras.layers.Conv2D(16,[3,3], activation='relu'),
  tf.keras.layers.GlobalAveragePooling2D(),
  tf.keras.layers.Dense(10)
])

Incluso sin entrenamiento, llame al modelo e inspeccione el resultado en ejecución ansiosa:

for images,labels in dataset.take(1):
  print("Logits: ", mnist_model(images[0:1]).numpy())
Logits:  [[ 0.06289896 -0.03877686 -0.07346137 -0.03169462  0.02922358 -0.02436475
  -0.00588411  0.03256026  0.01715117 -0.02714448]]

Si bien los modelos de keras tienen un bucle de entrenamiento incorporado (usando el método de fit ), a veces necesitas más personalización. A continuación, se muestra un ejemplo de un ciclo de entrenamiento implementado con entusiasmo:

optimizer = tf.keras.optimizers.Adam()
loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)

loss_history = []
def train_step(images, labels):
  with tf.GradientTape() as tape:
    logits = mnist_model(images, training=True)
    
    # Add asserts to check the shape of the output.
    tf.debugging.assert_equal(logits.shape, (32, 10))
    
    loss_value = loss_object(labels, logits)

  loss_history.append(loss_value.numpy().mean())
  grads = tape.gradient(loss_value, mnist_model.trainable_variables)
  optimizer.apply_gradients(zip(grads, mnist_model.trainable_variables))
def train(epochs):
  for epoch in range(epochs):
    for (batch, (images, labels)) in enumerate(dataset):
      train_step(images, labels)
    print ('Epoch {} finished'.format(epoch))
train(epochs = 3)
Epoch 0 finished
Epoch 1 finished
Epoch 2 finished

import matplotlib.pyplot as plt

plt.plot(loss_history)
plt.xlabel('Batch #')
plt.ylabel('Loss [entropy]')
Text(0, 0.5, 'Loss [entropy]')

png

Variables y optimizadores

tf.Variable objetos tf.Variable almacenan valores similares a tf.Tensor mutables a los que se accede durante el entrenamiento para facilitar la diferenciación automática.

Las colecciones de variables se pueden encapsular en capas o modelos, junto con los métodos que operan sobre ellas. Consulte Capas y modelos personalizados de Keras para obtener más detalles. La principal diferencia entre capas y modelos es que los modelos agregan métodos como Model.fit , Model.evaluate y Model.save .

Por ejemplo, el ejemplo de diferenciación automática anterior se puede reescribir:

class Linear(tf.keras.Model):
  def __init__(self):
    super(Linear, self).__init__()
    self.W = tf.Variable(5., name='weight')
    self.B = tf.Variable(10., name='bias')
  def call(self, inputs):
    return inputs * self.W + self.B
# A toy dataset of points around 3 * x + 2
NUM_EXAMPLES = 2000
training_inputs = tf.random.normal([NUM_EXAMPLES])
noise = tf.random.normal([NUM_EXAMPLES])
training_outputs = training_inputs * 3 + 2 + noise

# The loss function to be optimized
def loss(model, inputs, targets):
  error = model(inputs) - targets
  return tf.reduce_mean(tf.square(error))

def grad(model, inputs, targets):
  with tf.GradientTape() as tape:
    loss_value = loss(model, inputs, targets)
  return tape.gradient(loss_value, [model.W, model.B])

Próximo:

  1. Crea el modelo.
  2. Las derivadas de una función de pérdida con respecto a los parámetros del modelo.
  3. Una estrategia para actualizar las variables en función de las derivadas.
model = Linear()
optimizer = tf.keras.optimizers.SGD(learning_rate=0.01)

print("Initial loss: {:.3f}".format(loss(model, training_inputs, training_outputs)))

steps = 300
for i in range(steps):
  grads = grad(model, training_inputs, training_outputs)
  optimizer.apply_gradients(zip(grads, [model.W, model.B]))
  if i % 20 == 0:
    print("Loss at step {:03d}: {:.3f}".format(i, loss(model, training_inputs, training_outputs)))
Initial loss: 68.602
Loss at step 000: 65.948
Loss at step 020: 30.156
Loss at step 040: 14.091
Loss at step 060: 6.878
Loss at step 080: 3.637
Loss at step 100: 2.180
Loss at step 120: 1.525
Loss at step 140: 1.231
Loss at step 160: 1.098
Loss at step 180: 1.038
Loss at step 200: 1.011
Loss at step 220: 0.999
Loss at step 240: 0.994
Loss at step 260: 0.991
Loss at step 280: 0.990

print("Final loss: {:.3f}".format(loss(model, training_inputs, training_outputs)))
Final loss: 0.990

print("W = {}, B = {}".format(model.W.numpy(), model.B.numpy()))
W = 3.001922369003296, B = 2.0047335624694824

Ahorro basado en objetos

Un tf.keras.Model incluye un método conveniente save_weights que le permite crear fácilmente un punto de control:

model.save_weights('weights')
status = model.load_weights('weights')

Con tf.train.Checkpoint puede tomar el control total sobre este proceso.

Esta sección es una versión abreviada de la guía de puntos de control de entrenamiento .

x = tf.Variable(10.)
checkpoint = tf.train.Checkpoint(x=x)
x.assign(2.)   # Assign a new value to the variables and save.
checkpoint_path = './ckpt/'
checkpoint.save('./ckpt/')
'./ckpt/-1'
x.assign(11.)  # Change the variable after saving.

# Restore values from the checkpoint
checkpoint.restore(tf.train.latest_checkpoint(checkpoint_path))

print(x)  # => 2.0
<tf.Variable 'Variable:0' shape=() dtype=float32, numpy=2.0>

Para guardar y cargar modelos, tf.train.Checkpoint almacena el estado interno de los objetos, sin requerir variables ocultas. Para registrar el estado de un model , un optimizer y un paso global, tf.train.Checkpoint a un tf.train.Checkpoint :

model = tf.keras.Sequential([
  tf.keras.layers.Conv2D(16,[3,3], activation='relu'),
  tf.keras.layers.GlobalAveragePooling2D(),
  tf.keras.layers.Dense(10)
])
optimizer = tf.keras.optimizers.Adam(learning_rate=0.001)
checkpoint_dir = 'path/to/model_dir'
if not os.path.exists(checkpoint_dir):
  os.makedirs(checkpoint_dir)
checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt")
root = tf.train.Checkpoint(optimizer=optimizer,
                           model=model)

root.save(checkpoint_prefix)
root.restore(tf.train.latest_checkpoint(checkpoint_dir))
<tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x7f9abd45f1d0>

Métricas orientadas a objetos

tf.keras.metrics se almacenan como objetos. Actualice una métrica pasando los datos nuevos al invocable y recupere el resultado mediante el método tf.keras.metrics.result , por ejemplo:

m = tf.keras.metrics.Mean("loss")
m(0)
m(5)
m.result()  # => 2.5
m([8, 9])
m.result()  # => 5.5
<tf.Tensor: shape=(), dtype=float32, numpy=5.5>

Resúmenes y TensorBoard

TensorBoard es una herramienta de visualización para comprender, depurar y optimizar el proceso de entrenamiento del modelo. Utiliza eventos de resumen que se escriben mientras se ejecuta el programa.

Puede usar tf.summary para registrar resúmenes de variables en ejecución ansiosa. Por ejemplo, para registrar resúmenes de loss una vez cada 100 pasos de entrenamiento:

logdir = "./tb/"
writer = tf.summary.create_file_writer(logdir)

steps = 1000
with writer.as_default():  # or call writer.set_as_default() before the loop.
  for i in range(steps):
    step = i + 1
    # Calculate loss with your real train function.
    loss = 1 - 0.001 * step
    if step % 100 == 0:
      tf.summary.scalar('loss', loss, step=step)
ls tb/
events.out.tfevents.1602033841.kokoro-gcp-ubuntu-prod-892356553.1670.619697.v2

Temas avanzados de diferenciación automática

Modelos dinámicos

tf.GradientTape también se puede utilizar en modelos dinámicos. Este ejemplo de un algoritmo de búsqueda de línea de retroceso se parece al código NumPy normal, excepto que hay gradientes y es diferenciable, a pesar del complejo flujo de control:

def line_search_step(fn, init_x, rate=1.0):
  with tf.GradientTape() as tape:
    # Variables are automatically tracked.
    # But to calculate a gradient from a tensor, you must `watch` it.
    tape.watch(init_x)
    value = fn(init_x)
  grad = tape.gradient(value, init_x)
  grad_norm = tf.reduce_sum(grad * grad)
  init_value = value
  while value > init_value - rate * grad_norm:
    x = init_x - rate * grad
    value = fn(x)
    rate /= 2.0
  return x, value

Degradados personalizados

Los degradados personalizados son una forma fácil de anular los degradados. Dentro de la función de avance, defina el gradiente con respecto a las entradas, salidas o resultados intermedios. Por ejemplo, aquí hay una manera fácil de recortar la norma de los degradados en la pasada hacia atrás:

@tf.custom_gradient
def clip_gradient_by_norm(x, norm):
  y = tf.identity(x)
  def grad_fn(dresult):
    return [tf.clip_by_norm(dresult, norm), None]
  return y, grad_fn

Los degradados personalizados se utilizan comúnmente para proporcionar un degradado numéricamente estable para una secuencia de operaciones:

def log1pexp(x):
  return tf.math.log(1 + tf.exp(x))

def grad_log1pexp(x):
  with tf.GradientTape() as tape:
    tape.watch(x)
    value = log1pexp(x)
  return tape.gradient(value, x)

# The gradient computation works fine at x = 0.
grad_log1pexp(tf.constant(0.)).numpy()
0.5
# However, x = 100 fails because of numerical instability.
grad_log1pexp(tf.constant(100.)).numpy()
nan

Aquí, la función log1pexp se puede simplificar analíticamente con un gradiente personalizado. La siguiente implementación reutiliza el valor de tf.exp(x) que se calcula durante el paso hacia adelante, lo que lo hace más eficiente al eliminar los cálculos redundantes:

@tf.custom_gradient
def log1pexp(x):
  e = tf.exp(x)
  def grad(dy):
    return dy * (1 - 1 / (1 + e))
  return tf.math.log(1 + e), grad

def grad_log1pexp(x):
  with tf.GradientTape() as tape:
    tape.watch(x)
    value = log1pexp(x)
  return tape.gradient(value, x)

# As before, the gradient computation works fine at x = 0.
grad_log1pexp(tf.constant(0.)).numpy()
0.5
# And the gradient computation also works at x = 100.
grad_log1pexp(tf.constant(100.)).numpy()
1.0

Actuación

La computación se descarga automáticamente a las GPU durante la ejecución ansiosa. Si desea controlar dónde se ejecuta un cálculo, puede encerrarlo en un tf.device('/gpu:0') (o el equivalente de la CPU):

import time

def measure(x, steps):
  # TensorFlow initializes a GPU the first time it's used, exclude from timing.
  tf.matmul(x, x)
  start = time.time()
  for i in range(steps):
    x = tf.matmul(x, x)
  # tf.matmul can return before completing the matrix multiplication
  # (e.g., can return after enqueing the operation on a CUDA stream).
  # The x.numpy() call below will ensure that all enqueued operations
  # have completed (and will also copy the result to host memory,
  # so we're including a little more than just the matmul operation
  # time).
  _ = x.numpy()
  end = time.time()
  return end - start

shape = (1000, 1000)
steps = 200
print("Time to multiply a {} matrix by itself {} times:".format(shape, steps))

# Run on CPU:
with tf.device("/cpu:0"):
  print("CPU: {} secs".format(measure(tf.random.normal(shape), steps)))

# Run on GPU, if available:
if tf.config.experimental.list_physical_devices("GPU"):
  with tf.device("/gpu:0"):
    print("GPU: {} secs".format(measure(tf.random.normal(shape), steps)))
else:
  print("GPU: not found")
Time to multiply a (1000, 1000) matrix by itself 200 times:
CPU: 0.9788374900817871 secs
GPU: 0.04241943359375 secs

Un objeto tf.Tensor se puede copiar a un dispositivo diferente para ejecutar sus operaciones:

if tf.config.experimental.list_physical_devices("GPU"):
  x = tf.random.normal([10, 10])

  x_gpu0 = x.gpu()
  x_cpu = x.cpu()

  _ = tf.matmul(x_cpu, x_cpu)    # Runs on CPU
  _ = tf.matmul(x_gpu0, x_gpu0)  # Runs on GPU:0
WARNING:tensorflow:From <ipython-input-1-876293b5769c>:4: _EagerTensorBase.gpu (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.identity instead.
WARNING:tensorflow:From <ipython-input-1-876293b5769c>:5: _EagerTensorBase.cpu (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.identity instead.

Benchmarks

Para modelos con gran capacidad informática, como el entrenamiento ResNet50 en una GPU, el rendimiento de ejecución ávido es comparable a la ejecución de tf.function . Pero esta brecha se hace más grande para los modelos con menos computación y hay trabajo por hacer para optimizar las rutas de código activo para modelos con muchas operaciones pequeñas.

Trabajar con funciones

Si bien la ejecución ávida hace que el desarrollo y la depuración sean más interactivos, la ejecución de gráficos de estilo TensorFlow 1.x tiene ventajas para el entrenamiento distribuido, la optimización del rendimiento y la implementación de producción. Para cerrar esta brecha, TensorFlow 2.0 introduce function a través de la API tf.function . Para obtener más información, consulte la guía de funciones tf .