¡El Día de la Comunidad de ML es el 9 de noviembre! Únase a nosotros para recibir actualizaciones de TensorFlow, JAX, y más Más información

Introducción a las gráficas y la función tf.

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

Visión general

Esta guía analiza la superficie de TensorFlow y Keras para demostrar cómo funciona TensorFlow. Si por el contrario desea empezar inmediatamente con Keras, echa un vistazo a la colección de guías Keras .

En esta guía, aprenderá cómo TensorFlow le permite realizar cambios simples en su código para obtener gráficos, cómo se almacenan y representan los gráficos y cómo puede usarlos para acelerar sus modelos.

Esta es una visión general del panorama general que explica cómo tf.function le permite cambiar de ejecución ganas de ejecución de gráficos. Para una especificación más completa de tf.function , vaya a la tf.function guía .

¿Qué son las gráficas?

En las tres guías anteriores, se ejecutó TensorFlow con impaciencia. Esto significa que Python ejecuta las operaciones de TensorFlow, operación por operación, y devuelve los resultados a Python.

Si bien la ejecución ávida tiene varias ventajas únicas, la ejecución de gráficos permite la portabilidad fuera de Python y tiende a ofrecer un mejor rendimiento. Medios de ejecución de gráficos que cálculos tensoriales se ejecutan como un gráfico TensorFlow, a veces referido como un tf.Graph o simplemente un "gráfico".

Los gráficos son estructuras de datos que contienen un conjunto de tf.Operation objetos, que representan unidades de cálculo; y tf.Tensor objetos, que representan las unidades de datos que fluyen entre operaciones. Ellos se definen en un tf.Graph contexto. Dado que estos gráficos son estructuras de datos, se pueden guardar, ejecutar y restaurar sin el código Python original.

Así es como se ve un gráfico de TensorFlow que representa una red neuronal de dos capas cuando se visualiza en TensorBoard.

Un gráfico de TensorFlow simple

Los beneficios de los gráficos

Con un gráfico, tiene mucha flexibilidad. Puede usar su gráfico de TensorFlow en entornos que no tienen un intérprete de Python, como aplicaciones móviles, dispositivos integrados y servidores backend. TensorFlow utiliza gráficos como el formato de los modelos guardados cuando los exporta desde Python.

Los gráficos también se optimizan fácilmente, lo que permite al compilador realizar transformaciones como:

  • Estáticamente inferir el valor de los tensores por plegado nodos constantes en su cálculo ( "plegado constante").
  • Separe las subpartes de un cálculo que sean independientes y divídalas entre subprocesos o dispositivos.
  • Simplifique las operaciones aritméticas eliminando subexpresiones comunes.

Hay todo un sistema de optimización, Grappler , para realizar esta y otras aceleraciones.

En resumen, los gráficos son muy útiles y permiten su TensorFlow correr rápido, correr en paralelo, y se ejecutan de manera eficiente en múltiples dispositivos.

Sin embargo, aún desea definir sus modelos de aprendizaje automático (u otros cálculos) en Python por conveniencia, y luego construir gráficos automáticamente cuando los necesite.

Configuración

import tensorflow as tf
import timeit
from datetime import datetime

Aprovechando las gráficas

Crear y ejecutar un gráfico en TensorFlow utilizando tf.function , ya sea como un llamado directo o como un decorador. tf.function tiene una función regular como entrada y devuelve una Function . Una Function es una exigible Python que construye gráficos TensorFlow de la función de Python. Se utiliza una Function de la misma forma que su equivalente Python.

# Define a Python function.
def a_regular_function(x, y, b):
  x = tf.matmul(x, y)
  x = x + b
  return x

# `a_function_that_uses_a_graph` is a TensorFlow `Function`.
a_function_that_uses_a_graph = tf.function(a_regular_function)

# Make some tensors.
x1 = tf.constant([[1.0, 2.0]])
y1 = tf.constant([[2.0], [3.0]])
b1 = tf.constant(4.0)

orig_value = a_regular_function(x1, y1, b1).numpy()
# Call a `Function` like a Python function.
tf_function_value = a_function_that_uses_a_graph(x1, y1, b1).numpy()
assert(orig_value == tf_function_value)

