¿Por qué es TensorFlow 2 mucho más lento que TensorFlow 1?

137

Muchos usuarios lo han citado como la razón para cambiar a Pytorch, pero aún no he encontrado una justificación / explicación para sacrificar la calidad práctica más importante, la velocidad, para una ejecución ansiosa.

A continuación se muestra el rendimiento de la evaluación comparativa de código, TF1 frente a TF2, con TF1 ejecutándose en cualquier lugar del 47% al 276% más rápido .

Mi pregunta es: ¿qué es, a nivel gráfico o de hardware, lo que produce una desaceleración tan significativa?


Buscando una respuesta detallada, ya estoy familiarizado con los conceptos generales. Git relevante

Especificaciones : CUDA 10.0.130, cuDNN 7.4.2, Python 3.7.4, Windows 10, GTX 1070


Resultados de referencia :


ACTUALIZACIÓN : Deshabilitar la Ejecución ansiosa por el siguiente código no ayuda. Sin embargo, el comportamiento es inconsistente: a veces, ejecutar en modo gráfico ayuda considerablemente, otras veces se ejecuta más lentamente en relación con Eager.

Como los desarrolladores de TF no aparecen en ningún lado, investigaré este asunto yo mismo, puedo seguir el progreso en el problema de Github vinculado.

ACTUALIZACIÓN 2 : toneladas de resultados experimentales para compartir, junto con explicaciones; debe hacerse hoy.


Código de referencia :

# use tensorflow.keras... to benchmark tf.keras; used GPU for all above benchmarks
from keras.layers import Input, Dense, LSTM, Bidirectional, Conv1D
from keras.layers import Flatten, Dropout
from keras.models import Model
from keras.optimizers import Adam
import keras.backend as K
import numpy as np
from time import time

batch_shape = (32, 400, 16)
X, y = make_data(batch_shape)

model_small = make_small_model(batch_shape)
model_small.train_on_batch(X, y)  # skip first iteration which builds graph
timeit(model_small.train_on_batch, 200, X, y)

K.clear_session()  # in my testing, kernel was restarted instead

model_medium = make_medium_model(batch_shape)
model_medium.train_on_batch(X, y)  # skip first iteration which builds graph
timeit(model_medium.train_on_batch, 10, X, y)

Funciones utilizadas :

def timeit(func, iterations, *args):
    t0 = time()
    for _ in range(iterations):
        func(*args)
    print("Time/iter: %.4f sec" % ((time() - t0) / iterations))

def make_small_model(batch_shape):
    ipt   = Input(batch_shape=batch_shape)
    x     = Conv1D(128, 400, strides=4, padding='same')(ipt)
    x     = Flatten()(x)
    x     = Dropout(0.5)(x)
    x     = Dense(64, activation='relu')(x)
    out   = Dense(1,  activation='sigmoid')(x)
    model = Model(ipt, out)
    model.compile(Adam(lr=1e-4), 'binary_crossentropy')
    return model

def make_medium_model(batch_shape):
    ipt   = Input(batch_shape=batch_shape)
    x     = Bidirectional(LSTM(512, activation='relu', return_sequences=True))(ipt)
    x     = LSTM(512, activation='relu', return_sequences=True)(x)
    x     = Conv1D(128, 400, strides=4, padding='same')(x)
    x     = Flatten()(x)
    x     = Dense(256, activation='relu')(x)
    x     = Dropout(0.5)(x)
    x     = Dense(128, activation='relu')(x)
    x     = Dense(64,  activation='relu')(x)
    out   = Dense(1,   activation='sigmoid')(x)
    model = Model(ipt, out)
    model.compile(Adam(lr=1e-4), 'binary_crossentropy')
    return model

def make_data(batch_shape):
    return np.random.randn(*batch_shape), np.random.randint(0, 2, (batch_shape[0], 1))
