TensorFlow Lite-Inferenz

Der Begriff Inferenz bezieht sich auf die Ausführung eines TensorFlow Lite-Modells auf dem Gerät, um anhand von Eingabedaten Vorhersagen zu treffen. Um eine Inferenz mit einem TensorFlow Lite-Modell durchzuführen, müssen Sie es über einen Interpreter ausführen. Der TensorFlow Lite-Interpreter ist schlank und schnell. Der Interpreter verwendet eine statische Grafiksortierung und einen benutzerdefinierten (weniger dynamischen) Speicherzuordnungsoperator, um eine minimale Last, Initialisierung und Ausführungslatenz zu gewährleisten.

Auf dieser Seite wird beschrieben, wie Sie auf den TensorFlow Lite-Interpreter zugreifen und eine Inferenz mit C++, Java und Python ausführen. Außerdem finden Sie dort Links zu anderen Ressourcen für jede unterstützte Plattform.

Wichtige Konzepte

Die TensorFlow Lite-Inferenz umfasst in der Regel die folgenden Schritte:

  1. Modell laden

    Sie müssen das Modell .tflite in den Arbeitsspeicher laden, der die Ausführungsgrafik des Modells enthält.

  2. Daten transformieren

    Die Roheingabedaten für das Modell entsprechen in der Regel nicht dem vom Modell erwarteten Eingabedatenformat. Beispielsweise müssen Sie möglicherweise die Größe eines Bildes ändern oder das Bildformat ändern, damit es mit dem Modell kompatibel ist.

  3. Inferenz ausführen

    In diesem Schritt wird die TensorFlow Lite API zum Ausführen des Modells verwendet. Sie umfasst einige Schritte wie das Erstellen des Interpreters und das Zuweisen von Tensoren, wie in den folgenden Abschnitten beschrieben.

  4. Ausgabe interpretieren

    Wenn Sie Ergebnisse von der Modellinferenz erhalten, müssen Sie die Tensoren auf sinnvolle Weise interpretieren, die für Ihre Anwendung nützlich ist.

    Beispielsweise kann ein Modell nur eine Liste von Wahrscheinlichkeiten zurückgeben. Sie entscheiden, ob Sie die Wahrscheinlichkeiten relevanten Kategorien zuordnen und sie dem Endnutzer präsentieren möchten.

Unterstützte Plattformen

TensorFlow-Inferenz-APIs werden für die gängigsten mobilen/eingebetteten Plattformen wie Android, iOS und Linux in verschiedenen Programmiersprachen bereitgestellt.

In den meisten Fällen spiegelt das API-Design eine Bevorzugung von Leistung gegenüber Nutzerfreundlichkeit wider. TensorFlow Lite ist für schnelle Inferenzen auf kleinen Geräten ausgelegt. Daher sollte es keine Überraschung sein, dass die APIs versuchen, unnötige Kopien der Einfachheit halber zu vermeiden. Auch die Konsistenz mit TensorFlow APIs war kein explizites Ziel und eine gewisse Abweichung zwischen den Sprachen ist zu erwarten.

Mit der TensorFlow Lite API können Sie in allen Bibliotheken Modelle laden, Eingaben eingeben und Inferenzausgaben abrufen.

Android-Plattform

Unter Android können TensorFlow Lite-Inferenzen entweder mit Java- oder C++ APIs ausgeführt werden. Die Java APIs sind praktisch und können direkt in Ihren Android-Aktivitätsklassen verwendet werden. Die C++ APIs bieten mehr Flexibilität und Geschwindigkeit, erfordern jedoch möglicherweise das Schreiben von JNI-Wrappern, um Daten zwischen Java- und C++-Ebenen zu verschieben.

Weitere Informationen zu C++ und Java finden Sie weiter unten. Eine Anleitung und Beispielcode finden Sie in der Android-Kurzanleitung.

Android-Wrapper-Codegenerator in TensorFlow Lite