En el exterior, una Function se parece a una función regular se escriben haciendo uso de las operaciones TensorFlow. Debajo , sin embargo, es muy diferente. Una Function encapsula varias tf.Graph s detrás de una API . Así es como Function es capaz de darle los beneficios de la ejecución de gráficos , como la velocidad y la capacidad de despliegue.

tf.function se aplica a una función y todas las demás funciones que llama:

def inner_function(x, y, b):
  x = tf.matmul(x, y)
  x = x + b
  return x

# Use the decorator to make `outer_function` a `Function`.
@tf.function
def outer_function(x):
  y = tf.constant([[2.0], [3.0]])
  b = tf.constant(4.0)

  return inner_function(x, y, b)

# Note that the callable will create a graph that
# includes `inner_function` as well as `outer_function`.
outer_function(tf.constant([[1.0, 2.0]])).numpy()
array([[12.]], dtype=float32)

Si ha utilizado TensorFlow 1.x, se dará cuenta de que en ningún momento es necesario definir un Placeholder o tf.Session .

Conversión de funciones de Python en gráficos

Cualquier función que se escriba con TensorFlow contendrá una mezcla de una función de operaciones TF y la lógica de Python, como if-then cláusulas, bucles, break , return , continue , y mucho más. Mientras que las operaciones TensorFlow son fácilmente capturado por un tf.Graph , necesidades lógicas Python-específicos para someterse a un paso adicional para llegar a ser parte de la gráfica. tf.function utiliza una biblioteca llamada AutoGraph ( tf.autograph ) para convertir código Python en código de generación de gráfico.

def simple_relu(x):
  if tf.greater(x, 0):
    return x
  else:
    return 0

# `tf_simple_relu` is a TensorFlow `Function` that wraps `simple_relu`.
tf_simple_relu = tf.function(simple_relu)

print("First branch, with graph:", tf_simple_relu(tf.constant(1)).numpy())
print("Second branch, with graph:", tf_simple_relu(tf.constant(-1)).numpy())
First branch, with graph: 1
Second branch, with graph: 0

Aunque es poco probable que necesite ver los gráficos directamente, puede inspeccionar las salidas para verificar los resultados exactos. Estos no son fáciles de leer, por lo que no es necesario mirar con demasiada atención.

# This is the graph-generating output of AutoGraph.
print(tf.autograph.to_code(simple_relu))
def tf__simple_relu(x):
    with ag__.FunctionScope('simple_relu', 'fscope', ag__.ConversionOptions(recursive=True, user_requested=True, optional_features=(), internal_convert_user_code=True)) as fscope:
        do_return = False
        retval_ = ag__.UndefinedReturnValue()

        def get_state():
            return (do_return, retval_)

        def set_state(vars_):
            nonlocal retval_, do_return
            (do_return, retval_) = vars_

        def if_body():
            nonlocal retval_, do_return
            try:
                do_return = True
                retval_ = ag__.ld(x)
            except:
                do_return = False
                raise

        def else_body():
            nonlocal retval_, do_return
            try:
                do_return = True
                retval_ = 0
            except:
                do_return = False
                raise
        ag__.if_stmt(ag__.converted_call(ag__.ld(tf).greater, (ag__.ld(x), 0), None, fscope), if_body, else_body, get_state, set_state, ('do_return', 'retval_'), 2)
        return fscope.ret(retval_, do_return)
