Perché TensorFlow 2 è molto più lento di TensorFlow 1?


137

È stato citato da molti utenti come la ragione per passare a Pytorch, ma devo ancora trovare una giustificazione / spiegazione per sacrificare la più importante qualità pratica, velocità, per un'esecuzione entusiasta.

Di seguito sono riportate le prestazioni di benchmarking del codice, TF1 vs. TF2 - con TF1 in esecuzione ovunque dal 47% al 276% più veloce .

La mia domanda è: che cos'è, a livello di grafico o hardware, che produce un rallentamento così significativo?


Alla ricerca di una risposta dettagliata, ho già familiarità con concetti generali. Git pertinente

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


Risultati benchmark :


AGGIORNAMENTO : Disabilitare l'esecuzione desiderosa per il codice sottostante non aiuta. Il comportamento, tuttavia, è incoerente: a volte l'esecuzione in modalità grafica aiuta notevolmente, altre volte corre più lentamente rispetto a Eager.

Dato che gli sviluppatori di TF non compaiono da nessuna parte, indagherò io stesso sulla questione: posso seguire i progressi nel problema di Github collegato.

AGGIORNAMENTO 2 : tonnellate di risultati sperimentali da condividere, insieme a spiegazioni; dovrebbe essere fatto oggi.


Codice benchmark :

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

Funzioni utilizzate :

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

Hai mai usato cProfile con questo tipo di strumento per analizzare quale parte li rende così diversi?
zihaozhihao,

@zihaozhihao l' ho fatto , anche se non per questo in particolare; per link precedente e scrivendo un ottimizzatore personalizzato, ho già familiarità con le differenze nelle chiamate, ma non capisco perché uno è più lento dell'altro - né un esperto non TF può capirlo dalla fonte, che, oltre a essere un pasticcio aggrovigliato, non documenta le prestazioni relative. Sono richieste informazioni a livello di grafico / hardware, che i profiler non forniranno (per quanto sono in grado di usarle)
OverLordGoldDragon,

la versione numpy è la stessa in entrambi i test?
Chabir,

Ahi .... Se solo la vecchia Keras fosse già significativamente più lenta di PyTorch, immagina ora.
Daniel Möller,

il problema si ridimensiona con le dimensioni del modello? hai anche provato a eseguire lo stesso benchmark su altri sistemi operativi?
okawo,

Risposte:


76

AGGIORNAMENTO 18/02/2020 : ho lavorato in panchina 2.1 e 2.1 di notte; i risultati sono contrastanti. Tutte le configurazioni tranne una (modello e dimensioni dei dati) sono più veloci o molto più veloci dei migliori di TF2 e TF1. Quello che è più lento e più lento drammaticamente, è grande-grande - esp. nell'esecuzione del grafico (da 1,6x a 2,5x più lento ).

Inoltre, ci sono differenze estreme di riproducibilità tra Graph e Eager per un modello di grandi dimensioni che ho testato - uno non spiegabile tramite casualità / parallelismo di calcolo. Al momento non posso presentare codice riproducibile per questi reclami per vincoli temporali, quindi consiglio vivamente di testarlo per i tuoi modelli.

Non ho ancora aperto un problema Git su questi, ma ho commentato l' originale - ancora nessuna risposta. Aggiornerò le risposte una volta che i progressi sono stati fatti.


VERDETTO : non lo è , SE sai cosa stai facendo. Ma se non lo fai , potrebbe costarti molto, con alcuni aggiornamenti GPU in media e nel caso peggiore di più GPU.


QUESTA RISPOSTA : mira a fornire una descrizione di alto livello del problema, nonché linee guida su come decidere la configurazione della formazione specifica per le tue esigenze. Per una descrizione dettagliata di basso livello, che include tutti i risultati di benchmarking + il codice utilizzato, vedere la mia altra risposta.

Aggiornerò la / le mia / e risposta / e con ulteriori informazioni se ne apprendo - è possibile contrassegnare / "contrassegnare" questa domanda come riferimento.