Bei einem mit Metadaten erweiterten TensorFlow Lite-Modell können Entwickler den Android-Wrapper-Codegenerator von TensorFlow Lite verwenden, um plattformspezifischen Wrapper-Code zu erstellen. Dank des Wrapper-Codes ist eine direkte Interaktion mit ByteBuffer unter Android nicht mehr erforderlich. Stattdessen können Entwickler mit dem TensorFlow Lite-Modell mit typisierten Objekten wie Bitmap und Rect interagieren. Weitere Informationen finden Sie unter TensorFlow Lite Android-Wrapper-Codegenerator.

iOS-Plattform

Unter iOS ist TensorFlow Lite mit nativen iOS-Bibliotheken verfügbar, die in Swift und Objective-C geschrieben wurden. Sie können die C API auch direkt in Objective-C-Codes verwenden.

Ausführliche Informationen zur Verwendung von Swift, Objective-C und der C API finden Sie unten. Eine Anleitung und Beispielcode finden Sie in der Kurzanleitung für iOS.

Linux-Plattform

Auf Linux-Plattformen (einschließlich Raspberry Pi) können Sie Inferenzen mit TensorFlow Lite APIs ausführen, die in C++ und Python verfügbar sind, wie in den folgenden Abschnitten gezeigt.

Modell ausführen

Zum Ausführen eines TensorFlow Lite-Modells sind einige einfache Schritte erforderlich:

  1. Laden Sie das Modell in den Arbeitsspeicher.
  2. Erstellen Sie ein Interpreter auf der Grundlage eines vorhandenen Modells.
  3. Legt die Werte für die Eingabetensoren fest. (Optional können Sie die Größe der Eingabetensoren ändern, wenn die vordefinierten Größen nicht erwünscht sind.)
  4. Rufen Sie Inferenz auf.
  5. Ausgabe-Tensorwerte lesen.

In den folgenden Abschnitten wird beschrieben, wie diese Schritte in den einzelnen Sprachen ausgeführt werden können.

Modell in Java laden und ausführen

Plattform: Android

Die Java API zum Ausführen einer Inferenz mit TensorFlow Lite ist in erster Linie für die Verwendung mit Android entwickelt und ist daher als Abhängigkeit von einer Android-Bibliothek verfügbar: org.tensorflow:tensorflow-lite.

In Java verwenden Sie die Klasse Interpreter, um ein Modell zu laden und Modellinferenz voranzutreiben. Oft ist dies die einzige API, die Sie benötigen.

Sie können ein Interpreter mit einer .tflite-Datei initialisieren:

public Interpreter(@NotNull File modelFile);

Oder mit einem MappedByteBuffer:

public Interpreter(@NotNull MappedByteBuffer mappedByteBuffer);

In beiden Fällen müssen Sie ein gültiges TensorFlow Lite-Modell angeben oder die API löst IllegalArgumentException aus. Wenn Sie MappedByteBuffer zum Initialisieren eines Interpreter verwenden, darf dieser für die gesamte Lebensdauer des Interpreter unverändert bleiben.

Die bevorzugte Methode zum Ausführen von Inferenzen für ein Modell ist die Verwendung von Signaturen. Verfügbar für Modelle, die ab Tensorflow 2.5 konvertiert wurden.

try (Interpreter interpreter = new Interpreter(file_of_tensorflowlite_model)) {
  Map<String, Object> inputs = new HashMap<>();
  inputs.put("input_1", input1);
  inputs.put("input_2", input2);
  Map<String, Object> outputs = new HashMap<>();
  outputs.put("output_1", output1);
  interpreter.runSignature(inputs, outputs, "mySignature");
}

Die Methode runSignature verwendet drei Argumente:

  • Inputs : Ordnet die Eingaben vom Eingabenamen in der Signatur einem Eingabeobjekt zu.

  • Outputs (Ausgaben): Zuordnung für die Ausgabezuordnung vom Ausgabenamen in der Signatur zu den Ausgabedaten.

  • Signaturname (optional): Signaturname. Das Feld kann leer bleiben, wenn das Modell eine einzelne Signatur hat.

Eine andere Möglichkeit, eine Inferenz auszuführen, wenn das Modell keine definierten Signaturen hat. Rufen Sie einfach unter Interpreter.run() an. Beispiel:

try (Interpreter interpreter = new Interpreter(file_of_a_tensorflowlite_model)) {
  interpreter.run(input, output);
}

