Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
tensorflow
GitHub Repository: tensorflow/docs-l10n
Path: blob/master/site/es-419/guide/intro_to_graphs.ipynb
25115 views
Kernel: Python 3
#@title Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License.

Introduccion a los gráficos y la tf.function

Descripción general

En está guía se explica TensorFlow y Keras en profundidad para demostrar cómo funciona TensorFlow. Si quiere empezar con Keras inmediatamente, vea la colección de las guías de Keras.

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

Nota: Para los que tengan conocimientos solo de TensorFlow 1.x, en esta guía se muestra una perspectiva muy diferente de los gráficos.

Esto es un panorama general que cubre cómo tf.function permite cambiar de una ejecución eager a una ejecución de gráfico. Para ver todas las especificaciones de tf.function, vea la Guía para mejorar el rendimiento con tf.function.

¿Qué son los gráficos?

En las tres guías previas, puede ejecutar TensorFlow de forma eager. Esto quiere decir que las operaciones de TensorFlow se ejecutan con Python, operación por operación, y se devuelven los resultados en Python.

Si bien la ejecución eager tiene muchas ventajas únicas, la ejecución con gráficos permite portabilidad fuera de Python y suele ofrecer un mejor rendimiento. La ejecución de gráfico significa que los cálculos del tensor se ejecutan como gráfico de TensorFlow, a veces denominado tf.Graph o simplemente "gráfico."

Los gráficos son estructuras de datos que contienen un conjunto de objetos tf.Operation que representan unidades de cálculos; y objetos tf.Tensor que representan las unidades de datos que fluyen entre las operaciones. Se definen en un contexto de tf.Graph. Ya que estos gráficos son estructuras de datos, pueden guardarse, ejecutarse y realmacenarse sin el código original de Python.

Así se ve un gráfico de TensorFlow que representa una red neuronal de dos capas al visualizarse en TensorBoard:

Un gráfico simple de TensorFlow

Los beneficios de los gráficos

Con un gráfico, se tiene mucha flexibilidad. Puede usar su gráfico de TensorFlow en entornos que no tengan intérprete de Python, como en aplicaciones móviles, dispositivos incrustados y servidores backend. TensorFlow usa los gráficos como el formato de los modelos guardados cuando se exportan de Python.

Los gráficos también pueden optimizarse fácilmente, lo que permite hacer transformaciones como las siguientes:

  • Inferir estadísticamente el valor de los tensores al plegar los nodos de constantes en el cálculo ("plegado de constantes").

  • Separar subpartes de un cálculo que son independientes y dividirlas en subprocesos o dispositivos.

  • Simplificar las operaciones aritméticas mediante la eliminación de subexpresiones comunes.

Existe un sistema completo de optimización, Grappler, para hacer todo esto y otras aceleraciones.

En resumen, los gráficos son muy útiles y permiten que TensorFlow se ejecute rápido, se ejecute en paralelo y de forma eficiente en diferentes dispositivos.

Sin embargo, todavía es más convenienite definir los modelos de aprendizaje automátivo (o otros cálculos) en Python y después construir los gráficos automáticamente cuando los necesite.

Preparación

Importe algunas de las bibliotecas necesarias:

import tensorflow as tf import timeit from datetime import datetime

Aprovechar los gráficos

Creará y ejecutará un gráfico en TensorFlow con tf.function, ya sea como una llamada directa o como un decorador. tf.function toma una función regular como entrada y devuelve una Function. Una Function es un invocable de Python que construye gráficos de TensorFlow desde la función de Python. La Function se usa de la misma manera que se usa su equivalente de 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)

Desde afuera, una Function se parece a una función normal que se escribe con las operaciones de TensorFlow. Internamente, sin embargo, es muy diferente. Una Function encapsula varios tf.Graph detrás de una API (vea la sección de Polimorfismo para obtener más información). Es por eso que una Function le permite disfrutar de los beneficios de la ejecución de gráficos, como la velocidad e implementabilidad (consulte Los beneficios de los gráficos arriba).

tf.function se aplica a una función y a todas las otras 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()

Si ya uso TensorFlow 1.x, habrá notado que no hace falta definir un Placeholder o un tf.Session.

Convertir las funciones de Python en gráficos

Cualquier función que escriba con TensorFlow tendrá una mezcla de operaciones TF y lógicas de Python integradas, tales como cláusulas if-then, bucles, break, return, continue y más. Mientras las operaciones de TensorFlow son capturadas fácilmente con tf.Graph, las lógicas específicas de Python necesitan un paso adicional para ser parte del gráfico. La tf.function usa una biblioteca llamada AutoGraph (tf.autograph) para convertir el código de Python en código generador de gráficos.

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())

