Quantisierung nach dem Training

Die Quantisierung nach dem Training ist eine Konvertierungstechnik, die die Modellgröße reduzieren und gleichzeitig die Latenzzeit von CPU und Hardwarebeschleuniger bei geringer Verschlechterung der Modellgenauigkeit verbessern kann. Sie können einen bereits ausgebildeten Schwimmer TensorFlow Modell quantisiert , wenn Sie es TensorFlow Lite - Format mit dem konvertieren TensorFlow Lite Converter .

Optimierungsmethoden

Es stehen mehrere Quantisierungsoptionen nach dem Training zur Auswahl. Hier ist eine zusammenfassende Tabelle der Auswahlmöglichkeiten und der Vorteile, die sie bieten:

Technik Leistungen Hardware
Quantisierung des Dynamikbereichs 4x kleiner, 2x-3x Beschleunigung Zentralprozessor
Vollständige ganzzahlige Quantisierung 4x kleiner, 3x+ Beschleunigung CPU, Edge-TPU, Mikrocontroller
Float16-Quantisierung 2x kleiner, GPU-Beschleunigung CPU, GPU

Der folgende Entscheidungsbaum kann Ihnen dabei helfen zu bestimmen, welche Quantisierungsmethode nach dem Training für Ihren Anwendungsfall am besten geeignet ist:

Optimierungsmöglichkeiten nach dem Training

Quantisierung des Dynamikbereichs

Die einfachste Form der Quantisierung nach dem Training quantisiert statisch nur die Gewichte von Gleitkomma bis Ganzzahl, was eine Genauigkeit von 8 Bit hat:

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_quant_model = converter.convert()

Bei der Inferenz werden Gewichtungen von 8-Bit-Präzision in Gleitkomma umgewandelt und mit Gleitkomma-Kernels berechnet. Diese Konvertierung wird einmal durchgeführt und zwischengespeichert, um die Latenz zu reduzieren.

Um die Latenz weiter zu verbessern, quantisieren "Dynamikbereichs"-Operatoren dynamisch Aktivierungen basierend auf ihrem Bereich auf 8-Bit und führen Berechnungen mit 8-Bit-Gewichtungen und Aktivierungen durch. Diese Optimierung bietet Latenzen nahe einer vollständigen Festkomma-Inferenz. Die Ausgaben werden jedoch immer noch unter Verwendung von Gleitkommazahlen gespeichert, sodass die Beschleunigung bei Operationen mit dynamischem Bereich geringer ist als eine vollständige Festkommaberechnung.

Vollständige ganzzahlige Quantisierung

Sie können weitere Latenzverbesserungen, Reduzierungen der Spitzenspeicherauslastung und Kompatibilität mit Nur-Integer-Hardwaregeräten oder -Beschleunigern erzielen, indem Sie sicherstellen, dass die gesamte Modellmathematik ganzzahlig quantisiert ist.

Für eine ganzzahlige Quantisierung müssen Sie den Bereich, dh (min, max) aller Gleitkomma-Tensoren im Modell kalibrieren oder schätzen. Im Gegensatz zu konstanten Tensoren wie Gewichtungen und Bias können variable Tensoren wie Modelleingaben, Aktivierungen (Ausgaben von Zwischenschichten) und Modellausgaben nicht kalibriert werden, es sei denn, wir führen einige Inferenzzyklen durch. Daher benötigt der Konverter einen repräsentativen Datensatz, um sie zu kalibrieren. Dieser Datensatz kann eine kleine Teilmenge (etwa 100-500 Stichproben) der Trainings- oder Validierungsdaten sein. Siehe die representative_dataset() Funktion unten.

Von TensorFlow Version 2.7 können Sie den repräsentativen Datensatzes durch eine angeben Signatur wie das folgende Beispiel:

def representative_dataset():
  for data in dataset:
    yield {
      "image": data.image,
      "bias": data.bias,
    }

Sie können den repräsentativen Datensatz generieren, indem Sie eine Eingabetensorliste bereitstellen:

def representative_dataset():
  for data in tf.data.Dataset.from_tensor_slices((images)).batch(1).take(100):
    yield [tf.dtypes.cast(data, tf.float32)]

Seit der Version TensorFlow 2.7 empfehlen wir die Verwendung des signaturbasierten Ansatzes gegenüber dem eingabetensorlistenbasierten Ansatz, da die Reihenfolge der Eingabetensoren leicht umgedreht werden kann.

Zu Testzwecken können Sie einen Dummy-Datensatz wie folgt verwenden:

def representative_dataset():
    for _ in range(100):
      data = np.random.rand(1, 244, 244, 3)
      yield [data.astype(np.float32)]
 

Integer mit Float-Fallback (mit Standard-Float-Ein-/Ausgabe)

Um ein Modell vollständig ganzzahlig zu quantisieren, aber Float-Operatoren zu verwenden, wenn sie keine ganzzahlige Implementierung haben (um sicherzustellen, dass die Konvertierung reibungslos erfolgt), führen Sie die folgenden Schritte aus:

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset
tflite_quant_model = converter.convert()

Nur ganze Zahlen

Erstellen integer nur Modelle ist ein typischer Anwendungsfall für TensorFlow Lite für Mikrocontroller und Coral Edge - TPUs .

Um die Kompatibilität mit Nur-Integer-Geräten (wie 8-Bit-Mikrocontrollern) und Beschleunigern (wie der Coral Edge TPU) zu gewährleisten, können Sie außerdem die vollständige Ganzzahl-Quantisierung für alle Operationen einschließlich der Eingabe und Ausgabe erzwingen, indem Sie die folgenden Schritte ausführen:

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
converter.inference_input_type = tf.int8  # or tf.uint8
converter.inference_output_type = tf.int8  # or tf.uint8
tflite_quant_model = converter.convert()

