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

TensorFlow eficaz 2

Existen múltiples cambios en TensorFlow 2.0 para que los usuarios de TensorFlow sean más productivos. TensorFlow 2.0 elimina las API redundantes , hace que las API sean más consistentes ( Unified RNN , Unified Optimizers ) y se integra mejor con el tiempo de ejecución de Python con la ejecución Eager .

Muchas RFC han explicado los cambios que se han producido para crear TensorFlow 2.0. Esta guía presenta una visión de cómo debería ser el desarrollo en TensorFlow 2.0. Se supone que está familiarizado con TensorFlow 1.x.

Un breve resumen de los principales cambios.

Limpieza de API

Muchas API se han ido o movido en TF 2.0. Algunos de los cambios más importantes incluyen eliminar tf.app , tf.flags y tf.logging a favor del absl-py ahora de código abierto, reubicar proyectos que vivían en tf.contrib y limpiar el espacio de nombres tf.* Principal mediante mover funciones menos utilizadas a subpaquetes como tf.math . Algunas API se han reemplazado por sus equivalentes 2.0: tf.summary , tf.keras.metrics y tf.keras.optimizers . La forma más fácil de aplicar automáticamente estos cambios de nombre es usar el script de actualización v2 .

Ansiosa ejecución

TensorFlow 1.X requiere que los usuarios unan manualmente un árbol de sintaxis abstracta (el gráfico) haciendo tf.* Llamadas API. Luego requiere que los usuarios compilen manualmente el árbol de sintaxis abstracta pasando un conjunto de tensores de salida y tensores de entrada a una llamada session.run() . TensorFlow 2.0 se ejecuta con entusiasmo (como lo hace normalmente Python) y en 2.0, los gráficos y las sesiones deberían parecer detalles de implementación.

Un subproducto notable de la ejecución ansiosa es que tf.control_dependencies() ya no es necesario, ya que todas las líneas de código se ejecutan en orden (dentro de una función tf.function , el código con efectos secundarios se ejecuta en el orden escrito).

No más globales

TensorFlow 1.X se basó en gran medida en espacios de nombres implícitamente globales. Cuando tf.Variable() , se colocaría en el gráfico predeterminado y permanecería allí, incluso si perdías el rastro de la variable Python que lo apuntaba. A continuación, puede recuperar esa tf.Variable , pero solo si conoce el nombre con el que se creó. Esto era difícil de hacer si no tenía el control de la creación de la variable. Como resultado, proliferaron todo tipo de mecanismos para intentar ayudar a los usuarios a encontrar sus variables nuevamente, y para que los marcos encuentren variables creadas por el usuario: ámbitos variables, colecciones globales, métodos auxiliares como tf.get_global_step() , tf.global_variables_initializer() , optimizadores que calculan implícitamente gradientes sobre todas las variables entrenables, etc. TensorFlow 2.0 elimina todos estos mecanismos ( Variables 2.0 RFC ) a favor del mecanismo predeterminado: ¡Haga un seguimiento de sus variables! Si pierde el rastro de un tf.Variable . tf.Variable , se recoge la basura.

El requisito de rastrear variables crea un trabajo adicional para el usuario, pero con los objetos Keras (ver más abajo), la carga se minimiza.

Funciones, no sesiones

Una llamada session.run() es casi como una llamada de función: especifica las entradas y la función que se va a llamar, y obtiene un conjunto de salidas. En TensorFlow 2.0, puede decorar una función de Python usando tf.function() para marcarla para la compilación JIT para que TensorFlow la ejecute como un solo gráfico ( Funciones 2.0 RFC ). Este mecanismo permite que TensorFlow 2.0 obtenga todos los beneficios del modo gráfico:

  • Rendimiento: la función se puede optimizar (poda de nodos, fusión de kernel, etc.)
  • Portabilidad: la función se puede exportar / reimportar ( SavedModel 2.0 RFC ), lo que permite a los usuarios reutilizar y compartir funciones modulares de TensorFlow.
 # TensorFlow 1.X
outputs = session.run(f(placeholder), feed_dict={placeholder: input})
# TensorFlow 2.0
outputs = f(input)
 

Con el poder de intercalar libremente los códigos Python y TensorFlow, los usuarios pueden aprovechar la expresividad de Python. Pero el TensorFlow portátil se ejecuta en contextos sin un intérprete de Python, como dispositivos móviles, C ++ y JavaScript. Para ayudar a los usuarios a evitar tener que reescribir su código al agregar @tf.function , AutoGraph convierte un subconjunto de construcciones de Python en sus equivalentes TensorFlow:

AutoGraph admite anidaciones arbitrarias del flujo de control, lo que hace posible implementar de manera concisa y con precisión muchos programas complejos de ML, como modelos de secuencia, aprendizaje de refuerzo, bucles de entrenamiento personalizados y más.

Recomendaciones para TensorFlow 2.0 idiomático

Refactorice su código en funciones más pequeñas