OverLordGoldDragon
fuente
¿Alguna vez ha utilizado este tipo de herramienta para analizar qué parte las hace tan diferentes?
zihaozhihao
@zihaozhihao que tengo , aunque no para esto específicamente; según el enlace anterior y escribiendo un optimizador personalizado, ya estoy familiarizado con las diferencias en las llamadas, pero no entiendo por qué uno es más lento que el otro, ni ningún experto que no sea TF puede entenderlo desde la fuente, lo que, además de ser un desastre enredado, no documenta actuaciones relativas. Se requiere información gráfica / nivel de hardware, que los perfiladores no proporcionarán (en la medida en que pueda usarlos)
OverLordGoldDragon
¿La versión numpy es la misma en ambas pruebas?
chabir
Ay ... si el viejo Keras solo fuera significativamente más lento que PyTorch, imagínelo ahora.
Daniel Möller
¿El problema escala con el tamaño del modelo? ¿también ha intentado ejecutar el mismo punto de referencia en otro sistema operativo?
okawo

Respuestas:

76

ACTUALIZACIÓN 18/02/2020 : he puesto en la banca 2.1 y 2.1 por noche; Los resultados son mixtos. Todas las configuraciones menos una (modelo y tamaño de datos) son tan rápidas o mucho más rápidas que las mejores TF2 y TF1. El que es más lento, y más lento dramáticamente, es Large-Large - esp. en la ejecución del gráfico ( 1.6x a 2.5x más lento ).

Además, existen diferencias extremas de reproducibilidad entre Graph y Eager para un modelo grande que probé, uno no explicable a través de aleatoriedad / paralelismo de cómputo. Actualmente no puedo presentar código reproducible para estas notificaciones por restricciones de tiempo, por lo que recomiendo probar esto para sus propios modelos.

Todavía no he abierto un problema de Git sobre estos, pero hice un comentario sobre el original , aún no hay respuesta. Actualizaré las respuestas una vez que se haya avanzado.


VEREDICTO : no lo es , SI sabes lo que estás haciendo. Pero si no lo hace , podría costarle mucho, por un par de actualizaciones de GPU en promedio, y por múltiples GPU en el peor de los casos.


ESTA RESPUESTA : tiene como objetivo proporcionar una descripción de alto nivel del problema, así como pautas sobre cómo decidir sobre la configuración de capacitación específica para sus necesidades. Para obtener una descripción detallada de bajo nivel, que incluye todos los resultados de evaluación comparativa + código utilizado, consulte mi otra respuesta.

Actualizaré mi (s) respuesta (s) con más información si descubro que puede marcar / "marcar" esta pregunta como referencia.


RESUMEN DEL PROBLEMA : según lo confirmado por un desarrollador de TensorFlow, Q. Scott Zhu, TF2 centró el desarrollo en la ejecución Eager y la estrecha integración con Keras, lo que implicó cambios radicales en la fuente de TF, incluso a nivel de gráfico. Beneficios: capacidades ampliadas de procesamiento, distribución, depuración e implementación. Sin embargo, el costo de algunos de estos es la velocidad.

El asunto, sin embargo, es bastante más complejo. No se trata solo de TF1 frente a TF2: los factores que producen diferencias significativas en la velocidad del tren incluyen:

  1. TF2 vs. TF1
  2. Modo ansioso contra gráfico
  3. keras vs. tf.keras
  4. numpyvs. tf.data.Datasetvs.
  5. train_on_batch() vs. fit()
  6. GPU vs. CPU
  7. model(x)vs. model.predict(x)vs.

Desafortunadamente, casi ninguno de los anteriores es independiente del otro, y cada uno puede al menos duplicar el tiempo de ejecución en relación con el otro. Afortunadamente, puede determinar qué funcionará mejor de manera sistemática y con algunos atajos, como mostraré.


¿QUÉ TENGO QUE HACER? Actualmente, la única forma es: experimente con su modelo, datos y hardware específicos. Sin configuración única siempre funcionará mejor - pero no se qué hacer y qué no lo es para simplificar su búsqueda:

>> HACER:

  • train_on_batch()+ numpy+ tf.keras+ TF1 + Eager / Graph
  • train_on_batch()+ numpy+ tf.keras+ TF2 + Gráfico
  • fit()+ numpy+ tf.keras+ TF1 / TF2 + Gráfico + modelo grande y datos

