Por que o TensorFlow 2 é muito mais lento que o TensorFlow 1?

137

Ele foi citado por muitos usuários como o motivo da mudança para o Pytorch, mas ainda não encontrei uma justificativa / explicação para sacrificar a qualidade prática mais importante, a velocidade, para uma execução mais ágil.

Abaixo está o desempenho do benchmarking de código, TF1 vs. TF2 - com o TF1 executando de 47% a 276% mais rápido .

Minha pergunta é: o que é, no nível do gráfico ou do hardware, que gera uma desaceleração tão significativa?


Procurando uma resposta detalhada - já estou familiarizado com conceitos amplos. Git Relevante

Especificações : CUDA 10.0.130, cuDNN 7.4.2, Python 3.7.4, Windows 10, GTX 1070


Resultados de referência :


ATUALIZAÇÃO : Desabilitar a Execução Ansiosa pelo código abaixo não ajuda. O comportamento, no entanto, é inconsistente: algumas vezes, a execução no modo gráfico ajuda consideravelmente, outras vezes, mais devagar em relação ao Eager.

Como os desenvolvedores do TF não aparecem em nenhum lugar, eu mesmo vou investigar esse assunto - posso acompanhar o progresso na questão do Github vinculada.

ATUALIZAÇÃO 2 : toneladas de resultados experimentais para compartilhar, juntamente com explicações; deve ser feito hoje.


Código de referência :

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

Funções 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
fonte
Você já usou esse tipo de ferramenta do cProfile para analisar qual parte as torna tão diferentes?
Zihaozhihao 18/10/19
@zihaozhihao eu tenho , embora não especificamente para isso; por link anterior e escrevendo um otimizador personalizado, já estou familiarizado com as diferenças de chamadas, mas não entendo por que uma é mais lenta que a outra - nem qualquer especialista que não seja de TF pode entendê-lo da fonte, que, além de ser uma bagunça emaranhada, não documenta performances relativas. Intel-nível do hardware gráfico / é exigido, que profilers não irá fornecer (tanto quanto eu sou capaz de usá-los)
OverLordGoldDragon
a versão numpy é a mesma nos dois testes?
chabir
Ai ... Se o velho Keras sozinho já era significativamente mais lento que o PyTorch, imagine agora.
Daniel Möller
o problema é dimensionado com o tamanho do modelo? Você também tentou executar o mesmo benchmark em outro sistema operacional?
okawo

Respostas:

76

ATUALIZAÇÃO 18/2/2020 : Eu já paguei 2,1 e 2,1 todas as noites; os resultados são mistos. Todas as configurações, exceto uma (modelo e tamanho dos dados), são tão rápidas quanto ou muito mais rápidas que as melhores do TF2 e TF1. O que é mais lento e mais dramaticamente, é Grande-Grande - esp. na execução do gráfico ( 1,6x a 2,5x mais lento ).

Além disso, existem extrema diferenças reprodutibilidade entre o Graph e o Eager para um modelo grande que eu testei - um que não pode ser explicado por meio de aleatoriedade / paralelismo da computação. No momento, não posso apresentar código reproduzível para essas reivindicações por restrições de tempo; portanto, recomendo fortemente testá-lo para seus próprios modelos.

Ainda não abri um problema do Git sobre eles, mas comentei o original - ainda não há resposta. Atualizarei a (s) resposta (s) assim que o progresso for feito.


VERDITO : não é , se você sabe o que está fazendo. Mas se você fizer , isso poderá custar muito - com algumas atualizações de GPU, em média, e com o pior caso de várias GPUs.


ESTA RESPOSTA : tem como objetivo fornecer uma descrição de alto nível do problema, bem como diretrizes sobre como decidir sobre a configuração de treinamento específica para suas necessidades. Para uma descrição detalhada e de baixo nível, que inclui todos os resultados de benchmarking + código usado, consulte minha outra resposta.

