Keras inkonsistente Vorhersagezeit

17

Ich versuchte eine Schätzung der Vorhersagezeit meines Keras-Modells zu erhalten und erkannte etwas Seltsames. Abgesehen davon, dass das Modell normalerweise ziemlich schnell ist, braucht es von Zeit zu Zeit ziemlich lange, um eine Vorhersage zu treffen. Und nicht nur das, diese Zeiten verlängern sich auch, je länger das Modell läuft. Ich habe ein minimales Arbeitsbeispiel hinzugefügt, um den Fehler zu reproduzieren.

import time
import numpy as np
from sklearn.datasets import make_classification
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten

# Make a dummy classification problem
X, y = make_classification()

# Make a dummy model
model = Sequential()
model.add(Dense(10, activation='relu',name='input',input_shape=(X.shape[1],)))
model.add(Dense(2, activation='softmax',name='predictions'))
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])

model.fit(X, y, verbose=0, batch_size=20, epochs=100)

for i in range(1000):
    # Pick a random sample
    sample = np.expand_dims(X[np.random.randint(99), :], axis=0)
    # Record the prediction time 10x and then take the average
    start = time.time()
    for j in range(10):
        y_pred = model.predict_classes(sample)
    end = time.time()
    print('%d, %0.7f' % (i, (end-start)/10))

Die Zeit hängt nicht von der Probe ab (sie wird zufällig ausgewählt). Wenn der Test wiederholt wird, sind die Indizes in der for-Schleife, in denen die Vorhersage länger dauert, wieder (fast) gleich.

Geben Sie hier die Bildbeschreibung ein

Ich benutze:

tensorflow 2.0.0
python 3.7.4

Für meine Bewerbung muss ich die Ausführung in einer bestimmten Zeit garantieren. Dies ist jedoch angesichts dieses Verhaltens unmöglich. Was läuft falsch? Ist es ein Fehler in Keras oder ein Fehler im Tensorflow-Backend?

EDIT: predict_on_batchzeigt das gleiche Verhalten, jedoch spärlicher: Geben Sie hier die Bildbeschreibung ein

y_pred = model(sample, training=False).numpy() zeigt auch einige starke Ausreißer, die jedoch nicht zunehmen. Geben Sie hier die Bildbeschreibung ein

EDIT 2: Ich habe auf die neueste Version von Tensorflow 1 (1.15) heruntergestuft. Das Problem besteht nicht nur nicht mehr, auch die "normale" Vorhersagezeit hat sich deutlich verbessert! Ich sehe die beiden Spitzen nicht als problematisch an, da sie bei Wiederholung des Tests nicht aufgetreten sind (zumindest nicht bei denselben Indizes und linear ansteigend) und prozentual nicht so groß sind wie im ersten Diagramm. Geben Sie hier die Bildbeschreibung ein

Wir können daher den Schluss ziehen, dass dies ein dem Tensorflow 2.0 innewohnendes Problem zu sein scheint, das in anderen Situationen ein ähnliches Verhalten zeigt, wie es @OverLordGoldDragon erwähnt.

ga97dil
quelle
Dieses Verhalten klingt vorhersehbar ... der Anstieg ist irgendwie linear. Wenn Sie dieses Verhalten in Ihre Zeitberechnung einbeziehen, geht es dann nicht? --- Ich weiß nicht, was dort passiert ... aber was passiert, wenn Sie es predict_on_batchstattdessen versuchen ?
Daniel Möller
Ein weiterer Versuch, was passiert mit y_pred = model(sample).numpy()und mit y_pred = model(sample, training=False).numpy()?
Daniel Möller
Ich habe meine Erkenntnisse hinzugefügt. Die Numpy-Versionen scheinen das Verhalten nicht zu zeigen.
ga97dil
Ist predict_classesaber immer noch der schnellste .... es scheint. Was ist mit nur predict?
Daniel Möller
1
Ich nehme an, dies könnte eine Art Speicherbereinigung sein ...
Daniel Möller

Antworten:

10

TF2 weist in einigen Fällen, auf die ich gestoßen bin, im Allgemeinen eine schlechte und fehlerhafte Speicherverwaltung auf - kurze Beschreibung hier und hier . Insbesondere bei der Vorhersage erfolgt die leistungsfähigste Fütterungsmethode model(x)direkt über - siehe hier und die damit verbundenen Diskussionen.

In Kürze: model(x)wirkt über seine ihre __call__Methode (die es erbt base_layer.Layer), wohingegen predict(), predict_classes()usw. beinhaltet eine spezielle Schleifenfunktion über _select_training_loop(); Jedes verwendet unterschiedliche Datenvor- und -nachbearbeitungsmethoden, die für unterschiedliche Anwendungsfälle geeignet sind, und wurde model(x)in 2.1 speziell entwickelt, um die schnellste Leistung bei kleinen Modellen / kleinen Chargen (und möglicherweise bei jeder Größe) zu erzielen (und in 2.0 immer noch die schnellste).

Zitieren eines TensorFlow- Entwicklers aus verknüpften Diskussionen:

Sie können die Ausgabe mithilfe eines Modellaufrufs vorhersagen, nicht mithilfe einer Modellvorhersage. Das heißt, ein Aufruf model(x)würde dies viel schneller machen, da es keinen Teil "Konvertierung in Dataset" gibt und auch einen zwischengespeicherten direkt aufruft tf.function.