Die Methode run() nimmt nur eine Eingabe an und gibt nur eine Ausgabe zurück. Wenn Ihr Modell also mehrere Eingaben oder mehrere Ausgaben hat, verwenden Sie stattdessen:

interpreter.runForMultipleInputsOutputs(inputs, map_of_indices_to_outputs);

In diesem Fall entspricht jeder Eintrag in inputs einem Eingabetensor und map_of_indices_to_outputs ordnet die Indexe der Ausgabetensoren den entsprechenden Ausgabedaten zu.

In beiden Fällen sollten die Tensorindexe den Werten entsprechen, die Sie dem TensorFlow Lite Converter beim Erstellen des Modells angegeben haben. Beachten Sie, dass die Reihenfolge der Tensoren in input mit der Reihenfolge übereinstimmen muss, die dem TensorFlow Lite Converter zugewiesen wurde.

Die Klasse Interpreter bietet außerdem praktische Funktionen zum Abrufen des Index einer Modelleingabe oder -ausgabe mithilfe eines Vorgangsnamens:

public int getInputIndex(String opName);
public int getOutputIndex(String opName);

Wenn opName kein gültiger Vorgang im Modell ist, wird ein IllegalArgumentException ausgelöst.

Bedenken Sie außerdem, dass Interpreter Inhaber von Ressourcen ist. Um Speicherlecks zu vermeiden, müssen die Ressourcen nach der Verwendung durch Folgendes freigegeben werden:

interpreter.close();

Ein Beispielprojekt mit Java finden Sie im Beispiel für die Android-Bildklassifizierung.

Unterstützte Datentypen (in Java)

Damit Sie TensorFlow Lite verwenden können, müssen die Datentypen der Eingabe- und Ausgabetensoren einer der folgenden primitiven Typen sein:

  • float
  • int
  • long
  • byte

String-Typen werden ebenfalls unterstützt, diese sind jedoch anders codiert als die primitiven Typen. Insbesondere die Form eines Strings von Tensor bestimmt die Anzahl und Anordnung von Strings im Tensor, wobei jedes Element selbst ein String mit variabler Länge ist. In diesem Sinne kann die (Byte-)Größe des Tensors nicht allein aus der Form und dem Typ berechnet werden. Daher können Strings nicht als einzelnes, flaches ByteBuffer-Argument angegeben werden.

Wenn andere Datentypen verwendet werden, einschließlich Boxtypen wie Integer und Float, wird ein IllegalArgumentException ausgegeben.

Eingaben

Jede Eingabe sollte ein Array oder ein mehrdimensionales Array der unterstützten Primitiven oder ein unformatierter ByteBuffer der entsprechenden Größe sein. Wenn die Eingabe ein Array oder ein mehrdimensionales Array ist, wird die Größe des zugehörigen Eingabetensors zum Zeitpunkt der Inferenz implizit auf die Dimensionen des Arrays angepasst. Wenn die Eingabe ein ByteBuffer ist, sollte der Aufrufer die Größe des zugehörigen Eingabetensors (über Interpreter.resizeInput()) zuerst manuell anpassen, bevor die Inferenz ausgeführt wird.

Verwenden Sie bei Verwendung von ByteBuffer vorzugsweise direkte Byte-Zwischenspeicher, da Interpreter unnötige Kopien vermeiden kann. Wenn ByteBuffer ein direkter Byte-Zwischenspeicher ist, muss seine Reihenfolge ByteOrder.nativeOrder() sein. Nachdem er für eine Modellinferenz verwendet wurde, muss er unverändert bleiben, bis die Modellinferenz abgeschlossen ist.

Ausgaben

Jede Ausgabe sollte ein Array oder ein mehrdimensionales Array der unterstützten primitiven Typen oder ein ByteBuffer der entsprechenden Größe sein. Beachten Sie, dass einige Modelle dynamische Ausgaben haben, bei denen die Form der Ausgabetensoren je nach Eingabe variieren kann. Mit der vorhandenen Java Inference API gibt es keine einfache Möglichkeit, dies zu handhaben, aber geplante Erweiterungen werden dies ermöglichen.