Un patrón de uso común en TensorFlow 1.X fue la estrategia de "fregadero de la cocina", donde se estableció preventivamente la unión de todos los cálculos posibles, y luego los tensores seleccionados se evaluaron a través de session.run() . En TensorFlow 2.0, los usuarios deben refactorizar su código en funciones más pequeñas que se llaman según sea necesario. En general, no es necesario decorar cada una de estas funciones más pequeñas con tf.function ; solo use tf.function para decorar cómputos de alto nivel, por ejemplo, un paso de entrenamiento o el paso adelante de su modelo.

Use capas y modelos de Keras para administrar variables

Modelos y capas Keras ofrecen las convenientes variables y trainable_variables propiedades, que se reúnen de forma recursiva hasta que todas las variables dependientes. Esto facilita la administración local de variables donde se están utilizando.

Contraste:

 def dense(x, W, b):
  return tf.nn.sigmoid(tf.matmul(x, W) + b)

@tf.function
def multilayer_perceptron(x, w0, b0, w1, b1, w2, b2 ...):
  x = dense(x, w0, b0)
  x = dense(x, w1, b1)
  x = dense(x, w2, b2)
  ...

# You still have to manage w_i and b_i, and their shapes are defined far away from the code.
 

con la versión de Keras:

 # Each layer can be called, with a signature equivalent to linear(x)
layers = [tf.keras.layers.Dense(hidden_size, activation=tf.nn.sigmoid) for _ in range(n)]
perceptron = tf.keras.Sequential(layers)

# layers[3].trainable_variables => returns [w3, b3]
# perceptron.trainable_variables => returns [w0, b0, ...]
 

Las capas / modelos de Keras heredan de tf.train.Checkpointable y se integran con @tf.function , lo que hace posible el control directo o la exportación de modelos guardados de los objetos de Keras. No necesariamente tiene que usar la API .fit() Keras para aprovechar estas integraciones.

Aquí hay un ejemplo de aprendizaje de transferencia que demuestra cómo Keras facilita la recopilación de un subconjunto de variables relevantes. Digamos que estás entrenando a un modelo de varias cabezas con un tronco compartido:

 trunk = tf.keras.Sequential([...])
head1 = tf.keras.Sequential([...])
head2 = tf.keras.Sequential([...])

path1 = tf.keras.Sequential([trunk, head1])
path2 = tf.keras.Sequential([trunk, head2])

# Train on primary dataset
for x, y in main_dataset:
  with tf.GradientTape() as tape:
    # training=True is only needed if there are layers with different
    # behavior during training versus inference (e.g. Dropout).
    prediction = path1(x, training=True)
    loss = loss_fn_head1(prediction, y)
  # Simultaneously optimize trunk and head1 weights.
  gradients = tape.gradient(loss, path1.trainable_variables)
  optimizer.apply_gradients(zip(gradients, path1.trainable_variables))

# Fine-tune second head, reusing the trunk
for x, y in small_dataset:
  with tf.GradientTape() as tape:
    # training=True is only needed if there are layers with different
    # behavior during training versus inference (e.g. Dropout).
    prediction = path2(x, training=True)
    loss = loss_fn_head2(prediction, y)
  # Only optimize head2 weights, not trunk weights
  gradients = tape.gradient(loss, head2.trainable_variables)
  optimizer.apply_gradients(zip(gradients, head2.trainable_variables))

# You can publish just the trunk computation for other people to reuse.
tf.saved_model.save(trunk, output_path)
 

Combine tf.data.Datasets y @ tf.function

Al iterar sobre los datos de entrenamiento que se ajustan en la memoria, siéntase libre de usar la iteración regular de Python. De lo contrario, tf.data.Dataset es la mejor manera de transmitir datos de entrenamiento desde el disco. Los conjuntos de datos son iterables (no iteradores) y funcionan igual que otros iterables de Python en modo Eager. Puede utilizar completamente las funciones de tf.function() / transmisión asíncrona de conjuntos de datos tf.function() su código en tf.function() , que reemplaza la iteración de Python con las operaciones gráficas equivalentes usando AutoGraph.

 @tf.function
def train(model, dataset, optimizer):
  for x, y in dataset:
    with tf.GradientTape() as tape:
      # training=True is only needed if there are layers with different
      # behavior during training versus inference (e.g. Dropout).
      prediction = model(x, training=True)
      loss = loss_fn(prediction, y)
    gradients = tape.gradient(loss, model.trainable_variables)
    optimizer.apply_gradients(zip(gradients, model.trainable_variables))
 

Si usa la .fit() Keras .fit() , no tendrá que preocuparse por la iteración del conjunto de datos.

 model.compile(optimizer=optimizer, loss=loss_fn)
model.fit(dataset)
 

Aproveche AutoGraph con el flujo de control de Python

AutoGraph proporciona una forma de convertir el flujo de control dependiente de datos en equivalentes en modo gráfico como tf.cond y tf.while_loop .