SINTESI DEL PROBLEMA : come confermato da uno sviluppatore di TensorFlow, Q. Scott Zhu, TF2 ha focalizzato lo sviluppo sull'esecuzione desiderosa e la stretta integrazione con Keras, che ha comportato cambiamenti radicali nella fonte TF - anche a livello di grafico. Vantaggi: capacità di elaborazione, distribuzione, debug e distribuzione notevolmente ampliate. Il costo di alcuni di questi, tuttavia, è la velocità.

La questione, tuttavia, è abbastanza più complessa. Non è solo TF1 vs. TF2 - i fattori che producono differenze significative nella velocità del treno includono:

  1. TF2 vs. TF1
  2. Modalità desideroso vs. grafico
  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. ...

Sfortunatamente, quasi nessuna delle precedenti è indipendente dall'altra e ognuna può almeno raddoppiare i tempi di esecuzione rispetto a un'altra. Fortunatamente, puoi determinare cosa funzionerà meglio sistematicamente e con alcune scorciatoie, come mostrerò.


COSA DOVREI FARE? Attualmente, l'unico modo è: sperimentare il modello, i dati e l'hardware specifici. Nessuna configurazione singola funzionerà sempre meglio, ma ci sono cose da fare e da non fare per semplificare la ricerca:

>> DO:

  • train_on_batch()+ numpy+ tf.keras+ TF1 + Desideroso / Grafico
  • train_on_batch()+ numpy+ tf.keras+ TF2 + Grafico
  • fit()+ numpy+ tf.keras+ TF1 / TF2 + Grafico + modello e dati di grandi dimensioni

>> NON:

  • fit()+ numpy+ kerasper modelli e dati di piccole e medie dimensioni
  • fit()+ numpy+ tf.keras+ TF1 / TF2 + Desideroso
  • train_on_batch()+ numpy+ keras+ TF1 + Desideroso

  • [Maggiore] tf.python.keras ; può funzionare 10-100 volte più lentamente e con molti bug; Ulteriori informazioni

    • Questo include layers, models, optimizers, e servizi annessi "out-of-box" le importazioni di utilizzo; ops, utils e le relative importazioni "private" vanno bene, ma per essere sicuri, controlla gli alts e se sono utilizzati intf.keras

Fare riferimento al codice in fondo all'altra mia risposta per un esempio di impostazione di benchmarking. L'elenco sopra riportato si basa principalmente sulle tabelle "BENCHMARKS" nell'altra risposta.


LIMITAZIONI dei precedenti DO e NON:

  • Questa domanda si intitola "Perché TF2 è molto più lenta di TF1?", E mentre il suo corpo riguarda l'allenamento esplicito, la questione non si limita ad essa; anche l'inferenza è soggetta a importanti differenze di velocità, anche all'interno della stessa versione TF, importazione, formato dati, ecc. - vedi questa risposta .
  • È probabile che gli RNN cambino notevolmente la griglia di dati nell'altra risposta, poiché sono stati migliorati in TF2
  • Modelli principalmente utilizzati Conv1De Dense- nessun RNN, dati / target sparsi, ingressi 4 / 5D e altre configurazioni
  • Dati di input limitati a numpye tf.data.Dataset, mentre esistono molti altri formati; vedi altra risposta
  • È stata utilizzata la GPU; risultati potranno differire su una CPU. In effetti, quando ho posto la domanda, il mio CUDA non era configurato correttamente e alcuni dei risultati erano basati sulla CPU.

Perché TF2 ha sacrificato la qualità più pratica, la velocità, per un'esecuzione desiderosa? Non è chiaro, il grafico è ancora disponibile. Ma se la domanda è "perché ansioso":

  • Debug di qualità superiore : è probabile che ci siano molte domande che chiedono "come posso ottenere output di livello intermedio" o "come posso controllare i pesi"; con ansioso, è (quasi) semplice come .__dict__. Il grafico, al contrario, richiede familiarità con le funzioni di backend speciali, complicando notevolmente l'intero processo di debug e introspezione.
  • Prototipazione più rapida : per idee simili a quelle sopra; comprensione più veloce = più tempo rimanente per il DL reale.