Modell in Swift laden und ausführen

Plattform: iOS

Die Swift API ist im TensorFlowLiteSwift-Pod von CocoaPods verfügbar.

Zuerst musst du das Modul TensorFlowLite importieren.

import TensorFlowLite
// Getting model path
guard
  let modelPath = Bundle.main.path(forResource: "model", ofType: "tflite")
else {
  // Error handling...
}

do {
  // Initialize an interpreter with the model.
  let interpreter = try Interpreter(modelPath: modelPath)

  // Allocate memory for the model's input `Tensor`s.
  try interpreter.allocateTensors()

  let inputData: Data  // Should be initialized

  // input data preparation...

  // Copy the input data to the input `Tensor`.
  try self.interpreter.copy(inputData, toInputAt: 0)

  // Run inference by invoking the `Interpreter`.
  try self.interpreter.invoke()

  // Get the output `Tensor`
  let outputTensor = try self.interpreter.output(at: 0)

  // Copy output to `Data` to process the inference results.
  let outputSize = outputTensor.shape.dimensions.reduce(1, {x, y in x * y})
  let outputData =
        UnsafeMutableBufferPointer<Float32>.allocate(capacity: outputSize)
  outputTensor.data.copyBytes(to: outputData)

  if (error != nil) { /* Error handling... */ }
} catch error {
  // Error handling...
}

Modell in Objective-C laden und ausführen

Plattform: iOS

Die Objective-C API ist im TensorFlowLiteObjC-Pod von CocoaPods verfügbar.

Zuerst musst du das Modul TensorFlowLite importieren.

@import TensorFlowLite;
NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"model"
                                                      ofType:@"tflite"];
NSError *error;

// Initialize an interpreter with the model.
TFLInterpreter *interpreter = [[TFLInterpreter alloc] initWithModelPath:modelPath
                                                                  error:&error];
if (error != nil) { /* Error handling... */ }

// Allocate memory for the model's input `TFLTensor`s.
[interpreter allocateTensorsWithError:&error];
if (error != nil) { /* Error handling... */ }

NSMutableData *inputData;  // Should be initialized
// input data preparation...

// Get the input `TFLTensor`
TFLTensor *inputTensor = [interpreter inputTensorAtIndex:0 error:&error];
if (error != nil) { /* Error handling... */ }

// Copy the input data to the input `TFLTensor`.
[inputTensor copyData:inputData error:&error];
if (error != nil) { /* Error handling... */ }

// Run inference by invoking the `TFLInterpreter`.
[interpreter invokeWithError:&error];
if (error != nil) { /* Error handling... */ }

// Get the output `TFLTensor`
TFLTensor *outputTensor = [interpreter outputTensorAtIndex:0 error:&error];
if (error != nil) { /* Error handling... */ }

// Copy output to `NSData` to process the inference results.
NSData *outputData = [outputTensor dataWithError:&error];
if (error != nil) { /* Error handling... */ }

C API in Objective-C-Code verwenden

Bevollmächtigte werden von der Objective-C API derzeit nicht unterstützt. Wenn Sie Bevollmächtigte mit Objective-C-Code verwenden möchten, müssen Sie die zugrunde liegende C API direkt aufrufen.

#include "tensorflow/lite/c/c_api.h"
TfLiteModel* model = TfLiteModelCreateFromFile([modelPath UTF8String]);
TfLiteInterpreterOptions* options = TfLiteInterpreterOptionsCreate();

// Create the interpreter.
TfLiteInterpreter* interpreter = TfLiteInterpreterCreate(model, options);

// Allocate tensors and populate the input tensor data.
TfLiteInterpreterAllocateTensors(interpreter);
TfLiteTensor* input_tensor =
    TfLiteInterpreterGetInputTensor(interpreter, 0);
TfLiteTensorCopyFromBuffer(input_tensor, input.data(),
                           input.size() * sizeof(float));

// Execute inference.
TfLiteInterpreterInvoke(interpreter);

// Extract the output tensor data.
const TfLiteTensor* output_tensor =
    TfLiteInterpreterGetOutputTensor(interpreter, 0);
TfLiteTensorCopyToBuffer(output_tensor, output.data(),
                         output.size() * sizeof(float));