Float16-Quantisierung

Sie können die Größe eines Gleitkommamodells reduzieren, indem Sie die Gewichte auf float16 quantisieren, den IEEE-Standard für 16-Bit-Gleitkommazahlen. Führen Sie die folgenden Schritte aus, um die float16-Quantisierung von Gewichtungen zu aktivieren:

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_types = [tf.float16]
tflite_quant_model = converter.convert()

Die Vorteile der float16-Quantisierung sind wie folgt:

  • Es reduziert die Modellgröße um bis zur Hälfte (da alle Gewichte die Hälfte ihrer Originalgröße erreichen).
  • Es verursacht einen minimalen Genauigkeitsverlust.
  • Es unterstützt einige Delegaten (zB den GPU-Delegaten), die direkt mit float16-Daten arbeiten können, was zu einer schnelleren Ausführung als float32-Berechnungen führt.

Die Nachteile der float16-Quantisierung sind wie folgt:

  • Es reduziert die Latenz nicht so sehr wie eine Quantisierung auf Festkomma-Mathematik.
  • Standardmäßig "dequantisiert" ein float16-quantisiertes Modell die Gewichtungswerte auf float32, wenn es auf der CPU ausgeführt wird. (Beachten Sie, dass der GPU-Delegat diese Dequantisierung nicht durchführt, da er mit float16-Daten arbeiten kann.)

Nur Integer: 16-Bit-Aktivierungen mit 8-Bit-Gewichtungen (experimentell)

Dies ist ein experimentelles Quantisierungsschema. Es ähnelt dem "Nur Ganzzahl"-Schema, aber Aktivierungen werden basierend auf ihrem Bereich auf 16-Bit quantisiert, Gewichtungen werden in 8-Bit-Ganzzahlen quantisiert und Bias wird in 64-Bit-Ganzzahlen quantisiert. Dies wird im weiteren als 16x8-Quantisierung bezeichnet.

Der Hauptvorteil dieser Quantisierung besteht darin, dass sie die Genauigkeit erheblich verbessern kann, aber die Modellgröße nur geringfügig erhöht.

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.representative_dataset = representative_dataset
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_ops = [tf.lite.OpsSet.EXPERIMENTAL_TFLITE_BUILTINS_ACTIVATIONS_INT16_WEIGHTS_INT8]
tflite_quant_model = converter.convert()

Wenn die 16x8-Quantisierung für einige Operatoren im Modell nicht unterstützt wird, kann das Modell immer noch quantisiert werden, aber nicht unterstützte Operatoren bleiben in Float. Die folgende Option sollte der target_spec hinzugefügt werden, um dies zu ermöglichen.

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.representative_dataset = representative_dataset
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_ops = [tf.lite.OpsSet.EXPERIMENTAL_TFLITE_BUILTINS_ACTIVATIONS_INT16_WEIGHTS_INT8,
tf.lite.OpsSet.TFLITE_BUILTINS]
tflite_quant_model = converter.convert()

Beispiele für Anwendungsfälle, in denen Genauigkeitsverbesserungen durch dieses Quantisierungsschema bereitgestellt werden, umfassen: * Superauflösung, * Audiosignalverarbeitung wie Rauschunterdrückung und Beamforming, * Bildentrauschung, * HDR-Rekonstruktion aus einem einzelnen Bild.

Der Nachteil dieser Quantisierung ist:

  • Derzeit ist die Inferenz aufgrund des Fehlens einer optimierten Kernel-Implementierung merklich langsamer als 8-Bit-Full-Integer.
  • Derzeit ist es mit den vorhandenen hardwarebeschleunigten TFLite-Delegaten nicht kompatibel.

Ein Tutorial für diese Quantisierung - Modus kann gefunden werden hier .

Modellgenauigkeit

Da Gewichte nach dem Training quantisiert werden, kann es insbesondere bei kleineren Netzwerken zu einem Genauigkeitsverlust kommen. Vortrainiert voll quantisiert Modelle sind für bestimmte Netzwerke in der mitgelieferten TensorFlow Lite Modell - Repository . Es ist wichtig, die Genauigkeit des quantisierten Modells zu überprüfen, um sicherzustellen, dass jede Verschlechterung der Genauigkeit innerhalb akzeptabler Grenzen liegt. Es gibt Tools zur Bewertung TensorFlow Lite Modellgenauigkeit .

Alternativ kann , wenn die Genauigkeit Abfall zu hoch ist, sollten Sie mit Quantisierung bewusst Ausbildung . Dies erfordert jedoch Modifikationen während des Modelltrainings, um gefälschte Quantisierungsknoten hinzuzufügen, während die Quantisierungstechniken nach dem Training auf dieser Seite ein vorhandenes vortrainiertes Modell verwenden.

Darstellung für quantisierte Tensoren

Die 8-Bit-Quantisierung nähert sich Gleitkommawerten unter Verwendung der folgenden Formel an.

$$real\_value = (int8\_value - zero\_point) \times scale$$

Die Darstellung besteht aus zwei Hauptteilen:

  • Gewichte pro Achse (auch bekannt als pro Kanal) oder pro Tensor, dargestellt durch int8 Zweierkomplementwerte im Bereich [-127, 127] mit Nullpunkt gleich 0.

  • Aktivierungen/Eingaben pro Tensor, dargestellt durch int8-Zweierkomplementwerte im Bereich [-128, 127], mit einem Nullpunkt im Bereich [-128, 127].

Für eine Detailansicht unserer Quantisierungsschema finden Sie in unserer Quantisierung spec . Hardwareanbietern, die sich an die Delegate-Schnittstelle von TensorFlow Lite anschließen möchten, wird empfohlen, das dort beschriebene Quantisierungsschema zu implementieren.