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

Ansiosa ejecución

Ver en TensorFlow.org Ejecutar en Google Colab Ver código fuente en GitHub Descargar cuaderno

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

Eagercution es una plataforma flexible de aprendizaje automático para investigación y experimentación, que proporciona:

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

La ejecución rápida 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 manera predeterminada.

 tf.executing_eagerly()
 
True

Ahora puede ejecutar operaciones TensorFlow y los resultados volverán inmediatamente:

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

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

La ejecución ansiosa funciona bien con NumPy . Las operaciones NumPy aceptan argumentos de tf.Tensor . Las operaciones TensorFlow tf.math convierten los objetos Python y las matrices NumPy en objetos tf.Tensor . El método tf.Tensor.numpy devuelve el valor del objeto como una 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

Una ventaja importante de la ejecución entusiasta es que toda la funcionalidad del idioma del host está disponible mientras se ejecuta el 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.

Ansioso entrenamiento

Gradientes de computación

La diferenciación automática es útil para implementar algoritmos de aprendizaje automático, como la retropropagación para el entrenamiento de 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 ansiosos. Es especialmente útil para complicados bucles de entrenamiento.

Debido a que pueden ocurrir diferentes operaciones durante cada llamada, todas las operaciones de transferencia directa se graban en una "cinta". Para calcular el gradiente, reproduzca la cinta hacia atrá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)

Entrenar a una modelo

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

 # 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 capacitación, llame al modelo e inspeccione la salida en ejecución ansiosa:

 for images,labels in dataset.take(1):
  print("Logits: ", mnist_model(images[0:1]).numpy())
 
Logits:  [[-0.04290903  0.05850095 -0.09306249  0.01919096 -0.04339378  0.09877204
  -0.16061416  0.03829113 -0.04268045 -0.01678811]]

Si bien los modelos keras tienen un ciclo de entrenamiento incorporado (utilizando el método de fit ), a veces necesita más personalización. Aquí hay un ejemplo de un ciclo de entrenamiento implementado con ansias:

 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 de tipo 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 en ellas. Ver capas y modelos de Keras personalizados para 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 puede reescribirse:

 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. Los derivados de una función de pérdida con respecto a los parámetros del modelo.
  3. Una estrategia para actualizar las variables basadas en los derivados.
 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.574
Loss at step 000: 65.927
Loss at step 020: 30.202
Loss at step 040: 14.151
Loss at step 060: 6.936
Loss at step 080: 3.692
Loss at step 100: 2.233
Loss at step 120: 1.576
Loss at step 140: 1.281
Loss at step 160: 1.148
Loss at step 180: 1.088
Loss at step 200: 1.061
Loss at step 220: 1.049
Loss at step 240: 1.043
Loss at step 260: 1.041
Loss at step 280: 1.040

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

 print("W = {}, B = {}".format(model.W.numpy(), model.B.numpy()))
 
W = 2.988429069519043, B = 2.008305311203003

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

Usando tf.train.Checkpoint puedes tomar el control total sobre este proceso.

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

 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 0x7f9dcc8be588>

Métricas orientadas a objetos

tf.keras.metrics se almacenan como objetos. Actualice una métrica pasando los nuevos datos al invocable y recupere el resultado utilizando 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 capacitación 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 una 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.1595467632.kokoro-gcp-ubuntu-prod-341901152.10415.619671.v2

Temas avanzados de diferenciación automática

Modelos dinámicos

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

 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
 

Gradientes personalizados

Los degradados personalizados son una manera 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 gradientes en el paso 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 gradientes personalizados se usan comúnmente para proporcionar un gradiente 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 directo, 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.9854905605316162 secs
GPU: 0.0423429012298584 secs

Un objeto tf.Tensor se puede copiar en 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-43-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-43-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.

Puntos de referencia

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

Trabajar con funciones

Si bien la ejecución entusiasta 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 la capacitación distribuida, las optimizaciones de 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 .