// Dispose of the model and interpreter objects.
TfLiteInterpreterDelete(interpreter);
TfLiteInterpreterOptionsDelete(options);
TfLiteModelDelete(model);

Modell in C++ laden und ausführen

Plattformen: Android, iOS und Linux

In C++ wird das Modell in der Klasse FlatBufferModel gespeichert. Es kapselt ein TensorFlow Lite-Modell ein und Sie können es auf verschiedene Arten erstellen, je nachdem, wo das Modell gespeichert ist:

class FlatBufferModel {
  // Build a model based on a file. Return a nullptr in case of failure.
  static std::unique_ptr<FlatBufferModel> BuildFromFile(
      const char* filename,
      ErrorReporter* error_reporter);

  // Build a model based on a pre-loaded flatbuffer. The caller retains
  // ownership of the buffer and should keep it alive until the returned object
  // is destroyed. Return a nullptr in case of failure.
  static std::unique_ptr<FlatBufferModel> BuildFromBuffer(
      const char* buffer,
      size_t buffer_size,
      ErrorReporter* error_reporter);
};

Da das Modell jetzt ein FlatBufferModel-Objekt ist, können Sie es mit einer Interpreter ausführen. Eine einzelne FlatBufferModel kann gleichzeitig von mehreren Interpreter verwendet werden.

Die wichtigen Teile der Interpreter API werden im Code-Snippet unten gezeigt. Dabei ist Folgendes zu beachten:

  • Tensoren werden durch Ganzzahlen dargestellt, um Stringvergleiche (und feste Abhängigkeiten von Stringbibliotheken) zu vermeiden.
  • Auf einen Interpreter darf nicht aus gleichzeitigen Threads zugegriffen werden.
  • Die Speicherzuweisung für Eingabe- und Ausgabetensoren muss durch Aufrufen von AllocateTensors() direkt nach der Größenanpassung der Tensoren ausgelöst werden.

Die einfachste Verwendung von TensorFlow Lite mit C++ sieht so aus:

// Load the model
std::unique_ptr<tflite::FlatBufferModel> model =
    tflite::FlatBufferModel::BuildFromFile(filename);

// Build the interpreter
tflite::ops::builtin::BuiltinOpResolver resolver;
std::unique_ptr<tflite::Interpreter> interpreter;
tflite::InterpreterBuilder(*model, resolver)(&interpreter);

// Resize input tensors, if desired.
interpreter->AllocateTensors();

float* input = interpreter->typed_input_tensor<float>(0);
// Fill `input`.

interpreter->Invoke();

float* output = interpreter->typed_output_tensor<float>(0);

Weiteren Beispielcode finden Sie unter minimal.cc und label_image.cc.

Modell in Python laden und ausführen

Plattform: Linux

Die Python API zum Ausführen einer Inferenz benötigt meistens nur tf.lite.Interpreter, um ein Modell zu laden und eine Inferenz auszuführen.

Das folgende Beispiel zeigt, wie Sie mit dem Python-Interpreter eine .tflite-Datei laden und eine Inferenz mit zufälligen Eingabedaten ausführen:

Dieses Beispiel wird empfohlen, wenn Sie eine Konvertierung vom Modell mit einem definierten SignatureDef-Wert durchführen. Verfügbar ab TensorFlow 2.5

class TestModel(tf.Module):
  def __init__(self):
    super(TestModel, self).__init__()

  @tf.function(input_signature=[tf.TensorSpec(shape=[1, 10], dtype=tf.float32)])
  def add(self, x):
    '''
    Simple method that accepts single input 'x' and returns 'x' + 4.
    '''
    # Name the output 'result' for convenience.
    return {'result' : x + 4}


SAVED_MODEL_PATH = 'content/saved_models/test_variable'
TFLITE_FILE_PATH = 'content/test_variable.tflite'

# Save the model
module = TestModel()
# You can omit the signatures argument and a default signature name will be
# created with name 'serving_default'.
tf.saved_model.save(
    module, SAVED_MODEL_PATH,
    signatures={'my_signature':module.add.get_concrete_function()})