Hinweis : Dies sollte in 2.1 und insbesondere in 2.2 weniger problematisch sein - testen Sie jedoch jede Methode trotzdem. Mir ist auch klar, dass dies Ihre Frage zu den Zeitspitzen nicht direkt beantwortet. Ich vermute, dass es mit Eager-Caching-Mechanismen zusammenhängt, aber der sicherste Weg, dies festzustellen, ist via TF Profiler, das in 2.1 unterbrochen ist .


Update : in Bezug auf zunehmende Spitzen, mögliche GPU-Drosselung; Sie haben ~ 1000 Iter gemacht, versuchen Sie stattdessen 10.000 - schließlich sollte die Zunahme aufhören. Wie Sie in Ihren Kommentaren festgestellt haben, tritt dies bei nicht auf model(x). Dies ist sinnvoll, da ein GPU-Schritt weniger erforderlich ist ("Konvertierung in einen Datensatz").

Update2 : Sie könnten die Entwickler hier darüber nerven , wenn Sie auf dieses Problem stoßen . Ich singe hauptsächlich dort

OverLordGoldDragon
quelle
Dies ist eine gute Antwort darauf, warum eine Methode langsamer ist, erklärt jedoch nicht die zunehmende Laufzeit über mehrere Läufe.
LLSv2.0
1
@ LLSv2.0 Ich bin mir nicht ganz sicher, aber aktualisierte Antwort - Ich warte immer noch auf eine Antwort von Entwicklern, als ich dieses Problem hier
angesprochen
1
@ ga97dil Ja, dann habe ich keine Erklärungen mehr - fragen Sie Github, obwohl Sie möglicherweise mit langen Antwortzeiten konfrontiert sind.
OverLordGoldDragon
1
@ ga97dil In der Tat kann TF1 viel schneller sein als TF2 - obwohl es sich lohnt, TF 2.1 für kleine Modelle und Datensätze auszuprobieren, da es das schnellste Training in meinen Benchmarks ist (keine Vorhersage gemacht). Noch wichtiger ist, wenn Sie jemals TF2 verwenden, empfehle ich dringend, die Reproduzierbarkeit in Graph vs. Eager zu testen . Ergebnisse können unterscheiden sich sehr in TF 2.1.
OverLordGoldDragon
1
Ich habe deinen Beitrag zum Git-Thread und meinen Beitrag TF2 vs. TF1 hinzugefügt . Vielen Dank, dass Sie mich darüber informiert haben, dass das Problem in TF 1 verschwindet.
OverLordGoldDragon
2

Obwohl ich die Inkonsistenzen in der Ausführungszeit nicht erklären kann, kann ich empfehlen, dass Sie versuchen, Ihr Modell in TensorFlow Lite zu konvertieren, um Vorhersagen für einzelne Datensätze oder kleine Stapel zu beschleunigen.

Ich habe einen Benchmark für dieses Modell durchgeführt:

model = tf.keras.models.Sequential([
    tf.keras.layers.Dense(384, activation='elu', input_shape=(256,)),
    tf.keras.layers.Dense(384, activation='elu'),
    tf.keras.layers.Dense(256, activation='elu'),
    tf.keras.layers.Dense(128, activation='elu'),
    tf.keras.layers.Dense(32, activation='tanh')
])

Die Vorhersagezeiten für einzelne Datensätze waren:

  1. model.predict(input): 18 ms
  2. model(input): 1,3 ms
  3. Modell konvertiert zu TensorFlow Lite: 43us

Die Zeit zum Umrüsten des Modells betrug 2 Sekunden.

Die folgende Klasse zeigt, wie das Modell konvertiert und verwendet wird, und bietet eine predictMethode wie das Keras-Modell. Beachten Sie, dass es für die Verwendung mit Modellen geändert werden muss, die nicht nur einen einzelnen 1-D-Eingang und einen einzelnen 1-D-Ausgang haben.

class LiteModel:

    @classmethod
    def from_file(cls, model_path):
        return LiteModel(tf.lite.Interpreter(model_path=model_path))

    @classmethod
    def from_keras_model(cls, kmodel):
        converter = tf.lite.TFLiteConverter.from_keras_model(kmodel)
        tflite_model = converter.convert()
        return LiteModel(tf.lite.Interpreter(model_content=tflite_model))

    def __init__(self, interpreter):
        self.interpreter = interpreter
        self.interpreter.allocate_tensors()
        input_det = self.interpreter.get_input_details()[0]
        output_det = self.interpreter.get_output_details()[0]
        self.input_index = input_det["index"]
        self.output_index = output_det["index"]
        self.input_shape = input_det["shape"]
        self.output_shape = output_det["shape"]
        self.input_dtype = input_det["dtype"]
        self.output_dtype = output_det["dtype"]

    def predict(self, inp):
        inp = inp.astype(self.input_dtype)
        count = inp.shape[0]
        out = np.zeros((count, self.output_shape[1]), dtype=self.output_dtype)
        for i in range(count):
            self.interpreter.set_tensor(self.input_index, inp[i:i+1])
            self.interpreter.invoke()
            out[i] = self.interpreter.get_tensor(self.output_index)[0]
        return out

    def predict_single(self, inp):
        """ Like predict(), but only for a single record. The input data can be a Python list. """
        inp = np.array([inp], dtype=self.input_dtype)
        self.interpreter.set_tensor(self.input_index, inp)
        self.interpreter.invoke()
        out = self.interpreter.get_tensor(self.output_index)
        return out[0]

Den vollständigen Benchmark-Code und ein Diagramm finden Sie hier: https://medium.com/@micwurm/using-tensorflow-lite-to-speed-up-predictions-a3954886eb98

Michael
quelle
Cool, habe es noch nie probiert, aber vielleicht wäre es einen Versuch wert. Danke für den Tipp!
ga97dil