Estarei atualizando minhas respostas com mais informações, se descobrir alguma - pode marcar / "marcar com estrela" esta pergunta para referência.


RESUMO DA QUESTÃO : as DO confirmado por um desenvolvedor do TensorFlow, Q. Scott Zhu, o TF2 focou o desenvolvimento na execução ansiosa e forte integração com o Keras, que envolveu mudanças radicais na fonte do TF - inclusive no nível do gráfico. Benefícios: recursos de processamento, distribuição, depuração e implantação amplamente expandidos. O custo de alguns deles, no entanto, é a velocidade.

O assunto, no entanto, é bastante mais complexo. Não se trata apenas de TF1 vs. TF2 - os fatores que geram diferenças significativas na velocidade do trem incluem:

  1. TF2 vs. TF1
  2. Modo Ansioso vs. 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.

Infelizmente, quase nenhuma das opções acima é independente da outra, e cada uma pode pelo menos dobrar o tempo de execução em relação à outra. Felizmente, você pode determinar o que funcionará melhor sistematicamente e com alguns atalhos - como mostrarei.


O QUE DEVO FAZER? Atualmente, a única maneira é experimentar o seu modelo, dados e hardware específicos. Nenhuma configuração única será sempre funciona melhor - mas não são fizeram e não é para simplificar a sua pesquisa:

>> FAÇA:

  • train_on_batch()+ numpy+ tf.keras+ TF1 + Ansioso / Gráfico
  • train_on_batch()+ numpy+ tf.keras+ TF2 + Gráfico
  • fit()+ numpy+ tf.keras+ TF1 / TF2 + Gráfico + modelo grande e dados

>> NÃO:

  • fit()+ numpy+ keraspara modelos e dados pequenos e médios
  • fit()+ numpy+ tf.keras+ TF1 / TF2 + Ansioso
  • train_on_batch()+ numpy+ keras+ TF1 + Ansioso

  • [Major] tf.python.keras ; ele pode ser executado 10-100x mais lento e com muitos bugs; mais informações

    • Isto inclui layers, models, optimizers, e relacionado "out-of-box" importações de uso; ops, utils e importações 'privadas' relacionadas são boas - mas para ter certeza, verifique se há alterações e se elas são usadas emtf.keras

Consulte o código na parte inferior da minha outra resposta para um exemplo de configuração de benchmarking. A lista acima é baseada principalmente nas tabelas "BENCHMARKS" na outra resposta.


LIMITAÇÕES dos DOs e NÃOs acima:

  • Esta pergunta é intitulada "Por que o TF2 é muito mais lento que o TF1?" E, embora seu corpo esteja relacionado ao treinamento explicitamente, o assunto não se limita a ele; a inferência também está sujeita a grandes diferenças de velocidade, mesmo dentro da mesma versão TF, importação, formato de dados etc. - veja esta resposta .
  • É provável que as RNNs alterem notavelmente a grade de dados na outra resposta, pois foram aprimoradas no TF2
  • Modelos usados ​​principalmente Conv1De Dense- sem RNNs, dados / destinos esparsos, entradas 4 / 5D e outras configurações
  • Os dados de entrada limitadas a numpye tf.data.Dataset, embora existam muitos outros formatos; veja outra resposta
  • GPU foi usado; os resultados serão diferentes em uma CPU. De fato, quando fiz a pergunta, meu CUDA não estava configurado corretamente e alguns dos resultados foram baseados na CPU.

Por que o TF2 sacrificou a qualidade mais prática, a velocidade, para uma execução mais ágil? Claramente não está - o gráfico ainda está disponível. Mas se a pergunta é "por que ficar ansiosa":

  • Depuração superior : você provavelmente já encontrou várias perguntas: "como obtenho saídas de camada intermediária" ou "como inspeciono pesos"; com ansioso, é (quase) tão simples quanto .__dict__. O gráfico, ao contrário, requer familiaridade com funções especiais de back-end - complicando bastante todo o processo de depuração e introspecção.
  • Prototipagem mais rápida : por idéias semelhantes às anteriores; entendimento mais rápido = mais tempo restante para o DL real.