# This is the graph itself.
print(tf_simple_relu.get_concrete_function(tf.constant(1)).graph.as_graph_def())
node {
  name: "x"
  op: "Placeholder"
  attr {
    key: "_user_specified_name"
    value {
      s: "x"
    }
  }
  attr {
    key: "dtype"
    value {
      type: DT_INT32
    }
  }
  attr {
    key: "shape"
    value {
      shape {
      }
    }
  }
}
node {
  name: "Greater/y"
  op: "Const"
  attr {
    key: "dtype"
    value {
      type: DT_INT32
    }
  }
  attr {
    key: "value"
    value {
      tensor {
        dtype: DT_INT32
        tensor_shape {
        }
        int_val: 0
      }
    }
  }
}
node {
  name: "Greater"
  op: "Greater"
  input: "x"
  input: "Greater/y"
  attr {
    key: "T"
    value {
      type: DT_INT32
    }
  }
}
node {
  name: "cond"
  op: "StatelessIf"
  input: "Greater"
  input: "x"
  attr {
    key: "Tcond"
    value {
      type: DT_BOOL
    }
  }
  attr {
    key: "Tin"
    value {
      list {
        type: DT_INT32
      }
    }
  }
  attr {
    key: "Tout"
    value {
      list {
        type: DT_BOOL
        type: DT_INT32
      }
    }
  }
  attr {
    key: "_lower_using_switch_merge"
    value {
      b: true
    }
  }
  attr {
    key: "_read_only_resource_inputs"
    value {
      list {
      }
    }
  }
  attr {
    key: "else_branch"
    value {
      func {
        name: "cond_false_34"
      }
    }
  }
  attr {
    key: "output_shapes"
    value {
      list {
        shape {
        }
        shape {
        }
      }
    }
  }
  attr {
    key: "then_branch"
    value {
      func {
        name: "cond_true_33"
      }
    }
  }
}
node {
  name: "cond/Identity"
  op: "Identity"
  input: "cond"
  attr {
    key: "T"
    value {
      type: DT_BOOL
    }
  }
}
node {
  name: "cond/Identity_1"
  op: "Identity"
  input: "cond:1"
  attr {
    key: "T"
    value {
      type: DT_INT32
    }
  }
}
node {
  name: "Identity"
  op: "Identity"
  input: "cond/Identity_1"
  attr {
    key: "T"
    value {
      type: DT_INT32
    }
  }
}
library {
  function {
    signature {
      name: "cond_false_34"
      input_arg {
        name: "cond_placeholder"
        type: DT_INT32
      }
      output_arg {
        name: "cond_identity"
        type: DT_BOOL
      }
      output_arg {
        name: "cond_identity_1"
        type: DT_INT32
      }
    }
    node_def {
      name: "cond/Const"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_BOOL
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_BOOL
            tensor_shape {
            }
            bool_val: true
          }
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Const"
      }
    }
    node_def {
      name: "cond/Const_1"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_BOOL
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_BOOL
            tensor_shape {
            }
            bool_val: true
          }
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Const_1"
      }
    }
    node_def {
      name: "cond/Const_2"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_INT32
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_INT32
            tensor_shape {
            }
            int_val: 0
          }
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Const_2"
      }
    }
    node_def {
      name: "cond/Const_3"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_BOOL
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_BOOL
            tensor_shape {
            }
            bool_val: true
          }
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Const_3"
      }
    }
    node_def {
      name: "cond/Identity"
      op: "Identity"
      input: "cond/Const_3:output:0"
      attr {
        key: "T"
        value {
          type: DT_BOOL
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Identity"
      }
    }
    node_def {
      name: "cond/Const_4"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_INT32
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_INT32
            tensor_shape {
            }
            int_val: 0
          }
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Const_4"
      }
    }
    node_def {
      name: "cond/Identity_1"
      op: "Identity"
      input: "cond/Const_4:output:0"
      attr {
        key: "T"
        value {
          type: DT_INT32
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Identity_1"
      }
    }
    ret {
      key: "cond_identity"
      value: "cond/Identity:output:0"
    }
    ret {
      key: "cond_identity_1"
      value: "cond/Identity_1:output:0"
    }
    attr {
      key: "_construction_context"
      value {
        s: "kEagerRuntime"
      }
    }
    arg_attr {
      key: 0
      value {
        attr {
          key: "_output_shapes"
          value {
            list {
              shape {
              }
            }
          }
        }
      }
    }
  }
  function {
    signature {
      name: "cond_true_33"
      input_arg {
        name: "cond_identity_1_x"
        type: DT_INT32
      }
      output_arg {
        name: "cond_identity"
        type: DT_BOOL
      }
      output_arg {
        name: "cond_identity_1"
        type: DT_INT32
      }
    }
    node_def {
      name: "cond/Const"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_BOOL
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_BOOL
            tensor_shape {
            }
            bool_val: true
          }
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Const"
      }
    }
    node_def {
      name: "cond/Identity"
      op: "Identity"
      input: "cond/Const:output:0"
      attr {
        key: "T"
        value {
          type: DT_BOOL
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Identity"
      }
    }
    node_def {
      name: "cond/Identity_1"
      op: "Identity"
      input: "cond_identity_1_x"
      attr {
        key: "T"
        value {
          type: DT_INT32
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Identity_1"
      }
    }
    ret {
      key: "cond_identity"
      value: "cond/Identity:output:0"
    }
    ret {
      key: "cond_identity_1"
      value: "cond/Identity_1:output:0"
    }
    attr {
      key: "_construction_context"
      value {
        s: "kEagerRuntime"
      }
    }
    arg_attr {
      key: 0
      value {
        attr {
          key: "_output_shapes"
          value {
            list {
              shape {
              }
            }
          }
        }
      }
    }
  }
}
versions {
  producer: 808
  min_consumer: 12
}