>> NO:

  • fit()+ numpy+ keraspara modelos y datos pequeños y medianos
  • fit()+ numpy+ tf.keras+ TF1 / TF2 + Ansioso
  • train_on_batch()+ numpy+ keras+ TF1 + Ansioso

  • [Mayor] tf.python.keras ; puede correr 10-100x más lento y con muchos errores; más información

    • Esto incluye layers, models, optimizers, y relaciona "fuera de la caja" importaciones de uso; las operaciones, las utilidades y las importaciones 'privadas' relacionadas están bien, pero para estar seguro, verifique si hay alternativas y si se usan entf.keras

Consulte el código en la parte inferior de mi otra respuesta para un ejemplo de configuración de evaluación comparativa. La lista anterior se basa principalmente en las tablas "BENCHMARKS" en la otra respuesta.


LIMITACIONES de lo que HACER y NO HACER:

  • Esta pregunta se titula "¿Por qué TF2 es mucho más lento que TF1?", Y aunque su cuerpo se refiere al entrenamiento explícitamente, el asunto no se limita a eso; la inferencia también está sujeta a grandes diferencias de velocidad, incluso dentro de la misma versión TF, importación, formato de datos, etc. - vea esta respuesta .
  • Es probable que los RNN cambien notablemente la cuadrícula de datos en la otra respuesta, ya que se han mejorado en TF2
  • Modelos utilizados principalmente Conv1Dy Densesin RNN, datos / objetivos dispersos, entradas 4 / 5D y otras configuraciones
  • Datos de entrada limitados a numpyy tf.data.Dataset, aunque existen muchos otros formatos; ver otra respuesta
  • Se usó GPU; resultados serán diferir en una CPU. De hecho, cuando hice la pregunta, mi CUDA no estaba configurado correctamente y algunos de los resultados estaban basados ​​en la CPU.

¿Por qué TF2 sacrificó la calidad más práctica, la velocidad, para una ejecución ansiosa? Claramente no lo ha hecho: el gráfico todavía está disponible. Pero si la pregunta es "¿por qué ansioso?":

  • Depuración superior : es probable que haya encontrado una multitud de preguntas que preguntan "¿cómo obtengo resultados de capa intermedia" o "cómo inspecciono los pesos"? con ganas, es (casi) tan simple como .__dict__. Graph, por el contrario, requiere familiaridad con funciones especiales de back-end, lo que complica enormemente todo el proceso de depuración e introspección.
  • Creación de prototipos más rápida : por ideas similares a las anteriores; comprensión más rápida = más tiempo restante para la DL real.

¿CÓMO HABILITAR / DESACTIVAR EAGER?

tf.enable_eager_execution()  # TF1; must be done before any model/tensor creation
tf.compat.v1.disable_eager_execution() # TF2; above holds

INFORMACIÓN ADICIONAL :

  • Cuidado con los _on_batch()métodos en TF2; de acuerdo con el desarrollador de TF, todavía usan una implementación más lenta, pero no intencionalmente , es decir, se debe solucionar. Ver otra respuesta para más detalles.

SOLICITUDES DE DISPOSITIVOS DE FLUJO TENSOR :

  1. Por favor train_on_batch(), corrija , y el aspecto de rendimiento de llamar de forma fit()iterativa; los bucles de trenes personalizados son importantes para muchos, especialmente para mí.
  2. Agregue documentación / mención de documentación de estas diferencias de rendimiento para el conocimiento de los usuarios.
  3. Mejore la velocidad de ejecución general para evitar que los píos salten a Pytorch.

AGRADECIMIENTOS : Gracias a