COMO ATIVAR / DESATIVAR O EAGER?

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

INFORMAÇÕES ADICIONAIS :

  • Cuidado com os _on_batch()métodos no TF2; de acordo com o desenvolvedor do TF, eles ainda usam uma implementação mais lenta, mas não intencionalmente - isto é, deve ser corrigido. Veja outra resposta para detalhes.

PEDIDOS PARA DEVS DE FLUXO DE TENSOR :

  1. Corrija train_on_batch()e o aspecto de desempenho da chamada fit()iterativa; loops de trem personalizados são importantes para muitos, especialmente para mim.
  2. Adicione documentação / menção de documentação dessas diferenças de desempenho para o conhecimento dos usuários.
  3. Melhore a velocidade geral de execução para impedir que os espreitadelas pulem para o Pytorch.

AGRADECIMENTOS : Graças a


ATUALIZAÇÕES :

  • 14/11/19 - encontrei um modelo (no meu aplicativo real) que é executado mais lentamente no TF2 para todas as configurações * com dados de entrada Numpy. As diferenças variaram de 13 a 19%, com média de 17%. As diferenças entre kerase tf.keras, no entanto, foram mais dramáticas: 18-40% , média. 32% (ambos TF1 e 2). (* - exceto Eager, para o qual TF2 OOM'd)

  • 17/11/19 - devs atualizou on_batch()métodos em uma consolidação recente , afirmando ter uma velocidade aprimorada - a ser lançado no TF 2.1 ou disponível agora como tf-nightly. Como não consigo rodar mais tarde, o atraso será atrasado até o 2.1.

  • 20/2/20 - o desempenho da previsão também vale a pena comparar; no TF2, por exemplo, os tempos de previsão da CPU podem envolver picos periódicos
OverLordGoldDragon
fonte
3
Que tal fit_generator? ... Eu praticamente nunca quero train_on_batche gerenciar meu próprio ciclo de treinamento em lotes é um enorme antipadrão a ser evitado, mesmo a um grande custo.
Ely
@ely Ele ainda precisa ser testado, conforme observado na minha outra resposta - mas, se houver algo, eu prevejo que ele seja uma fitpequena sobrecarga adicional de processamento de dados. Quanto aos loops de trem, escrevi meu próprio personalizado que acabou se transformando em uma espécie de API; fit_generatorcarece de introspecção, capacidade de personalização e economia / carregamento - portanto, não é absolutamente para mim. Eventualmente, publicarei meu ciclo de treinamento no Github.
OverLordGoldDragon
A falta de introspecção e capacidade de personalização é um recurso para mim, não um bug. IDK a que o comentário salvar / carregar está se referindo? Salvamento / carregamento intermediário durante um loop não controlado pelo gerador de dados? (Eu também estou pessoalmente feliz contando apenas com retornos de chamada para isso e veria a necessidade de qualquer personalização adicional como um cheiro de código que meu loop de treinamento foi projetado incorretamente).
Ely
@ely Não é simples, mas é necessário para treinar pipelines de dados de entrada complexos, funções objetivas e configurações de modelo não API (por exemplo, conjuntos). A introspecção é essencial para muitos fins de depuração e engenharia de recursos. Na falta de um save / load externo e na pausabilidade e retomada do loop de trem para modelos caros em termos de computação - um pesadelo. Independentemente disso, em última análise, depende de suas necessidades específicas e não é abordado; a maneira mais segura de testar o desempenho com fit_generatorsua aplicação é testá-lo.
OverLordGoldDragon
47

ESTA RESPOSTA : tem como objetivo fornecer uma descrição detalhada do problema em nível de gráfico / hardware - incluindo loops de trem TF2 vs. TF1, processadores de dados de entrada e execuções no modo Ansioso vs. Gráfico. Para um resumo do problema e diretrizes de resolução, consulte minha outra resposta.


VERDITO DE DESEMPENHO : às vezes um é mais rápido, às vezes o outro, dependendo da configuração. No que diz respeito ao TF2 vs TF1, eles estão em pé de igualdade, em média, mas existem diferenças significativas baseadas na configuração, e o TF1 supera o TF2 com mais frequência do que vice-versa. Veja "BENCHMARKING" abaixo.


EAGER VS. GRÁFICO : a carne de toda essa resposta para alguns: o desejo do TF2 é mais lento que o do TF1, de acordo com meus testes. Detalhes mais abaixo.

A diferença fundamental entre os dois é: o Graph configura uma rede computacional de forma proativa e é executado quando 'solicitado' - enquanto o Eager executa tudo na criação. Mas a história só começa aqui:

  • Ansioso NÃO é desprovido de Graph , e pode de fato ser principalmente Graph, contrário à expectativa. O que é em grande parte, é executado Graph - isso inclui pesos de modelo e otimizador, compreendendo grande parte do gráfico.

  • Ansioso reconstrói parte do próprio gráfico na execução ; conseqüência direta do Graph não estar totalmente construído - veja os resultados do criador de perfil. Isso tem uma sobrecarga computacional.

  • Ansioso é mais lento com entradas Numpy ; de acordo com esse comentário e código do Git , as entradas do Numpy no Eager incluem o custo adicional de copiar tensores da CPU para a GPU. Percorrendo o código fonte, as diferenças de manipulação de dados são claras; Ansioso passa diretamente o Numpy, enquanto o Graph passa os tensores que são avaliados para o Numpy; incerto do processo exato, mas este último deve envolver otimizações no nível da GPU

  • TF2 Eager é mais lento que TF1 Eager - isso é ... inesperado. Veja os resultados do benchmarking abaixo. As diferenças variam de insignificante a significante, mas são consistentes. Não sei por que é esse o caso - se um desenvolvedor do TF esclarecer, atualizará a resposta.


TF2 vs. TF1 : citando partes relevantes da resposta de um desenvolvedor de TF, Q. Scott Zhu, com um pouco da minha ênfase e reformulação:

No aguardo, o tempo de execução precisa executar as operações e retornar o valor numérico para cada linha de código python. A natureza da execução de uma única etapa faz com que seja lenta .

No TF2, o Keras utiliza o tf.function para criar seu gráfico para treinamento, avaliação e previsão. Nós os chamamos de "função de execução" para o modelo. No TF1, a "função de execução" era um FuncGraph, que compartilhava algum componente comum como função TF, mas tem uma implementação diferente.

Durante o processo, deixamos de alguma forma uma implementação incorreta para train_on_batch (), test_on_batch () e predict_on_batch () . Eles ainda estão numericamente corretos , mas a função de execução para x_on_batch é uma função python pura, em vez de uma função python empacotada tf.function. Isso causará lentidão

No TF2, convertemos todos os dados de entrada em um tf.data.Dataset, pelo qual podemos unificar nossa função de execução para lidar com o tipo único de entradas. Pode haver alguma sobrecarga na conversão do conjunto de dados , e acho que essa é uma sobrecarga única, em vez de um custo por lote

Com a última frase do último parágrafo acima e a última cláusula do parágrafo abaixo:

Para superar a lentidão no modo ansioso, temos @ tf.function, que transformará uma função python em um gráfico. Ao alimentar valor numérico como matriz np, o corpo da função tf.f. é convertido em gráfico estático, sendo otimizado e retorna o valor final, que é rápido e deve ter desempenho semelhante ao modo gráfico TF1.

Discordo - de acordo com meus resultados de criação de perfil, que mostram que o processamento de dados de entrada do Eager é substancialmente mais lento que o do Graph. Além disso, não tem certeza sobre isso tf.data.Datasetem particular, mas o Eager chama repetidamente vários dos mesmos métodos de conversão de dados - consulte o profiler.

Por fim, o commit vinculado do desenvolvedor: número significativo de alterações para dar suporte aos loops do Keras v2 .


Loops de trem : dependendo de (1) Ansioso vs. Gráfico; (2) formato de dados de entrada, a formação em prosseguirá com um laço trem distintas - em TF2, _select_training_loop(), training.py , um dos seguintes:

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 um deles lida com a alocação de recursos de maneira diferente e tem conseqüências no desempenho e na capacidade.


Loops de trem: fitvs train_on_batch, kerasvstf.keras .: cada um dos quatro usa loops de trem diferentes, embora talvez não em todas as combinações possíveis. keras' fit, por exemplo, usa uma forma de fit_loop, por exemplo training_arrays.fit_loop(), e train_on_batchpode ser usada K.function(). tf.keraspossui uma hierarquia mais sofisticada descrita em parte na seção anterior.


Loops de treinamento: documentação - documentação relevante da fonte sobre alguns dos diferentes métodos de execução:

Diferentemente de outras operações do TensorFlow, não convertemos entradas numéricas python em tensores. Além disso, um novo gráfico é gerado para cada valor numérico python distinto

function instancia um gráfico separado para cada conjunto exclusivo de formas e tipos de dados de entrada .

Um único objeto tf.function pode precisar mapear para vários gráficos de computação sob o capô. Isso deve ser visível apenas como desempenho (os gráficos de rastreamento têm um custo computacional e de memória diferente de zero )


Processadores de dados de entrada : semelhante ao acima, o processador é selecionado caso a caso, dependendo dos sinalizadores internos definidos de acordo com as configurações de tempo de execução (modo de execução, formato de dados, estratégia de distribuição). O caso mais simples é o Eager, que funciona diretamente com matrizes Numpy. Para alguns exemplos específicos, consulte esta resposta .


TAMANHO DO MODELO, TAMANHO DOS DADOS:

  • É decisivo; nenhuma configuração única se destacava no topo de todos os tamanhos de modelo e de dados.
  • O tamanho dos dados em relação ao tamanho do modelo é importante; para dados e modelos pequenos, a transferência de dados (por exemplo, CPU para GPU) pode dominar. Da mesma forma, pequenos processadores aéreos podem rodar mais devagar em grandes dados por tempo de conversão de dados dominante (consulte convert_to_tensor"PROFILER")
  • A velocidade varia de acordo com os diferentes meios de processamento dos recursos dos loops de trem e dos processadores de dados de entrada.

REFERÊNCIAS : a carne moída. - Documento do Word - Planilha do Excel


Terminologia :

  • % sem números são todos os segundos
  • % calculado como (1 - longer_time / shorter_time)*100; lógica: estamos interessados em qual fator um é mais rápido que o outro; shorter / longeré na verdade uma relação não linear, não é útil para comparação direta
  • Determinação do sinal de%:
    • TF2 vs TF1: +se o TF2 for mais rápido
    • GvE (Gráfico vs. Ansioso): +se o Gráfico for mais rápido
  • TF2 = TensorFlow 2.0.0 + Keras 2.3.1; TF1 = TensorFlow 1.14.0 + Keras 2.2.5

PROFILER :


PROFILER - Explicação : Spyder 3.3.6 IDE profiler.

  • Algumas funções são repetidas em ninhos de outras; portanto, é difícil rastrear a separação exata entre as funções "processamento de dados" e "treinamento", para que haja alguma sobreposição - conforme pronunciado no último resultado.

  • % de números calculados em tempo de execução wrt menos tempo de compilação

  • Tempo de construção calculado somando todos os tempos de execução (únicos) que foram chamados 1 ou 2 vezes
  • Tempo de trem calculado somando todos os tempos de execução (únicos) que foram chamados o mesmo número de vezes que o número de iterações e alguns dos tempos de execução de seus ninhos
  • Infelizmente, as funções são definidas de acordo com seus nomes originais (ou seja, _func = functerão o perfil como func), o que se mistura no tempo de construção - daí a necessidade de excluí-lo

AMBIENTE DE TESTE :

  • Código executado na parte inferior com o mínimo de tarefas em segundo plano em execução
  • A GPU foi "aquecida" com algumas iterações antes de cronometrar as iterações, conforme sugerido nesta postagem
  • CUDA 10.0.130, cuDNN 7.6.0, TensorFlow 1.14.0 e TensorFlow 2.0.0 criados a partir da fonte, além do Anaconda
  • Python 3.7.4, Spyder 3.3.6 IDE
  • GTX 1070, Windows 10, 24 GB DDR4 2,4 MHz RAM, i7-7700HQ CPU de 2,8 GHz

METODOLOGIA :

  • Modelo de referência 'pequeno', 'médio' e 'grande' e tamanhos de dados
  • Corrija o número de parâmetros para cada tamanho de modelo, independentemente do tamanho dos dados de entrada
  • O modelo "maior" possui mais parâmetros e camadas
  • Dados "maiores" têm uma sequência mais longa, mas o mesmo batch_sizeenum_channels
  • Modelos só uso Conv1D, Densecamadas 'learnable'; RNNs evitados por implemento da versão TF. diferenças
  • Sempre executou um ajuste de trem fora do loop de benchmarking, para omitir a construção de gráficos do modelo e do otimizador
  • Não usar dados esparsos (por exemplo layers.Embedding()) ou destinos esparsos (por exemploSparseCategoricalCrossEntropy()

LIMITAÇÕES : uma resposta "completa" explicaria todos os circuitos e iteradores de trem possíveis, mas isso certamente está além da minha capacidade de tempo, salário inexistente ou necessidade geral. Os resultados são tão bons quanto a metodologia - interpretem com a mente aberta.


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
fonte
Não tenho certeza se seu código está correto. Eu acho que seus modelos sempre rodam no modo gráfico desde que você liga model.compilesem run_eagerly=Trueargumento. Se estiver no modo rápido, você pode executar parte do seu código no modo gráfico usando tf.function. Portanto, acho que a implementação padrão de compileé criar gráfico computacional em vez de executá-lo ansiosamente por razões de desempenho. Observe também que, se seu modelo for convolucional, você não verá a aceleração no modo gráfico, pois a interação do python é mínima. Se você faz muitas operações matemáticas, isso pode fazer uma grande diferença (também na utilização da memória).
user2781994
@OverLordGoldDragon, mas no TF 2, o modo ansioso é por padrão, mas model.compilesem run_eagerly=Truegarante o modo gráfico, ou não?
user2781994
@OverLordGoldDragon Concordo que nem todos os métodos importados são executados no modo gráfico, mas acho que um model.compileou model.fitdeve garantir que o treinamento seja executado internamente no modo gráfico.
user2781994
@OverLordGoldDragon TRUE - "tf.keras.Model.compile leva três argumentos importantes: ... Além disso, para garantir que o modelo treine e avalie avidamente, você pode passar run_eagerly=Truecomo um parâmetro para compilar." (fonte tensorflow.org/guide/keras/overview ) Portanto, se você não passa o run_eagerly=Truemodelo, PODE executar no modo gráfico. Não tenho certeza qual é o fator decisivo, mas por que ele não funcionaria no modo gráfico se é mais eficiente do que ansioso?
user2781994
Você quer mais evidências? :) "Por padrão, tentaremos compilar seu modelo em um gráfico estático para oferecer o melhor desempenho de execução." ( github.com/tensorflow/tensorflow/blob/r2.0/tensorflow/python/… )
user2781994