# Convert the model using TFLiteConverter
converter = tf.lite.TFLiteConverter.from_saved_model(SAVED_MODEL_PATH)
tflite_model = converter.convert()
with open(TFLITE_FILE_PATH, 'wb') as f:
  f.write(tflite_model)

# Load the TFLite model in TFLite Interpreter
interpreter = tf.lite.Interpreter(TFLITE_FILE_PATH)
# There is only 1 signature defined in the model,
# so it will return it by default.
# If there are multiple signatures then we can pass the name.
my_signature = interpreter.get_signature_runner()

# my_signature is callable with input as arguments.
output = my_signature(x=tf.constant([1.0], shape=(1,10), dtype=tf.float32))
# 'output' is dictionary with all outputs from the inference.
# In this case we have single output 'result'.
print(output['result'])

Ein weiteres Beispiel, wenn für das Modell keine SignatureDefs definiert sind.

import numpy as np
import tensorflow as tf

# Load the TFLite model and allocate tensors.
interpreter = tf.lite.Interpreter(model_path="converted_model.tflite")
interpreter.allocate_tensors()

# Get input and output tensors.
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()

# Test the model on random input data.
input_shape = input_details[0]['shape']
input_data = np.array(np.random.random_sample(input_shape), dtype=np.float32)
interpreter.set_tensor(input_details[0]['index'], input_data)

interpreter.invoke()

# The function `get_tensor()` returns a copy of the tensor data.
# Use `tensor()` in order to get a pointer to the tensor.
output_data = interpreter.get_tensor(output_details[0]['index'])
print(output_data)

Als Alternative zum Laden des Modells als vorab konvertierte .tflite-Datei können Sie Ihren Code mit der TensorFlow Lite Converter Python API kombinieren. So können Sie Ihr Keras-Modell in das TensorFlow Lite-Format konvertieren und dann eine Inferenz ausführen:

import numpy as np
import tensorflow as tf

img = tf.keras.Input(shape=(64, 64, 3), name="img")
const = tf.constant([1., 2., 3.]) + tf.constant([1., 4., 4.])
val = img + const
out = tf.identity(val, name="out")

# Convert to TF Lite format
converter = tf.lite.TFLiteConverter.from_keras_model(tf.keras.models.Model(inputs=[img], outputs=[out]))
tflite_model = converter.convert()

# Load the TFLite model and allocate tensors.
interpreter = tf.lite.Interpreter(model_content=tflite_model)
interpreter.allocate_tensors()

# Continue to get tensors and so forth, as shown above...

Weiteren Python-Beispielcode finden Sie unter label_image.py.

Inferenz mit dynamischem Formmodell ausführen

Wenn Sie ein Modell mit einer dynamischen Eingabeform ausführen möchten, passen Sie die Größe der Eingabeform an, bevor Sie eine Inferenz ausführen. Andernfalls wird die Form None in TensorFlow-Modellen durch den Platzhalter 1 in TFLite-Modellen ersetzt.

Die folgenden Beispiele zeigen, wie Sie die Größe der Eingabeform anpassen, bevor eine Inferenz in verschiedenen Sprachen ausgeführt wird. In allen Beispielen wird davon ausgegangen, dass die Eingabeform als [1/None, 10] definiert ist, und die Größe muss auf [3, 10] angepasst werden.

C++-Beispiel:

// Resize input tensors before allocate tensors
interpreter->ResizeInputTensor(/*tensor_index=*/0, std::vector<int>{3,10});
interpreter->AllocateTensors();

Beispiel für Python:

# Load the TFLite model in TFLite Interpreter
interpreter = tf.lite.Interpreter(model_path=TFLITE_FILE_PATH)

# Resize input shape for dynamic shape model and allocate tensor
interpreter.resize_tensor_input(interpreter.get_input_details()[0]['index'], [3, 10])
interpreter.allocate_tensors()

# Get input and output tensors.
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()

Unterstützte Vorgänge

TensorFlow Lite unterstützt einen Teil der TensorFlow-Vorgänge mit einigen Einschränkungen. Eine vollständige Liste der Vorgänge und Einschränkungen finden Sie auf der Seite zu TF Lite-Vorgängen.