COME ATTIVARE / DISATTIVARE EAGER?

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

INFORMAZIONI AGGIUNTIVE :

  • Attento ai _on_batch()metodi in TF2; secondo lo sviluppatore TF, usano ancora un'implementazione più lenta, ma non intenzionalmente - cioè deve essere risolto. Vedi altra risposta per i dettagli.

RICHIESTE AI DISPOSITIVI TENSORFLOW :

  1. Correggi train_on_batch()e l'aspetto delle prestazioni della chiamata in modo fit()iterativo; i circuiti personalizzati sono importanti per molti, specialmente per me.
  2. Aggiungi documentazione / docstring menzione di queste differenze di prestazioni per la conoscenza degli utenti.
  3. Migliora la velocità generale di esecuzione per impedire ai pip di saltare a Pytorch.

RINGRAZIAMENTI : Grazie a


AGGIORNAMENTI :

  • 14/11/19 - trovato un modello (nella mia vera applicazione) che funziona più lentamente su TF2 per tutte le * configurazioni con dati di input Numpy. Le differenze erano comprese tra il 13 e il 19%, con una media del 17%. Le differenze tra kerase tf.keras, tuttavia, erano più drammatiche: 18-40% , media. 32% (sia TF1 che 2). (* - tranne Eager, per il quale TF2 OOM'd)

  • 17/11/19 - Sviluppaon_batch() metodi aggiornati in un recente commit , dichiarando di avere una velocità migliorata - da rilasciare in TF 2.1 o ora disponibile come tf-nightly. Dato che non riesco a far funzionare quest'ultimo, ritarderà la panchina fino alla 2.1.

  • 20/20/20 - vale anche la pena fare una previsione delle prestazioni di previsione; in TF2, ad esempio, i tempi di previsione della CPU possono comportare picchi periodici

3
Che dire fit_generator? ... Non voglio praticamente mai train_on_batche gestire il mio ciclo di allenamento tra i lotti è un enorme, enorme anti-schema da evitare anche a costi elevati.
ely,

@ely Resta da testare, come notato nella mia altra risposta - ma semmai lo prevedo fitcon un sovraccarico di elaborazione dei dati aggiuntivo. Per quanto riguarda i loop dei treni, ho scritto il mio personalizzato che alla fine si è trasformato in una sorta di API; fit_generatormanca di introspezione, personalizzazione e salvataggio / caricamento - quindi assolutamente no per me. Alla fine pubblicherò il mio ciclo di allenamento, su Github.
OverLordGoldDragon

La mancanza di introspezione e personalizzazione è una caratteristica per me, non un bug. IDK a cosa si riferisce il commento di salvataggio / caricamento? Salvataggio / caricamento intermedi durante un loop non controllato dal generatore di dati? (Sono anche personalmente felice di fare affidamento solo sui callback per questo, e vedrei la necessità di ogni ulteriore personalizzazione come un odore di codice che il mio ciclo di allenamento è progettato in modo errato).
ely,

@ely Non è semplice, ma è necessario per l'addestramento con pipeline di dati di input complessi, funzioni oggettive e configurazioni di modelli non API (ad es. ensemble). L'introspezione è un must per molti scopi di debugging e ingegneria delle funzionalità. Mancanza di un salvataggio / carico esterno e la plausibilità e la ripresa del circuito per i modelli costosi dal punto di vista computazionale - un incubo. Indipendentemente da ciò, alla fine dipende dalle vostre esigenze specifiche e dall'essere fuori tema; il modo più sicuro per testare le prestazioni con la fit_generatortua applicazione è, beh, testarlo.
OverLordGoldDragon

47

QUESTA RISPOSTA : mira a fornire una descrizione dettagliata, a livello di grafico / hardware del problema, inclusi loop di treni TF2 vs. TF1, processori di dati di input ed esecuzioni in modalità Eager vs. Graph. Per un riepilogo dei problemi e linee guida per la risoluzione, vedere la mia altra risposta.


VERDETTO DELLE PRESTAZIONI : a volte uno è più veloce, a volte l'altro, a seconda della configurazione. Per quanto riguarda TF2 vs TF1, sono in media alla pari, ma esistono differenze significative basate sulla configurazione e TF1 supera TF2 più spesso di viceversa. Vedi "BENCHMARKING" di seguito.


EAGER VS. GRAFICO : la carne di questa intera risposta per alcuni: il desideroso di TF2 è più lento di quello di TF1, secondo i miei test. Dettagli più in basso.

La differenza fondamentale tra i due è: Graph imposta una rete computazionale in modo proattivo ed esegue quando "detto a" - mentre Eager esegue tutto al momento della creazione. Ma la storia inizia solo qui:

  • Desideroso NON è privo di Grafico , e in realtà può essere principalmente Grafico, contrariamente alle aspettative. Quello che è in gran parte, viene eseguito Graph - questo include pesi del modello e dell'ottimizzatore, che comprende una grande porzione del grafico.

  • Eager ricostruisce parte del proprio grafico durante l'esecuzione ; conseguenza diretta di Graph non completamente costruito - vedere i risultati del profiler. Questo ha un sovraccarico computazionale.

  • Eager è più lento con input Numpy ; in base a questo commento e codice Git , gli input Numpy in Eager includono i costi generali di copia dei tensori dalla CPU alla GPU. Passando attraverso il codice sorgente, le differenze nella gestione dei dati sono chiare; Eager passa direttamente a Numpy, mentre Graph passa a tensori che poi valutano a Numpy; incerto dell'esatto processo, ma quest'ultimo dovrebbe comportare ottimizzazioni a livello di GPU

  • TF2 Eager è più lento di TF1 Eager - questo è ... inaspettato. Vedi i risultati del benchmarking di seguito. Le differenze vanno da trascurabili a significative, ma sono coerenti. Non sono sicuro del perché - se un dev TF chiarisce, aggiornerà la risposta.


TF2 vs. TF1 : citando parti rilevanti di uno sviluppatore TF, Q. Scott Zhu, risposta - con un po 'della mia enfasi e riformulazione:

In impazienza, il runtime deve eseguire le operazioni e restituire il valore numerico per ogni riga di codice Python. La natura dell'esecuzione a singolo passaggio fa sì che sia lenta .

In TF2, Keras sfrutta la funzione tf.per costruire il suo grafico per allenamento, valutazione e previsione. Li chiamiamo "funzione di esecuzione" per il modello. In TF1, la "funzione di esecuzione" era un FuncGraph, che condivideva alcuni componenti comuni come funzione TF, ma ha un'implementazione diversa.

Durante il processo, abbiamo in qualche modo lasciato un'implementazione errata per train_on_batch (), test_on_batch () e predict_on_batch () . Sono ancora numericamente corretti , ma la funzione di esecuzione per x_on_batch è una funzione Python pura, piuttosto che una funzione Python con funzione tf.function. Ciò causerà lentezza

In TF2, convertiamo tutti i dati di input in un tf.data.Dataset, mediante il quale possiamo unificare la nostra funzione di esecuzione per gestire il singolo tipo di input. Potrebbe esserci un certo overhead nella conversione del set di dati e penso che si tratti di un overhead solo una volta, piuttosto che di un costo per batch

Con l'ultima frase dell'ultimo paragrafo sopra e l'ultima clausola del paragrafo seguente:

Per superare la lentezza in modalità desideroso, abbiamo @ tf.function, che trasformerà una funzione python in un grafico. Quando si alimenta un valore numerico come l'array np, il corpo della funzione tf. viene convertito in grafico statico, ottimizzato, e restituisce il valore finale, che è veloce e dovrebbe avere prestazioni simili alla modalità grafico TF1.

Non sono d'accordo - per i miei risultati di profilazione, che mostrano che l'elaborazione dei dati di input di Eager è sostanzialmente più lenta di quella di Graph. Inoltre, non sono sicuro tf.data.Datasetin particolare, ma Eager chiama ripetutamente più metodi di conversione dei dati uguali - vedi profiler.

Infine, il commit collegato di dev: Numero significativo di modifiche per supportare i loop di Keras v2 .


Train Loops : dipende da (1) desideroso vs. grafico; (2) formato di dati in ingresso, la formazione in procederà con un ciclo treno distinte - in TF2, _select_training_loop(), training.py , uno di:

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

Ognuno gestisce l'allocazione delle risorse in modo diverso e ha conseguenze su prestazioni e capacità.


Loop del treno: fitvs train_on_batch, kerasvstf.keras .: ciascuno dei quattro utilizza diversi loop del treno, anche se forse non in tutte le possibili combinazioni. keras" fit, ad esempio, utilizza una forma di fit_loop, ad esempio training_arrays.fit_loop(), e train_on_batchpuò essere utilizzata K.function(). tf.kerasha una gerarchia più sofisticata descritta in parte nella sezione precedente.


Train Loops: documentazione - documentazione sorgente pertinente su alcuni dei diversi metodi di esecuzione:

A differenza di altre operazioni TensorFlow, non convertiamo input numerici python in tensori. Inoltre, viene generato un nuovo grafico per ciascun valore numerico python distinto

function crea un'istanza di un grafico separato per ogni set univoco di forme di input e tipi di dati .

Potrebbe essere necessario mappare un singolo oggetto tf.function su più grafici di calcolo sotto il cofano. Questo dovrebbe essere visibile solo come prestazione (i grafici di tracciamento hanno un costo di calcolo e di memoria diverso da zero )


Processori di dati di input : simile al precedente, il processore viene selezionato caso per caso, a seconda dei flag interni impostati in base alle configurazioni di runtime (modalità di esecuzione, formato dei dati, strategia di distribuzione). Il caso più semplice è con Eager, che funziona direttamente con gli array Numpy. Per alcuni esempi specifici, vedi questa risposta .


MISURA MODELLO, MISURA DATI:

  • È decisivo; nessuna singola configurazione si incoronava in cima a tutte le dimensioni di modello e dati.
  • La dimensione dei dati relativa alla dimensione del modello è importante; per dati e modello di piccole dimensioni, può prevalere il sovraccarico di trasferimento dati (ad es. da CPU a GPU). Allo stesso modo, i piccoli processori overhead possono funzionare più lentamente su dati di grandi dimensioni in base al tempo di conversione dei dati (vedere convert_to_tensorin "PROFILER")
  • La velocità differisce per i diversi circuiti dei circuiti di treno e di input dei diversi gestori di dati per la gestione delle risorse.

BENCHMARKS : la carne macinata. - Documento Word - Foglio di calcolo Excel


Terminologia :

  • % -less numeri sono tutti i secondi
  • % calcolato come (1 - longer_time / shorter_time)*100; motivazione: siamo interessati a quale fattore uno è più veloce dell'altro; shorter / longerè in realtà una relazione non lineare, non utile per il confronto diretto
  • Determinazione del segno%:
    • TF2 vs TF1: +se TF2 è più veloce
    • GvE (Graph vs. Eager): +se Graph è più veloce
  • TF2 = TensorFlow 2.0.0 + Keras 2.3.1; TF1 = TensorFlow 1.14.0 + Keras 2.2.5

PROFILER :


PROFILER - Spiegazione : Spyder 3.3.6 IDE profiler.

  • Alcune funzioni si ripetono nei nidi di altre; quindi, è difficile rintracciare l'esatta separazione tra le funzioni di "elaborazione dei dati" e di "addestramento", quindi ci saranno alcune sovrapposizioni, come si pronuncia nell'ultimo risultato.

  • % delle cifre calcolate runtime wrt meno tempo di costruzione

  • Tempo di costruzione calcolato sommando tutti i runtime (unici) chiamati 1 o 2 volte
  • Tempo del treno calcolato sommando tutti i runtime (unici) che sono stati chiamati lo stesso numero di volte del numero di iterazioni e alcuni dei runtime dei loro nidi
  • Le funzioni sono profilate in base al loro nome originale , sfortunatamente (cioè _func = funcverrà profilato come func), che si mescola nel tempo di costruzione - da qui la necessità di escluderlo

TEST DELL'AMBIENTE :

  • Codice eseguito in basso con attività in background minime in esecuzione
  • La GPU è stata "riscaldata" con alcune iterazioni prima di temporizzare le iterazioni, come suggerito in questo post
  • CUDA 10.0.130, cuDNN 7.6.0, TensorFlow 1.14.0 e TensorFlow 2.0.0 costruiti dalla sorgente, più Anaconda
  • Python 3.7.4, Spyder 3.3.6 IDE
  • GTX 1070, Windows 10, 24 GB di RAM DDR4 da 2,4 MHz, CPU i7-7700HQ da 2,8 GHz

METODOLOGIA :

  • Benchmark 'piccolo', 'medio', e 'grande' modello e dimensioni dei dati
  • Correzione del numero di parametri per ciascuna dimensione del modello, indipendentemente dalla dimensione dei dati di input
  • Il modello "più grande" ha più parametri e livelli
  • I dati "più grandi" hanno una sequenza più lunga, ma lo stesso batch_sizeenum_channels
  • I modelli utilizzano solo Conv1D, Densestrati 'apprendibili'; RNN evitati per implementazione della versione TF. differenze
  • Funzionava sempre con un treno al di fuori del ciclo di benchmarking, per omettere la costruzione di grafici di ottimizzatori e modelli
  • Non utilizzare dati sparsi (ad es. layers.Embedding()) O target sparsi (ad esSparseCategoricalCrossEntropy()

LIMITAZIONI : una risposta "completa" spiegherebbe ogni possibile ciclo del treno e iteratore, ma sicuramente oltre la mia capacità di tempo, busta paga inesistente o necessità generale. I risultati sono buoni quanto la metodologia: interpretare con una mente aperta.


CODICE :

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)

Non sono sicuro che il tuo codice sia corretto. Penso che i tuoi modelli funzionino sempre in modalità grafica poiché chiami model.compilesenza run_eagerly=Trueargomento. Se in modalità Eger, è possibile eseguire parte del codice in modalità Graph utilizzando tf.function. Pertanto, penso che l'implementazione predefinita di compilesia creare un grafico computazionale invece di eseguirlo con entusiasmo per motivi di prestazioni. Nota anche che se il tuo modello è convoluzionale, non vedrai la velocità in modalità grafica poiché l'interazione con Python è minima. Se fai molte operazioni matematiche di quanto possa fare la differenza (anche nell'utilizzo della memoria).
user2781994

@OverLordGoldDragon ma in TF 2, la modalità desiderosa è predefinita ma model.compilesenza run_eagerly=Truegarantisce la modalità grafica o no?
user2781994

@OverLordGoldDragon Concordo sul fatto che non tutti i metodi importati funzionano in modalità grafica, ma penso che uno model.compileo entrambi model.fitdebbano garantire che la formazione venga eseguita internamente in modalità grafica.
user2781994

@OverLordGoldDragon TRUE - "tf.keras.Model.compile prende tre argomenti importanti: ... Inoltre, per essere sicuro che il modello si alleni e valuti con entusiasmo, puoi assicurarti di passare run_eagerly=Truecome parametro da compilare." (fonte tensorflow.org/guide/keras/overview ) Pertanto, se non si supera il run_eagerly=Truemodello, POSSO funzionare in modalità grafica. Non sono sicuro di quale sia il fattore decisivo, ma perché non dovrebbe funzionare in modalità grafica se è più efficiente di desideroso.
user2781994

Vuoi più prove? :) "Per impostazione predefinita, tenteremo di compilare il modello in un grafico statico per offrire le migliori prestazioni di esecuzione." ( github.com/tensorflow/tensorflow/blob/r2.0/tensorflow/python/… )
user2781994
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.