AGGIORNAMENTO - 15/01/2020 : la migliore prassi corrente per piccoli lotti dovrebbe essere per alimentare ingressi al modello direttamente - cioè preds = model(x)
, se strati comportano in modo diverso a treno / inferenza, model(x, training=False)
. Per l'ultimo commit, questo è ora documentato .
Non li ho confrontati con questi, ma per il discussione su Git vale anche la pena provare predict_on_batch()
, specialmente con miglioramenti in TF 2.1.
ULTIMA CULPRIT : self._experimental_run_tf_function = True
. È sperimentale . Ma in realtà non è male.
A tutti gli sviluppatori di TensorFlow che leggono: ripulisci il tuo codice . È un casino. E viola importanti pratiche di codifica, come una funzione fa una cosa ; _process_inputs
fa molto più di "input di processo", lo stesso per _standardize_user_data
. "Io non sono pagato abbastanza" - ma faccio pagare, nel tempo in più speso comprendere la propria roba, e gli utenti che riempiono la tua pagina problemi con gli insetti più facile risolto con un codice più chiaro.
SOMMARIO : è solo un po ' più lento con compile()
.
compile()
imposta un flag interno che assegna una diversa funzione di previsione a predict
. Questa funzione costruisce un nuovo grafico ad ogni chiamata, rallentandolo rispetto al non compilato. Tuttavia, la differenza è pronunciata solo quando tempo del treno è molto più breve del tempo di elaborazione dei dati . Se aumentiamo le dimensioni del modello ad almeno medie, i due diventano uguali. Vedi il codice in fondo.
Questo leggero aumento del tempo di elaborazione dei dati è più che compensato dalla capacità del grafico amplificato. Poiché è più efficiente mantenere solo un grafico del modello in giro, quello di pre-compilazione viene scartato.Tuttavia : se il tuo modello è piccolo rispetto ai dati, stai meglio senza l' compile()
inferenza del modello. Vedi la mia altra risposta per una soluzione alternativa.
COSA DOVREI FARE?
Confronta le prestazioni del modello compilate e non compilate come ho nel codice in fondo.
- La compilazione è più veloce : esegui
predict
compilazione su un modello compilato.
- La compilazione è più lenta : viene eseguita
predict
su un modello non compilato.
Sì, entrambi sono possibili e dipenderà dalla (1) dimensione dei dati; (2) dimensioni del modello; (3) hardware. Il codice in basso mostra in realtà un modello compilato più veloce, ma 10 iterazioni sono un piccolo esempio. Vedi "soluzioni alternative" nella mia altra risposta per il "come fare".
DETTAGLI :
Il debug ha richiesto del tempo, ma è stato divertente. Di seguito descrivo i principali colpevoli che ho scoperto, cito alcuni documenti pertinenti e mostrerò i risultati del profiler che hanno portato al collo di bottiglia finale.
( FLAG == self.experimental_run_tf_function
, per brevità)
Model
di default crea un'istanza con FLAG=False
. compile()
lo imposta suTrue
.
predict()
comporta l'acquisizione della funzione di previsione, func = self._select_training_loop(x)
- Senza alcun kwarg speciale passato a
predict
e compile
, tutte le altre bandiere sono tali che:
- (A)
FLAG==True
->func = training_v2.Loop()
- (B)
FLAG==False
->func = training_arrays.ArrayLikeTrainingLoop()
- Dal docstring del codice sorgente , (A) è fortemente dipendente dal grafico, usa più strategia di distribuzione e le operazioni sono inclini a creare e distruggere elementi grafici, che "possono" (influire) sulle prestazioni.
Vero colpevole : _process_inputs()
rappresenta l' 81% del tempo di esecuzione . Il suo componente principale? _create_graph_function()
, 72% di runtime . Questo metodo non esiste nemmeno per (B) . L'uso di un modello di medie dimensioni, tuttavia, _process_inputs
comprende meno dell'1% del tempo di esecuzione . Codice in basso e seguiranno i risultati della profilazione.
Responsabili del trattamento :
(A) :, <class 'tensorflow.python.keras.engine.data_adapter.TensorLikeDataAdapter'>
utilizzato in _process_inputs()
. Codice sorgente pertinente
(B) :, numpy.ndarray
restituito da convert_eager_tensors_to_numpy
. Codice sorgente pertinente , e qui
FUNZIONE DI ESECUZIONE DEL MODELLO (ad es. Previsione)
(A) : funzione di distribuzione , e qui
(B) : funzione di distribuzione (diversa) , e qui
PROFILER : risultati per il codice nell'altra mia risposta, "modello piccolo", e in questa risposta, "modello medio":
Modello minuscolo : 1000 iterazioni,compile()
Modello minuscolo : 1000 iterazioni, n compile()
Modello medio : 10 iterazioni
DOCUMENTAZIONE (indirettamente) sugli effetti di compile()
: fonte
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 , ad esempio chiamando g(2)
e g(3)
genererà due nuovi grafici
function
crea un'istanza di un grafico separato per ogni set univoco di forme di input e tipi di dati . Ad esempio, il seguente frammento di codice determinerà la traccia di tre grafici distinti, poiché ciascun input ha una forma diversa
Potrebbe essere necessario mappare un singolo oggetto tf.function su più grafici di calcolo sotto il cofano. Questo dovrebbe essere visibile solo in quanto le prestazioni (i grafici di tracciamento hanno un costo di calcolo e di memoria diverso da zero ) ma non dovrebbero influire sulla correttezza del programma
ESEMPIO CONTRO :
from tensorflow.keras.layers import Input, Dense, LSTM, Bidirectional, Conv1D
from tensorflow.keras.layers import Flatten, Dropout
from tensorflow.keras.models import Model
import numpy as np
from time import time
def timeit(func, arg, iterations):
t0 = time()
for _ in range(iterations):
func(arg)
print("%.4f sec" % (time() - t0))
batch_size = 32
batch_shape = (batch_size, 400, 16)
ipt = Input(batch_shape=batch_shape)
x = Bidirectional(LSTM(512, activation='relu', return_sequences=True))(ipt)
x = LSTM(512, activation='relu', return_sequences=True)(ipt)
x = Conv1D(128, 400, 1, 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)
X = np.random.randn(*batch_shape)
timeit(model.predict, X, 10)
model.compile('adam', loss='binary_crossentropy')
timeit(model.predict, X, 10)
Uscite :
34.8542 sec
34.7435 sec