La mayoría de las veces, tf.function funcionará sin consideraciones especiales. Sin embargo, hay algunas advertencias, y la guía tf.function pueden ayudar aquí, así como la referencia completa AutoGraph

El polimorfismo: una Function , muchos gráficos

A tf.Graph está especializada para un tipo específico de entradas (por ejemplo, los tensores con una específica dtype o los objetos con el mismo id() ).

Cada vez que se invoca una Function con nuevos dtypes y formas en sus argumentos, Function crea un nuevo tf.Graph para los nuevos argumentos. Los dtypes y formas de una tf.Graph entradas 's son conocidos como una firma de entrada o simplemente una firma.

Las Function almacena el tf.Graph correspondiente a la firma en un ConcreteFunction . Un ConcreteFunction es una envoltura alrededor de un tf.Graph .

@tf.function
def my_relu(x):
  return tf.maximum(0., x)

# `my_relu` creates new graphs as it observes more signatures.
print(my_relu(tf.constant(5.5)))
print(my_relu([1, -1]))
print(my_relu(tf.constant([3., -3.])))
tf.Tensor(5.5, shape=(), dtype=float32)
tf.Tensor([1. 0.], shape=(2,), dtype=float32)
tf.Tensor([3. 0.], shape=(2,), dtype=float32)

Si la Function ya se ha llamado con esa firma, Function no crea un nuevo tf.Graph .

# These two calls do *not* create new graphs.
print(my_relu(tf.constant(-2.5))) # Signature matches `tf.constant(5.5)`.
print(my_relu(tf.constant([-1., 1.]))) # Signature matches `tf.constant([3., -3.])`.
tf.Tensor(0.0, shape=(), dtype=float32)
tf.Tensor([0. 1.], shape=(2,), dtype=float32)

Porque está respaldado por múltiples gráficos, una Function es polimórfico. Que le permite soportar tipos de entrada más que una sola tf.Graph podría representar, así como para optimizar cada tf.Graph para un mejor rendimiento.

# There are three `ConcreteFunction`s (one for each graph) in `my_relu`.
# The `ConcreteFunction` also knows the return type and shape!
print(my_relu.pretty_printed_concrete_signatures())
my_relu(x)
  Args:
    x: float32 Tensor, shape=()
  Returns:
    float32 Tensor, shape=()

my_relu(x=[1, -1])
  Returns:
    float32 Tensor, shape=(2,)

my_relu(x)
  Args:
    x: float32 Tensor, shape=(2,)
  Returns:
    float32 Tensor, shape=(2,)

usando tf.function

Hasta ahora, usted ha aprendido cómo convertir una función de Python en un gráfico, simplemente usando tf.function como decorador o envoltorio. Pero en la práctica, conseguir tf.function para que funcione correctamente puede ser difícil! En las siguientes secciones, aprenderá cómo puede hacer que su código funcione como se esperaba con tf.function .

Ejecución de gráficos frente a ejecución ávida

El código de una Function puede ser ejecutada tanto entusiasmo y en forma de gráfico. Por defecto, Function ejecuta su código como un gráfico:

@tf.function
def get_MSE(y_true, y_pred):
  sq_diff = tf.pow(y_true - y_pred, 2)
  return tf.reduce_mean(sq_diff)
y_true = tf.random.uniform([5], maxval=10, dtype=tf.int32)
y_pred = tf.random.uniform([5], maxval=10, dtype=tf.int32)
print(y_true)
print(y_pred)
tf.Tensor([6 4 3 1 5], shape=(5,), dtype=int32)
tf.Tensor([6 7 9 9 1], shape=(5,), dtype=int32)
get_MSE(y_true, y_pred)
<tf.Tensor: shape=(), dtype=int32, numpy=25>

