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

TensorFlow 2 efectivo

Hay varios 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 ( RNN unificados , optimizadores unificados ) y se integra mejor con el tiempo de ejecución de Python con ejecución Eager .

Muchas RFC han explicado los cambios que se han realizado para hacer 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 desaparecieron o se movieron en TF 2.0. Algunos de los cambios principales incluyen eliminar tf.app , tf.flags y tf.logging a favor de 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 en 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 sencilla de aplicar automáticamente estos cambios de nombre es utilizar el script de actualización v2 .

Ejecución ansiosa

TensorFlow 1.X requiere que los usuarios unan manualmente un árbol de sintaxis abstracto (el gráfico) haciendo llamadas a la API tf.* . 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 deben sentirse como detalles de implementación.

Un subproducto notable de la ejecución ávida 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 globales implícitamente. Cuando tf.Variable() , se colocaría en el gráfico predeterminado y permanecería allí, incluso si perdiste la pista de la variable de Python que apuntaba a ella. A continuación, podría recuperar esa tf.Variable , pero solo si conocía 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, y así sucesivamente. TensorFlow 2.0 elimina todos estos mecanismos ( Variables 2.0 RFC ) a favor del mecanismo predeterminado: ¡Mantenga un registro de sus variables! Si pierde el rastro de una tf.Variable , Se recolecta la basura.

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

Funciones, no sesiones

Una llamada a session.run() es casi como una llamada a una función: usted especifica las entradas y la función a llamar, y obtiene un conjunto de salidas. En TensorFlow 2.0, puedes decorar una función de Python usando tf.function() para marcarla para la compilación JIT de modo 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 / volver a importar ( 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 código de Python y TensorFlow, los usuarios pueden aprovechar la expresividad de Python. Pero 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 de TensorFlow:

AutoGraph admite anidamientos arbitrarios de flujo de control, lo que hace posible implementar de manera concisa y eficiente muchos programas de AA complejos, como modelos de secuencia, aprendizaje por refuerzo, ciclos 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 cocina", donde la unión de todos los cálculos posibles se estableció de manera preventiva y luego se evaluaron tensores seleccionados mediante 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álculos de alto nivel, por ejemplo, un paso de entrenamiento o el pase de avance de su modelo.

Use capas y modelos de Keras para administrar variables

Los modelos y capas de Keras ofrecen las variables convenientes y las propiedades trainable_variables , que recopilan de forma recursiva todas las variables dependientes. Esto facilita la gestión de variables de forma local en el lugar donde se utilizan.

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 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 objetos de Keras. No es necesario que utilice la API .fit() Keras para aprovechar estas integraciones.

Aquí hay un ejemplo de aprendizaje por transferencia que demuestra cómo Keras facilita la recopilación de un subconjunto de variables relevantes. Supongamos que está entrenando 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)

Combinar tf.data.Datasets y @ tf.function

Al iterar sobre datos de entrenamiento que quepan en la memoria, no dude en 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 como 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 de gráficos 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 los datos en equivalentes en modo gráfico como tf.cond y tf.while_loop . 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, puede 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 general más detallada de las funciones 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 redirigirlo a un escritor usando un administrador de contexto. (Si omite el administrador de contexto, no sucede nada). A diferencia de TF 1.x, los resúmenes se envían directamente al escritor; no hay una add_summary() "fusión" separada add_summary() llamada add_summary() separada, lo que significa que el valor del step debe proporcionarse en el sitio de llamada.

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 llama a .result() . Borre 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 al señalar TensorBoard en el directorio de registro de resumen:

tensorboard --logdir /tmp/summaries

Utilice tf.config.experimental_run_functions_eagerly () al depurar

En TensorFlow 2.0, la ejecución ansiosa 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 utilizar la ejecución de Graph, por 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 de 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]))