ACTUALIZACIONES :

  • 14/11/19 : encontré un modelo (en mi aplicación real) que se ejecuta más lentamente en TF2 para todas las configuraciones * con datos de entrada Numpy. Las diferencias oscilaron entre el 13 y el 19%, con un promedio del 17%. Las diferencias entre kerasy tf.keras, sin embargo, fueron más dramáticas: 18-40% , promedio. 32% (ambos TF1 y 2). (* - excepto Eager, para el cual TF2 OOM'd)

  • 17/11/19 : los desarrolladores actualizaron los on_batch()métodos en una confirmación reciente , indicando que han mejorado la velocidad, que se lanzará en TF 2.1 o estará disponible ahora como tf-nightly. Como no puedo ejecutarlo más tarde, retrasaré el banco hasta 2.1.

  • 20/02/20 - el rendimiento de predicción también vale la pena. en TF2, por ejemplo, los tiempos de predicción de la CPU pueden implicar picos periódicos
OverLordGoldDragon
fuente
3
¿Qué hay de fit_generator? ... Prácticamente nunca quiero train_on_batchy administrar mi propio ciclo de entrenamiento en lotes es un gran antipatrón enorme que se debe evitar incluso a un gran costo.
ely
@ely Queda por probar, como se señaló en mi otra respuesta, pero en todo caso predigo que será fitcon una pequeña sobrecarga de procesamiento de datos adicional. En cuanto a los bucles de trenes, escribí mi propio personalizado que finalmente se convirtió en una especie de API; fit_generatorcarece de introspección, personalización y guardar / cargar, por lo que es un nop absoluto para mí Eventualmente publicaré mi ciclo de entrenamiento, en Github.
OverLordGoldDragon
La falta de introspección y personalización es una característica para mí, no un error. IDK, ¿a qué se refiere el comentario guardar / cargar? ¿Guardado / carga intermedio durante un ciclo no controlado por el generador de datos? (También estoy personalmente feliz de depender solo de las devoluciones de llamada para eso, y vería la necesidad de una mayor personalización como un olor a código de que mi ciclo de entrenamiento está diseñado incorrectamente).
ely
@ely No es simple, pero es necesario para la capacitación con canales de datos de entrada complejos, funciones objetivas y configuraciones de modelos que no son API (por ejemplo, conjuntos). La introspección es imprescindible para muchos propósitos de depuración e ingeniería de características. Al carecer de una carga / guardado externo, y entrenar la capacidad de repetición y la reanudación del bucle para modelos computacionalmente caros, una pesadilla. De todos modos, en última instancia depende de sus necesidades específicas y de salir del tema; la forma más segura de probar el rendimiento con fit_generatorsu aplicación es, bueno, probarlo.
OverLordGoldDragon
47

ESTA RESPUESTA : tiene como objetivo proporcionar una descripción detallada del problema a nivel gráfico / hardware, incluidos los bucles de tren TF2 vs. TF1, los procesadores de datos de entrada y las ejecuciones en modo Eager vs. Graph. Para un resumen del problema y pautas de resolución, vea mi otra respuesta.


VEREDICTO DE DESEMPEÑO : a veces uno es más rápido, a veces el otro, dependiendo de la configuración. En cuanto a TF2 vs TF1, están a la par en promedio, pero existen diferencias significativas basadas en la configuración, y TF1 supera a TF2 con más frecuencia que viceversa. Ver "BENCHMARKING" a continuación.


EAGER VS. GRÁFICO : la carne de esta respuesta completa para algunos: el deseo de TF2 es más lento que el de TF1, según mis pruebas. Detalles más abajo.

La diferencia fundamental entre los dos es: Graph configura una red computacional de manera proactiva , y se ejecuta cuando se le 'dice', mientras que Eager ejecuta todo al momento de la creación. Pero la historia solo comienza aquí:

  • Eager no está desprovisto de Graph , y de hecho puede ser principalmente Graph, al contrario de lo esperado. Lo que es en gran parte es el gráfico ejecutado : esto incluye pesos de modelo y optimizador, que comprenden una gran parte del gráfico.

  • Eager reconstruye parte del propio gráfico en la ejecución ; consecuencia directa de que Graph no se construyó completamente - ver resultados del generador de perfiles. Esto tiene una sobrecarga computacional.

  • Ansioso es más lento con entradas Numpy ; según este comentario y código de Git , las entradas de Numpy en Eager incluyen el costo general de copiar tensores de la CPU a la GPU. Al recorrer el código fuente, las diferencias en el manejo de datos son claras; Eager pasa directamente a Numpy, mientras que Graph pasa a los tensores que luego evalúan a Numpy; incierto del proceso exacto, pero este último debe incluir optimizaciones a nivel de GPU

  • TF2 Eager es más lento que TF1 Eager : esto es ... inesperado. Vea los resultados de la evaluación comparativa a continuación. Las diferencias abarcan desde insignificante a significativo, pero son consistentes. No estoy seguro de por qué es así: si un desarrollador de TF aclara, actualizará la respuesta.


TF2 vs. TF1 : citando porciones relevantes de la respuesta de un desarrollador de TF, Q. Scott Zhu, con un poco de mi énfasis y reformulación:

En impaciente, el tiempo de ejecución necesita ejecutar las operaciones y devolver el valor numérico para cada línea de código de Python. La naturaleza de la ejecución de un solo paso hace que sea lenta .

En TF2, Keras aprovecha la función tf para construir su gráfico para entrenamiento, evaluación y predicción. Los llamamos "función de ejecución" para el modelo. En TF1, la "función de ejecución" era un FuncGraph, que compartía algún componente común como función TF, pero tenía una implementación diferente.

Durante el proceso, de alguna manera dejamos una implementación incorrecta para train_on_batch (), test_on_batch () y predict_on_batch () . Todavía son numéricamente correctos , pero la función de ejecución para x_on_batch es una función de Python pura, en lugar de una función de Python envuelta en tf.function. Esto causará lentitud

En TF2, convertimos todos los datos de entrada en un tf.data.Dataset, mediante el cual podemos unificar nuestra función de ejecución para manejar el tipo único de las entradas. Puede haber una sobrecarga en la conversión del conjunto de datos , y creo que esta es una sobrecarga única, en lugar de un costo por lote

Con la última oración del último párrafo anterior y la última cláusula del párrafo siguiente:

Para superar la lentitud en el modo ansioso, tenemos @ tf.function, que convertirá una función de Python en un gráfico. Cuando se introduce un valor numérico como np array, el cuerpo de la función tf se convierte en un gráfico estático, se optimiza y devuelve el valor final, que es rápido y debe tener un rendimiento similar al modo de gráfico TF1.

No estoy de acuerdo, según mis resultados de perfil, que muestran que el procesamiento de datos de entrada de Eager es sustancialmente más lento que el de Graph. Además, no estoy seguro acerca de esto tf.data.Dataseten particular, pero Eager llama repetidamente a varios de los mismos métodos de conversión de datos; consulte el generador de perfiles.

Por último, el compromiso vinculado del desarrollador: número significativo de cambios para admitir los bucles Keras v2 .


Train Loops : dependiendo de (1) Eager vs. Graph; (2) formato de datos de entrada, el entrenamiento continuará con un ciclo de entrenamiento distinto: en TF2 _select_training_loop(), training.py , uno de:

training_v2.Loop()
training_distributed.DistributionMultiWorkerTrainingLoop(
              training_v2.Loop()) # multi-worker mode
# Case 1: distribution strategy
training_distributed.DistributionMultiWorkerTrainingLoop(
            training_distributed.DistributionSingleWorkerTrainingLoop())
# Case 2: generator-like. Input is Python generator, or Sequence object,
# or a non-distributed Dataset or iterator in eager execution.
training_generator.GeneratorOrSequenceTrainingLoop()
training_generator.EagerDatasetOrIteratorTrainingLoop()
# Case 3: Symbolic tensors or Numpy array-like. This includes Datasets and iterators 
# in graph mode (since they generate symbolic tensors).
training_generator.GeneratorLikeTrainingLoop() # Eager
training_arrays.ArrayLikeTrainingLoop() # Graph

Cada uno maneja la asignación de recursos de manera diferente y tiene consecuencias en el rendimiento y la capacidad.


Train Loops: fitvs train_on_batch, kerasvstf.keras .: cada uno de los cuatro usa diferentes bucles de tren, aunque quizás no en todas las combinaciones posibles. keras' fit, por ejemplo, usa una forma de fit_loop, por ejemplo training_arrays.fit_loop(), y train_on_batchpuede usar K.function(). tf.kerastiene una jerarquía más sofisticada descrita en parte en la sección anterior.


Train Loops: documentación - cadena de documentación fuente relevante en algunos de los diferentes métodos de ejecución:

A diferencia de otras operaciones de TensorFlow, no convertimos entradas numéricas de python en tensores. Además, se genera un nuevo gráfico para cada valor numérico de Python distinto

function crea una instancia de un gráfico separado para cada conjunto único de formas de entrada y tipos de datos .

Un solo objeto tf.function podría necesitar mapearse a múltiples gráficos de cálculo debajo del capó. Esto debería ser visible solo como rendimiento (los gráficos de seguimiento tienen un costo computacional y de memoria distinto de cero )


Procesadores de datos de entrada : similar al anterior, el procesador se selecciona caso por caso, dependiendo de los indicadores internos establecidos de acuerdo con las configuraciones de tiempo de ejecución (modo de ejecución, formato de datos, estrategia de distribución). El caso más simple es con Eager, que funciona directamente con matrices Numpy. Para algunos ejemplos específicos, vea esta respuesta .


TAMAÑO DEL MODELO, TAMAÑO DE LOS DATOS:

  • Es decisivo; ninguna configuración única se coronó sobre todos los tamaños de modelo y datos.
  • El tamaño de los datos en relación con el tamaño del modelo es importante; para datos y modelos pequeños, la sobrecarga de transferencia de datos (por ejemplo, CPU a GPU) puede dominar. Del mismo modo, los pequeños procesadores generales pueden funcionar más lentamente en grandes datos por tiempo de conversión de datos dominante (ver convert_to_tensoren "PERFIL")
  • La velocidad difiere según los diferentes medios de procesamiento de recursos de los bucles de tren y los procesadores de datos de entrada.

BENCHMARKS : la carne picada. - Documento de Word - Hoja de cálculo de Excel


Terminología :

  • Los números sin% son todos segundos
  • % calculado como (1 - longer_time / shorter_time)*100; justificación: nos interesa qué factor es uno más rápido que el otro; shorter / longeren realidad es una relación no lineal, no es útil para la comparación directa
  • % de determinación de signos:
    • TF2 vs TF1: +si TF2 es más rápido
    • GvE (Graph vs. Eager): +si Graph es más rápido
  • TF2 = TensorFlow 2.0.0 + Keras 2.3.1; TF1 = TensorFlow 1.14.0 + Keras 2.2.5

PERFIL :


PERFIL - Explicación : Spyder 3.3.6 IDE profiler.

  • Algunas funciones se repiten en nidos de otros; por lo tanto, es difícil rastrear la separación exacta entre las funciones de "procesamiento de datos" y "entrenamiento", por lo que habrá cierta superposición, como se manifestó en el último resultado.

  • % de cifras calculadas en tiempo de ejecución wrt menos tiempo de construcción

  • Tiempo de compilación calculado sumando todos los tiempos de ejecución (únicos) que se llamaron 1 o 2 veces
  • Tiempo de entrenamiento calculado sumando todos los tiempos de ejecución (únicos) que se llamaron el mismo número de veces que el número de iteraciones, y algunos de los tiempos de ejecución de sus nidos
  • Desafortunadamente, las funciones se perfilan de acuerdo con sus nombres originales (es decir, _func = funcse perfilarán como func), lo que se mezcla en el tiempo de compilación, de ahí la necesidad de excluirlo

ENTORNO DE PRUEBA :

  • Código ejecutado en la parte inferior con tareas mínimas en segundo plano en ejecución
  • La GPU se "calentó" con algunas iteraciones antes de las iteraciones de temporización, como se sugiere en esta publicación
  • CUDA 10.0.130, cuDNN 7.6.0, TensorFlow 1.14.0 y TensorFlow 2.0.0 creados a partir de la fuente, más Anaconda
  • Python 3.7.4, Spyder 3.3.6 IDE
  • GTX 1070, Windows 10, 24GB DDR4 RAM de 2.4 MHz, CPU i7-7700HQ 2.8-GHz

METODOLOGÍA :

  • Benchmark 'pequeño', 'mediano' y 'grande' de modelos y tamaños de datos
  • Se corrigió el número de parámetros para cada tamaño de modelo, independientemente del tamaño de los datos de entrada
  • El modelo "más grande" tiene más parámetros y capas
  • Los datos "más grandes" tienen una secuencia más larga, pero igual batch_sizeynum_channels
  • Sólo utilizan modelos Conv1D, Densecapas '' se pueden aprender; RNNs evitados por implem versión TF. diferencias
  • Siempre ejecutó un ajuste de tren fuera del ciclo de evaluación comparativa, para omitir la construcción del modelo y del optimizador gráfico
  • No utilizar datos dispersos (p layers.Embedding(). Ej. ) U objetivos dispersos (p. Ej.SparseCategoricalCrossEntropy()

LIMITACIONES : una respuesta "completa" explicaría todos los posibles bucles e iteradores de trenes, pero eso seguramente está más allá de mi capacidad de tiempo, cheque de pago inexistente o necesidad general. Los resultados son tan buenos como la metodología: interprete con una mente abierta.


CÓDIGO :

import numpy as np
import tensorflow as tf
import random
from termcolor import cprint
from time import time

from tensorflow.keras.layers import Input, Dense, Conv1D
from tensorflow.keras.layers import Dropout, GlobalAveragePooling1D
from tensorflow.keras.models import Model
from tensorflow.keras.optimizers import Adam
import tensorflow.keras.backend as K
#from keras.layers import Input, Dense, Conv1D
#from keras.layers import Dropout, GlobalAveragePooling1D
#from keras.models import Model 
#from keras.optimizers import Adam
#import keras.backend as K

#tf.compat.v1.disable_eager_execution()
#tf.enable_eager_execution()

def reset_seeds(reset_graph_with_backend=None, verbose=1):
    if reset_graph_with_backend is not None:
        K = reset_graph_with_backend
        K.clear_session()
        tf.compat.v1.reset_default_graph()
        if verbose:
            print("KERAS AND TENSORFLOW GRAPHS RESET")

    np.random.seed(1)
    random.seed(2)
    if tf.__version__[0] == '2':
        tf.random.set_seed(3)
    else:
        tf.set_random_seed(3)
    if verbose:
        print("RANDOM SEEDS RESET")

print("TF version: {}".format(tf.__version__))
reset_seeds()

def timeit(func, iterations, *args, _verbose=0, **kwargs):
    t0 = time()
    for _ in range(iterations):
        func(*args, **kwargs)
        print(end='.'*int(_verbose))
    print("Time/iter: %.4f sec" % ((time() - t0) / iterations))

def make_model_small(batch_shape):
    ipt   = Input(batch_shape=batch_shape)
    x     = Conv1D(128, 40, strides=4, padding='same')(ipt)
    x     = GlobalAveragePooling1D()(x)
    x     = Dropout(0.5)(x)
    x     = Dense(64, activation='relu')(x)
    out   = Dense(1,  activation='sigmoid')(x)
    model = Model(ipt, out)
    model.compile(Adam(lr=1e-4), 'binary_crossentropy')
    return model

def make_model_medium(batch_shape):
    ipt = Input(batch_shape=batch_shape)
    x = ipt
    for filters in [64, 128, 256, 256, 128, 64]:
        x  = Conv1D(filters, 20, strides=1, padding='valid')(x)
    x     = GlobalAveragePooling1D()(x)
    x     = Dense(256, activation='relu')(x)
    x     = Dropout(0.5)(x)
    x     = Dense(128, activation='relu')(x)
    x     = Dense(64,  activation='relu')(x)
    out   = Dense(1,   activation='sigmoid')(x)
    model = Model(ipt, out)
    model.compile(Adam(lr=1e-4), 'binary_crossentropy')
    return model

def make_model_large(batch_shape):
    ipt   = Input(batch_shape=batch_shape)
    x     = Conv1D(64,  400, strides=4, padding='valid')(ipt)
    x     = Conv1D(128, 200, strides=1, padding='valid')(x)
    for _ in range(40):
        x = Conv1D(256,  12, strides=1, padding='same')(x)
    x     = Conv1D(512,  20, strides=2, padding='valid')(x)
    x     = Conv1D(1028, 10, strides=2, padding='valid')(x)
    x     = Conv1D(256,   1, strides=1, padding='valid')(x)
    x     = GlobalAveragePooling1D()(x)
    x     = Dense(256, activation='relu')(x)
    x     = Dropout(0.5)(x)
    x     = Dense(128, activation='relu')(x)
    x     = Dense(64,  activation='relu')(x)    
    out   = Dense(1,   activation='sigmoid')(x)
    model = Model(ipt, out)
    model.compile(Adam(lr=1e-4), 'binary_crossentropy')
    return model

def make_data(batch_shape):
    return np.random.randn(*batch_shape), \
           np.random.randint(0, 2, (batch_shape[0], 1))

def make_data_tf(batch_shape, n_batches, iters):
    data = np.random.randn(n_batches, *batch_shape),
    trgt = np.random.randint(0, 2, (n_batches, batch_shape[0], 1))
    return tf.data.Dataset.from_tensor_slices((data, trgt))#.repeat(iters)

batch_shape_small  = (32, 140,   30)
batch_shape_medium = (32, 1400,  30)
batch_shape_large  = (32, 14000, 30)

batch_shapes = batch_shape_small, batch_shape_medium, batch_shape_large
make_model_fns = make_model_small, make_model_medium, make_model_large
iterations = [200, 100, 50]
shape_names = ["Small data",  "Medium data",  "Large data"]
model_names = ["Small model", "Medium model", "Large model"]

def test_all(fit=False, tf_dataset=False):
    for model_fn, model_name, iters in zip(make_model_fns, model_names, iterations):
        for batch_shape, shape_name in zip(batch_shapes, shape_names):
            if (model_fn is make_model_large) and (batch_shape is batch_shape_small):
                continue
            reset_seeds(reset_graph_with_backend=K)
            if tf_dataset:
                data = make_data_tf(batch_shape, iters, iters)
            else:
                data = make_data(batch_shape)
            model = model_fn(batch_shape)

            if fit:
                if tf_dataset:
                    model.train_on_batch(data.take(1))
                    t0 = time()
                    model.fit(data, steps_per_epoch=iters)
                    print("Time/iter: %.4f sec" % ((time() - t0) / iters))
                else:
                    model.train_on_batch(*data)
                    timeit(model.fit, iters, *data, _verbose=1, verbose=0)
            else:
                model.train_on_batch(*data)
                timeit(model.train_on_batch, iters, *data, _verbose=1)
            cprint(">> {}, {} done <<\n".format(model_name, shape_name), 'blue')
            del model

test_all(fit=True, tf_dataset=False)
OverLordGoldDragon
fuente
No estoy seguro si su código es correcto. Creo que sus modelos siempre se ejecutan en modo gráfico ya que llama model.compilesin run_eagerly=Trueargumento. Si está en modo ansioso, puede ejecutar parte de su código en modo gráfico usando tf.function. Por lo tanto, creo que la implementación predeterminada de compilees crear un gráfico computacional en lugar de ejecutarlo ansiosamente por razones de rendimiento. También tenga en cuenta que si su modelo es convolucional, entonces no verá la aceleración en modo gráfico ya que la interacción con Python es mínima. Si realiza muchas operaciones matemáticas, puede hacer una gran diferencia (también en la utilización de la memoria).
user2781994
@OverLordGoldDragon pero en TF 2, el modo ansioso es por defecto pero model.compileno run_eagerly=Truegarantiza el modo gráfico, ¿o no?
user2781994
@OverLordGoldDragon Estoy de acuerdo en que no todos los métodos importados se ejecutan en modo gráfico, pero creo que eso model.compileo model.fitdebe garantizar que la capacitación se ejecute en modo gráfico internamente.
user2781994
@OverLordGoldDragon TRUE - "tf.keras.Model.compile toma tres argumentos importantes: ... Además, para asegurarse de que el modelo entrena y evalúa con entusiasmo, puede asegurarse de pasar run_eagerly=Truecomo parámetro para compilar". (fuente tensorflow.org/guide/keras/overview ) Por lo tanto, si no pasa el run_eagerly=Truemodelo, PUEDO ejecutarlo en modo gráfico. No estoy seguro de cuál es el factor decisivo, pero ¿por qué no funcionaría en modo gráfico si es más eficiente que ansioso?
usuario2781994
¿Quieres más evidencia? :) "Por defecto, intentaremos compilar su modelo en un gráfico estático para ofrecer el mejor rendimiento de ejecución". ( github.com/tensorflow/tensorflow/blob/r2.0/tensorflow/python/… )
usuario2781994