Para verificar que su Function gráfica 's está haciendo el mismo cálculo que su función Python equivalente, se puede hacer ejecutar con impaciencia tf.config.run_functions_eagerly(True) . Este es un interruptor que apaga Function capacidad de 's para crear y ejecutar gráficos, en lugar de ejecutar el código normalmente.

tf.config.run_functions_eagerly(True)
get_MSE(y_true, y_pred)
<tf.Tensor: shape=(), dtype=int32, numpy=25>
# Don't forget to set it back when you are done.
tf.config.run_functions_eagerly(False)

Sin embargo, Function puede comportarse de forma diferente en el gráfico y ejecución ansiosos. El pitón print función es un ejemplo de cómo se diferencian estos dos modos. Cheque de dejar salir lo que sucede cuando se inserta una print declaración a su función y llamar repetidamente.

@tf.function
def get_MSE(y_true, y_pred):
  print("Calculating MSE!")
  sq_diff = tf.pow(y_true - y_pred, 2)
  return tf.reduce_mean(sq_diff)

Observa lo que está impreso:

error = get_MSE(y_true, y_pred)
error = get_MSE(y_true, y_pred)
error = get_MSE(y_true, y_pred)
Calculating MSE!

¿Es sorprendente la salida? get_MSE solamente impreso una vez que a pesar de que fue llamado tres veces.

Para explicarlo, la print la declaración se ejecuta cuando Function se ejecuta el código original con el fin de crear el gráfico en un proceso conocido como "rastreo" . Rastreo de capturas de las operaciones TensorFlow en un gráfico, y print no se captura en el gráfico. Ese gráfico se ejecuta a continuación, para las tres llamadas sin necesidad alguna de ejecutar el código de Python.

Como prueba de cordura, apaguemos la ejecución del gráfico para comparar:

# Now, globally set everything to run eagerly to force eager execution.
tf.config.run_functions_eagerly(True)
# Observe what is printed below.
error = get_MSE(y_true, y_pred)
error = get_MSE(y_true, y_pred)
error = get_MSE(y_true, y_pred)
Calculating MSE!
Calculating MSE!
Calculating MSE!
tf.config.run_functions_eagerly(False)

print es un efecto secundario de Python, y hay otras diferencias que se deben tener en cuenta al convertir una función en una Function .

Ejecución no estricta

La ejecución del gráfico solo ejecuta las operaciones necesarias para producir los efectos observables, que incluyen:

  • El valor de retorno de la función
  • Efectos secundarios bien conocidos documentados como:
    • Las operaciones de entrada / salida, como tf.print
    • Operaciones de depuración, tales como las funciones aserción en tf.debugging
    • Las mutaciones de tf.Variable

Este comportamiento generalmente se conoce como "ejecución no estricta" y difiere de la ejecución ansiosa, que recorre todas las operaciones del programa, necesarias o no.

En particular, la verificación de errores en tiempo de ejecución no cuenta como un efecto observable. Si se omite una operación porque no es necesaria, no puede generar ningún error de tiempo de ejecución.

En el siguiente ejemplo, la operación de "innecesaria" tf.gather se salta durante la ejecución del gráfico, por lo que el error de ejecución InvalidArgumentError no se eleva, ya que sería en ejecución ansioso. No confíe en que se genere un error al ejecutar un gráfico.

def unused_return_eager(x):
  # Get index 1 will fail when `len(x) == 1`
  tf.gather(x, [1]) # unused 
  return x

try:
  print(unused_return_eager(tf.constant([0.0])))
except tf.errors.InvalidArgumentError as e:
  # All operations are run during eager execution so an error is raised.
  print(f'{type(e).__name__}: {e}')
tf.Tensor([0.], shape=(1,), dtype=float32)
@tf.function
def unused_return_graph(x):
  tf.gather(x, [1]) # unused
  return x

# Only needed operations are run during graph exection. The error is not raised.
print(unused_return_graph(tf.constant([0.0])))
tf.Tensor([0.], shape=(1,), dtype=float32)

tf.function mejores prácticas

Puede tomar algún tiempo para acostumbrarse al comportamiento de Function . Para empezar a trabajar rápidamente, los usuarios de primera vez deben jugar un poco con la decoración de las funciones de juguete con @tf.function para obtener experiencia con pasar de ganas de ejecución de gráficos.

