Optimización de gráficos de TensorFlow con Grappler

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

Descripción general

TensorFlow usa ejecuciones gráficas y ávidas para ejecutar cálculos. Un tf.Graph contiene un conjunto de objetos tf.Operation (ops) que representan unidades de cálculo y objetos tf.Tensor que representan las unidades de datos que fluyen entre ops.

Grappler es el sistema de optimización de gráficos predeterminado en el tiempo de ejecución de TensorFlow. Grappler aplica optimizaciones en modo gráfico (dentro de tf.function ) para mejorar el rendimiento de tus cálculos de TensorFlow a través de simplificaciones de gráficos y otras optimizaciones de alto nivel, como cuerpos de funciones en línea para habilitar optimizaciones entre procedimientos. La optimización de tf.Graph también reduce el uso máximo de memoria del dispositivo y mejora la utilización del hardware al optimizar el mapeo de los nodos del gráfico para calcular los recursos.

Utilice tf.config.optimizer.set_experimental_options() para un control más tf.Graph sobre sus optimizaciones tf.Graph .

Optimizadores de gráficos disponibles

Grappler realiza optimizaciones de gráficos a través de un controlador de nivel superior llamado MetaOptimizer . Los siguientes optimizadores de gráficos están disponibles con TensorFlow:

  • Optimizador de plegado constante: infiere estáticamente el valor de los tensores cuando es posible plegando nodos constantes en el gráfico y materializa el resultado mediante constantes.
  • Optimizador aritmético: simplifica las operaciones aritméticas eliminando subexpresiones comunes y simplificando las declaraciones aritméticas.
  • Optimizador de diseño: optimiza los diseños de tensor para ejecutar operaciones dependientes del formato de datos, como las convoluciones, de manera más eficiente.
  • Optimizador de reasignación: reasigna subgráficos en implementaciones más eficientes al reemplazar los subgráficos que ocurren comúnmente con núcleos monolíticos fusionados optimizados.
  • Optimizador de memoria: analiza el gráfico para inspeccionar el uso máximo de memoria para cada operación e inserta operaciones de copia de memoria CPU-GPU para intercambiar memoria GPU a CPU para reducir el uso máximo de memoria.
  • Optimizador de dependencias: elimina o reorganiza las dependencias de control para acortar la ruta crítica de un paso del modelo o habilita otras optimizaciones. También elimina los nodos que son efectivamente no operativos, como Identity.
  • Optimizador de poda: elimina los nodos que no tienen ningún efecto en la salida del gráfico. Por lo general, se ejecuta primero para reducir el tamaño del gráfico y acelerar el procesamiento en otras pasadas de Grappler.
  • Optimizador de funciones: optimiza la biblioteca de funciones de un programa de TensorFlow y los cuerpos de las funciones en línea para habilitar otras optimizaciones entre procedimientos.
  • Optimizador de formas: optimiza los subgrafos que operan con la forma y la información relacionada con la forma.
  • Optimizador de paralelo automático: paraleliza automáticamente los gráficos dividiéndolos a lo largo de la dimensión del lote. Este optimizador está desactivado de forma predeterminada.
  • Optimizador de bucle: optimiza el flujo de control del gráfico al sacar de los bucles subgrafos invariantes de bucle y eliminar las operaciones de pila redundantes en los bucles. También optimiza los bucles con recuentos de viajes conocidos estáticamente y elimina las ramas muertas conocidas estáticamente en los condicionales.
  • Optimizador de asignadores con ámbito : introduce asignadores con ámbito para reducir el movimiento de datos y consolidar algunas operaciones.
  • Pin to host optimizer: intercambia pequeñas operaciones en la CPU. Este optimizador está desactivado de forma predeterminada.
  • Optimizador de precisión mixto automático: convierte los tipos de datos a float16 cuando corresponda para mejorar el rendimiento. Actualmente se aplica solo a las GPU.
  • Eliminador de depuración: elimina los nodos relacionados con las operaciones de depuración como tf.debugging.Assert , tf.debugging.check_numerics y tf.print del gráfico. Este optimizador está desactivado de forma predeterminada.

Configuración

import numpy as np
import timeit
import traceback
import contextlib


import tensorflow as tf

Cree un administrador de contexto para alternar fácilmente los estados del optimizador.

@contextlib.contextmanager
def options(options):
  old_opts = tf.config.optimizer.get_experimental_options()
  tf.config.optimizer.set_experimental_options(options)
  try:
    yield
  finally:
    tf.config.optimizer.set_experimental_options(old_opts)