Si bien es poco probable que necesite ver los gráficos de forma directa, puede inspeccionar las salidas para verificar los resultados exactos. No son fáciles de leer, así que no es necesario mirar con tanta atención.

# This is the graph-generating output of AutoGraph. print(tf.autograph.to_code(simple_relu))
# This is the graph itself. print(tf_simple_relu.get_concrete_function(tf.constant(1)).graph.as_graph_def())

La mayoría de las veces, la tf.function funciona sin consideraciones especiales. Sin embargo, hay que tener algunas precauciones y la guía de tf.function puede ser útil, así como también la referencia completa de AutoGraph.

Polimorfismo: una Function, muchos gráficos

Un tf.Graph se especializa en un tipo específico de entradas (por ejemplo, tensores con un dtype específico u objetos con el mismo id()).

Cada vez que se invoca una Function con un conjunto de argumentos que pueden manipularse con cualquiera de sus gráficos existentes (como argumentos con un dtypes nuevo o formas incompatibles), la Function crea un nuevo tf.Graph especializado en los nuevos argumentos. La especificación del tipo de las entradas de un tf.Graph se conoce como su signatura de entrada o solo signatura. Para obtener más información sobre cuando se genera un tf.Graph nuevo como controlarlo, vea la sección Reglas de trazado de la guía Mejor rendimiento con tf.function.

La Function almacena el tf.Graph que corresponde a esa signatura en un ConcreteFunction. Un ConcreteFunction es un encapsulador del 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.])))

Si la Function ya fue llamada con esa signatura, la Function no crea un tf.Graph nuevo.

# 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.])`.

Debido a que tiene varios gráficos de respaldo, una Function es polimórfica. Esto permite que se admitan más tipos de entrada que las que un simple tf.Graph puede representar y que se optimice 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())

Usar tf.function

Hasta este punto ha aprendido a convertir la función de Python en un gráfico de forma simple usando tf.function como un decorador o encapsulador. Pero en la práctica, hacer que tf.function funcione de forma correcta puede ser complicado. En la siguiente sección, aprenderá cómo hacer que su código funcione con tf.function como debe ser.

Ejecución de gráfico versus ejecución eager

Se puede ejecutar el código en una Function de forma eager y como un gráfico. De forma predeterminada, la 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)
get_MSE(y_true, y_pred)

Para verificar que el gráfico de Function realice el mismo cálculo que su función equivalente de Python, puede ejecutarlo en eager con tf.config.run_functions_eagerly(True). Este es un cambio que desactiva la capacidad de Function para crear y ejecutar gráficos, en vez de ejecutar el código normalmente.

tf.config.run_functions_eagerly(True)
get_MSE(y_true, y_pred)
# Don't forget to set it back when you are done. tf.config.run_functions_eagerly(False)

Por el contrario, Function puede comportarse diferente en una ejecución de gráfico o eager. La función print de Python es un ejemplo de cómo difieren los dos modos. Veamos qué pasa cuando se inserta la declaración print en su función y la llamamos 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)

Observe lo que se imprime:

error = get_MSE(y_true, y_pred) error = get_MSE(y_true, y_pred) error = get_MSE(y_true, y_pred)

¿Le sorprende la salida? get_MSE solo se imprimió una vez a pesar de haberlo llamado tres veces.

Esto es así, la declaración print se ejecuta cuando Function ejecuta el código original para crear un gráfico en un proceso conocido como "trazado" (consulte la sección de Trazado de la guía de tf.function. El trazado captura las operaciones de TensorFlow en un gráfico y print no se incluye en el gráfico. Este gráfico luego se ejecuta para las tres llamadas sin tener que ejecutar el código de Python de nuevo.

Para comprobarlo, desactivaremos la ejecución de 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)
tf.config.run_functions_eagerly(False)

print es un efecto secundario de Python, y hay otras diferencias que debería tener en cuenta al convertir una función en Function. Puede obtener más información en la sección de Limitaciones de la guía Mejor rendimiento con tf.function.

Nota: si quiere imprimir valores en ambas ejecuciones, eager y de gráfico, mejor use tf.print.

Ejecución no estricta

La ejecución de gráfico solo ejecuta las operaciones necesarias para producir los efectos observables, entre ellas se encuentra:

  • El valor de retorno de la función

  • Los efectos secundarios más conocidos documentados como:

    • Las operaciones de entrada o de salida, como tf.print

    • Operaciones de depuración, como las funciones de aserción en tf.debugging

    • Mutaciones de tf.Variable