Diseñar para tf.function puede ser la mejor opción para escribir programas TensorFlow compatible con gráficos. A continuación se ofrecen algunos consejos:

  • Toggle entre la ejecución ansiosos y gráfico temprano y con frecuencia con tf.config.run_functions_eagerly de identificar si / cuando los dos modos divergen.
  • Crear tf.Variable s fuera de la función Python y modificarlos en el interior. Lo mismo ocurre con los objetos que utilizan tf.Variable , como keras.layers , keras.Model s y tf.optimizers .
  • Evite escribir funciones que dependen de las variables externas Python , con exclusión de tf.Variable s y objetos Keras.
  • Prefiere escribir funciones que tomen tensores y otros tipos de TensorFlow como entrada. Puede pasar en otros tipos de objetos, pero tenga cuidado !
  • Incluyendo la mayor cantidad de cálculo como sea posible bajo un tf.function para maximizar la ganancia de rendimiento. Por ejemplo, decore un paso de entrenamiento completo o todo el ciclo de entrenamiento.

Viendo la aceleración

tf.function suele mejorar el rendimiento de su código, pero la cantidad de aceleración depende del tipo de cálculo que se corre. Los cálculos pequeños pueden estar dominados por la sobrecarga de llamar a un gráfico. Puede medir la diferencia en el rendimiento de la siguiente manera:

x = tf.random.uniform(shape=[10, 10], minval=-1, maxval=2, dtype=tf.dtypes.int32)

def power(x, y):
  result = tf.eye(10, dtype=tf.dtypes.int32)
  for _ in range(y):
    result = tf.matmul(x, result)
  return result
print("Eager execution:", timeit.timeit(lambda: power(x, 100), number=1000))
Eager execution: 2.286959676999686
power_as_graph = tf.function(power)
print("Graph execution:", timeit.timeit(lambda: power_as_graph(x, 100), number=1000))
Graph execution: 0.6817171659999985

tf.function se utiliza comúnmente para acelerar bucles de formación, y se puede aprender más sobre esto en Grabación de un bucle de formación a partir de cero con Keras.

Rendimiento y compensaciones

Los gráficos pueden acelerar su código, pero el proceso de creación tiene algunos gastos generales. Para algunas funciones, la creación del gráfico lleva más tiempo que la ejecución del gráfico. Esta inversión generalmente se amortiza rápidamente con el aumento de rendimiento de las ejecuciones posteriores, pero es importante tener en cuenta que los primeros pasos de cualquier entrenamiento de modelo grande pueden ser más lentos debido al rastreo.

No importa cuán grande sea su modelo, desea evitar el rastreo con frecuencia. Los tf.function analizan guía de cómo especificaciones conjunto de entrada y los argumentos uso de tensores para evitar volver sobre. Si encuentra que está obteniendo un rendimiento inusualmente bajo, es una buena idea verificar si está retrocediendo accidentalmente.

Cuando es una Function localización?

Para averiguar cuando su Function está trazando, añadir una print comunicado a su código. Como regla general, Function se ejecutará la print estado de cuenta cada vez que traza.

@tf.function
def a_function_with_python_side_effect(x):
  print("Tracing!") # An eager-only side effect.
  return x * x + tf.constant(2)

# This is traced the first time.
print(a_function_with_python_side_effect(tf.constant(2)))
# The second time through, you won't see the side effect.
print(a_function_with_python_side_effect(tf.constant(3)))
Tracing!
tf.Tensor(6, shape=(), dtype=int32)
tf.Tensor(11, shape=(), dtype=int32)
# This retraces each time the Python argument changes,
# as a Python argument could be an epoch count or other
# hyperparameter.
print(a_function_with_python_side_effect(2))
print(a_function_with_python_side_effect(3))
Tracing!
tf.Tensor(6, shape=(), dtype=int32)
Tracing!
tf.Tensor(11, shape=(), dtype=int32)

Los nuevos argumentos de Python siempre desencadenan la creación de un nuevo gráfico, de ahí el seguimiento adicional.

Próximos pasos

Usted puede aprender más sobre tf.function en la página de referencia de la API y siguiendo el rendimiento mejor con tf.function guía.