Un lugar común donde aparece el flujo de control dependiente de los datos es en los modelos de secuencia. tf.keras.layers.RNN envuelve una celda RNN, lo que le permite desenrollar estática o dinámicamente la recurrencia. Por el bien de la demostración, podría volver a implementar el desenrollado dinámico de la siguiente manera:

 class DynamicRNN(tf.keras.Model):

  def __init__(self, rnn_cell):
    super(DynamicRNN, self).__init__(self)
    self.cell = rnn_cell

  def call(self, input_data):
    # [batch, time, features] -> [time, batch, features]
    input_data = tf.transpose(input_data, [1, 0, 2])
    outputs = tf.TensorArray(tf.float32, input_data.shape[0])
    state = self.cell.zero_state(input_data.shape[1], dtype=tf.float32)
    for i in tf.range(input_data.shape[0]):
      output, state = self.cell(input_data[i], state)
      outputs = outputs.write(i, output)
    return tf.transpose(outputs.stack(), [1, 0, 2]), state
 

Para obtener una descripción más detallada de las características de AutoGraph, consulte la guía .

tf.metrics agrega datos y tf.summary los registra

Para registrar resúmenes, use tf.summary.(scalar|histogram|...) y rediríjalo a un escritor usando un administrador de contexto. (Si omite el administrador de contexto, no pasa nada). A diferencia de TF 1.x, los resúmenes se emiten directamente al escritor; no hay una add_summary() "merge" separada add_summary() llamada add_summary() separada, lo que significa que el valor del step debe proporcionarse en el sitio de llamadas.

 summary_writer = tf.summary.create_file_writer('/tmp/summaries')
with summary_writer.as_default():
  tf.summary.scalar('loss', 0.1, step=42)
 

Para agregar datos antes de registrarlos como resúmenes, use tf.metrics . Las métricas tienen estado: acumulan valores y devuelven un resultado acumulativo cuando se llama .result() . Borrar los valores acumulados con .reset_states() .

 def train(model, optimizer, dataset, log_freq=10):
  avg_loss = tf.keras.metrics.Mean(name='loss', dtype=tf.float32)
  for images, labels in dataset:
    loss = train_step(model, optimizer, images, labels)
    avg_loss.update_state(loss)
    if tf.equal(optimizer.iterations % log_freq, 0):
      tf.summary.scalar('loss', avg_loss.result(), step=optimizer.iterations)
      avg_loss.reset_states()

def test(model, test_x, test_y, step_num):
  # training=False is only needed if there are layers with different
  # behavior during training versus inference (e.g. Dropout).
  loss = loss_fn(model(test_x, training=False), test_y)
  tf.summary.scalar('loss', loss, step=step_num)

train_summary_writer = tf.summary.create_file_writer('/tmp/summaries/train')
test_summary_writer = tf.summary.create_file_writer('/tmp/summaries/test')

with train_summary_writer.as_default():
  train(model, optimizer, dataset)

with test_summary_writer.as_default():
  test(model, test_x, test_y, optimizer.iterations)
 

Visualice los resúmenes generados señalando TensorBoard en el directorio de registro de resumen:

 tensorboard --logdir /tmp/summaries
 

Use tf.config.experimental_run_functions_eagerly () al depurar

En TensorFlow 2.0, la ejecución Eager le permite ejecutar el código paso a paso para inspeccionar formas, tipos de datos y valores. Ciertas API, como tf.function , tf.keras , etc. están diseñadas para usar la ejecución Graph, para rendimiento y portabilidad. Al depurar, use tf.config.experimental_run_functions_eagerly(True) para usar la ejecución Eager dentro de este código.

Por ejemplo:

 @tf.function
def f(x):
  if x > 0:
    import pdb
    pdb.set_trace()
    x = x + 1
  return x

tf.config.experimental_run_functions_eagerly(True)
f(tf.constant(1))
 
 >>> f()
-> x = x + 1
(Pdb) l
  6     @tf.function
  7     def f(x):
  8       if x > 0:
  9         import pdb
 10         pdb.set_trace()
 11  ->     x = x + 1
 12       return x
 13
 14     tf.config.experimental_run_functions_eagerly(True)
 15     f(tf.constant(1))
[EOF]
 

Esto también funciona dentro de los modelos Keras y otras API que admiten la ejecución Eager:

 class CustomModel(tf.keras.models.Model):

  @tf.function
  def call(self, input_data):
    if tf.reduce_mean(input_data) > 0:
      return input_data
    else:
      import pdb
      pdb.set_trace()
      return input_data // 2


tf.config.experimental_run_functions_eagerly(True)
model = CustomModel()
model(tf.constant([-2, -4]))
 
 >>> call()
-> return input_data // 2
(Pdb) l
 10         if tf.reduce_mean(input_data) > 0:
 11           return input_data
 12         else:
 13           import pdb
 14           pdb.set_trace()
 15  ->       return input_data // 2
 16
 17
 18     tf.config.experimental_run_functions_eagerly(True)
 19     model = CustomModel()
 20     model(tf.constant([-2, -4]))