Este comportamiento se conoce normalemente como "ejecución no estricta" y es diferente a la ejecución eager, que recorre paso a paso todas las operaciones del programa, sean necesarias o no.

Particularmente, la verificación del error de tiempo de ejecución no cuenta como efecto obervable. Si se omite una operación porque no es necesaria, no se provoca ningún error en el tiempo de ejecución.

En el siguiente ejemplo, se omite la operación "innecesaria"tf.gather durante la ejecución de gráfico, por eso no se provoca un error de ejecución InvalidArgumentError ya que sería en una ejecución eager. No espere que ocurra 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.function def unused_return_graph(x): tf.gather(x, [1]) # unused return x # Only needed operations are run during graph execution. The error is not raised. print(unused_return_graph(tf.constant([0.0])))

Mejores prácticas de tf.function

Es posible que lleve un tiempo acostumbrarse al comportamiento de Function. Para empezar rápidamente, los usuarios que lo usan por primera vez, deberían probar decorando funciones de juguete con @tf.function para obtener experiencia al cambiar de la ejecución eager a la de gráfico.

Diseñar para tf.function puede ser lo mejor para escribir programas de TensorFlow que sean compatibles con gráficos. Estas son algunas recomendaciones:

  • Alterne entre la ejecución eager y de gráfico pronto y con frecuencia con tf.config.run_functions_eagerly para identificar cuando difieren, si difieren, los dos modos.

  • Cree unas tf.Variable fuera de la función de Python y modifíquelas internamente. Haga lo mismo con los objetos que usan tf.Variable, como los tf.keras.layers, tf.keras.Model y tf.keras.optimizers.

  • Evite escribir funciones que dependan de las variables de Python, excluya la tf.Variable y los objetos de Keras. Obtenga más información en la sección *Depender de las variables globales y gratis de Python * de la guía de tf.function.

  • Opte por escribir funciones que tomen tensores y otros tipos de TensorFlow como entrada. También puede pasar otros tipos de objetos pero tenga cuidado. Para obtener más información, vea Depender de objetos de Python de la guía de tf.function.

  • Incluya todos los cálculos que pueda en tf.function para maximizar la ganancia de rendimiento. Por ejemplo, decore un paso de entrenamiento completo o todo un bucle de entrenamiento.

Observar la aceleración

tf.function suele mejorar el rendimiento del código, pero la cantidad de aceleración depende del tipo de cálculo que se ejecute. Los calculos pequeños pueden se dominados por la sobrecarga de llamar 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), "seconds")
power_as_graph = tf.function(power) print("Graph execution:", timeit.timeit(lambda: power_as_graph(x, 100), number=1000), "seconds")

Generalmente, tf.function se usa para acelerar los bucles de entrenamiento, y puede obtener más información sobre esto en la sección Acelerar el entrenamiento con tf.function de la guía Escribir un bucle de entrenamiento desde cero con Keras.

Nota: También puede probar tf.function(jit_compile=True) para potenciar el rendimiento significativamente, especialmente si su código es pesado en el flujo de control de TensorFlow y usa muchos tensores pequeños. Obtenga más información en la sección Compilación explicita con tf.function(jit_compile=True) del Resumen de XLA.

Rendimiento y compensaciones

Los gráficos pueden acelerar su código, pero el proceso de creación puede tener algo de sobrecarga. Para algunas funciones, la creación de gráficos lleva más tiempo que la ejecución. Esta inversión suele recuperarse con el potenciador de rendimiento de las ejecuciones posteriores, pero es importante saber que los primeros pasos de cualquier entrenamiento de modelo grande puede ser lento debido al trazado.

No importa el tamaño del modelo, es mejor evitar el trazado frecuente. En la guía de tf.function se explica cómo establecer especificaciones de entrada y cómo usar argumentos de tensor para evitar el retrazado en la sección Controlar el retrazado. Si nota que los rendimientos son extrañamente malos, sería bueno verificar que no esté retrazando por accidente.

¿Cuándo traza Function?

Para averiguar si Function está trazando, agregue una declaración print en su código. Como regla de oro, Function ejecutará la declaración print cada vez que realice un trazado.

@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)))
# 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))

Los argumentos nuevos de Python siempre provocan la creación de un gráfico nuevo, y por eso el trazado adicional.

Siguientes pasos

Puede obtener más información sobre tf.function en la página de referencia de la API si visita la guía de Mejor rendimiento con tf.function.