Compare el rendimiento de ejecución con y sin Grappler

TensorFlow 2 y posteriores se ejecutan con entusiasmo de forma predeterminada. Utilice tf.function para cambiar la ejecución predeterminada al modo Gráfico. Grappler se ejecuta automáticamente en segundo plano para aplicar las optimizaciones de gráficos anteriores y mejorar el rendimiento de ejecución.

Optimizador de plegado constante

Como ejemplo preliminar, considere una función que realiza operaciones sobre constantes y devuelve una salida.

def test_function_1():
  @tf.function
  def simple_function(input_arg):
    print('Tracing!')
    a = tf.constant(np.random.randn(2000,2000), dtype = tf.float32)
    c = a
    for n in range(50):
      c = c@a
    return tf.reduce_mean(c+input_arg)

  return simple_function

Apague el optimizador de plegado constante y ejecute la función:

with options({'constant_folding': False}):
  print(tf.config.optimizer.get_experimental_options())
  simple_function = test_function_1()
  # Trace once
  x = tf.constant(2.2)
  simple_function(x)
  print("Vanilla execution:", timeit.timeit(lambda: simple_function(x), number = 1), "s")
{'constant_folding': False, 'disable_model_pruning': False, 'disable_meta_optimizer': False}
Tracing!
Vanilla execution: 0.0801323579999007 s

Habilite el optimizador de plegado constante y ejecute la función nuevamente para observar una aceleración en la ejecución de la función.

with options({'constant_folding': True}):
  print(tf.config.optimizer.get_experimental_options())
  simple_function = test_function_1()
  # Trace once
  x = tf.constant(2.2)
  simple_function(x)
  print("Constant folded execution:", timeit.timeit(lambda: simple_function(x), number = 1), "s")
{'constant_folding': True, 'disable_model_pruning': False, 'disable_meta_optimizer': False}
Tracing!
Constant folded execution: 0.0006300529998952697 s

Optimizador de depuración de stripper

Considere una función simple que verifica el valor numérico de su argumento de entrada y lo devuelve.

def test_function_2():
  @tf.function
  def simple_func(input_arg):
    output = input_arg
    tf.debugging.check_numerics(output, "Bad!")
    return output
  return simple_func

Primero, ejecute la función con el optimizador depurador de eliminación de errores desactivado.

test_func = test_function_2()
p1 = tf.constant(float('inf'))
try:
  test_func(p1)
except tf.errors.InvalidArgumentError as e:
  traceback.print_exc(limit=2)
Traceback (most recent call last):
  File "<ipython-input-8-1ac473fdfbab>", line 4, in <module>
    test_func(p1)
  File "/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/eager/def_function.py", line 780, in __call__
    result = self._call(*args, **kwds)
tensorflow.python.framework.errors_impl.InvalidArgumentError: 2 root error(s) found.
  (0) Invalid argument:  Bad! : Tensor had Inf values
     [[node CheckNumerics (defined at <ipython-input-7-cbee1561c83e>:5) ]]
  (1) Invalid argument:  Bad! : Tensor had Inf values
     [[node CheckNumerics (defined at <ipython-input-7-cbee1561c83e>:5) ]]
     [[Identity/_4]]
0 successful operations.
0 derived errors ignored. [Op:__inference_simple_func_131]

Errors may have originated from an input operation.
Input Source operations connected to node CheckNumerics:
 input_arg (defined at <ipython-input-8-1ac473fdfbab>:4)

Input Source operations connected to node CheckNumerics:
 input_arg (defined at <ipython-input-8-1ac473fdfbab>:4)

Function call stack:
simple_func -> simple_func

tf.debugging.check_numerics genera un error de argumento no válido debido al argumento Inf para test_func .

Habilite el optimizador del eliminador de errores y ejecute la función nuevamente.

with options({'debug_stripper': True}):
  test_func2 = test_function_2()
  p1 = tf.constant(float('inf'))
  try:
    test_func2(p1)
  except tf.errors.InvalidArgumentError as e:
    traceback.print_exc(limit=2)

El optimizador tf.debug.check_numerics nodo tf.debug.check_numerics del gráfico y ejecuta la función sin generar ningún error.

Resumen

El tiempo de ejecución de TensorFlow usa Grappler para optimizar los gráficos automáticamente antes de la ejecución. Utilice tf.config.optimizer.set_experimental_options para habilitar o deshabilitar los distintos optimizadores de gráficos.

Para obtener más información sobre Grappler, consulte Optimizaciones de gráficos de TensorFlow .