Eingabe- und Ausgabedaten mit der TensorFlow Lite-Supportbibliothek verarbeiten

Entwickler von mobilen Anwendungen interagieren normalerweise mit typisierten Objekten wie Bitmaps oder Primitiven wie Ganzzahlen. Die TensorFlow Lite-Interpreter-API, die das On-Device-Modell für maschinelles Lernen ausführt, verwendet jedoch Tensoren in Form von ByteBuffer. Diese lassen sich nur schwer debuggen und bearbeiten. Die Android-Supportbibliothek für TensorFlow Lite wurde entwickelt, um die Eingabe und Ausgabe von TensorFlow Lite-Modellen zu verarbeiten und den TensorFlow Lite-Interpreter nutzerfreundlicher zu machen.

Erste Schritte

Gradle-Abhängigkeit und andere Einstellungen importieren

Kopieren Sie die Modelldatei .tflite in das Asset-Verzeichnis des Android-Moduls, in dem das Modell ausgeführt wird. Geben Sie an, dass die Datei nicht komprimiert werden soll, und fügen Sie die TensorFlow Lite-Bibliothek der build.gradle-Datei des Moduls hinzu:

android {
    // Other settings

    // Specify tflite file should not be compressed for the app apk
    aaptOptions {
        noCompress "tflite"
    }

}

dependencies {
    // Other dependencies

    // Import tflite dependencies
    implementation 'org.tensorflow:tensorflow-lite:0.0.0-nightly-SNAPSHOT'
    // The GPU delegate library is optional. Depend on it as needed.
    implementation 'org.tensorflow:tensorflow-lite-gpu:0.0.0-nightly-SNAPSHOT'
    implementation 'org.tensorflow:tensorflow-lite-support:0.0.0-nightly-SNAPSHOT'
}

Entdecken Sie die in MavenCentral gehostete TensorFlow Lite-Supportbibliothek AAR für verschiedene Versionen der Supportbibliothek.

Grundlegende Bildbearbeitung und -konvertierung

Die TensorFlow Lite Support Library bietet eine Reihe von grundlegenden Bildbearbeitungsmethoden wie Zuschneiden und Ändern der Größe. Wenn Sie sie verwenden möchten, erstellen Sie ein ImagePreprocessor und fügen Sie die erforderlichen Vorgänge hinzu. Um das Bild in das für den TensorFlow Lite-Interpreter erforderliche Tensorformat zu konvertieren, erstellen Sie einen TensorImage, der als Eingabe verwendet wird:

import org.tensorflow.lite.DataType;
import org.tensorflow.lite.support.image.ImageProcessor;
import org.tensorflow.lite.support.image.TensorImage;
import org.tensorflow.lite.support.image.ops.ResizeOp;

// Initialization code
// Create an ImageProcessor with all ops required. For more ops, please
// refer to the ImageProcessor Architecture section in this README.
ImageProcessor imageProcessor =
    new ImageProcessor.Builder()
        .add(new ResizeOp(224, 224, ResizeOp.ResizeMethod.BILINEAR))
        .build();

// Create a TensorImage object. This creates the tensor of the corresponding
// tensor type (uint8 in this case) that the TensorFlow Lite interpreter needs.
TensorImage tensorImage = new TensorImage(DataType.UINT8);

// Analysis code for every frame
// Preprocess the image
tensorImage.load(bitmap);
tensorImage = imageProcessor.process(tensorImage);

DataType eines Tensors kann durch die Metadatenextraktorbibliothek und andere Modellinformationen gelesen werden.

Grundlegende Audiodatenverarbeitung

Die TensorFlow Lite-Supportbibliothek definiert auch ein TensorAudio-Klassen-Wrapping einiger grundlegender Methoden zur Audiodatenverarbeitung. Es wird hauptsächlich in Verbindung mit AudioRecord verwendet und erfasst Audiobeispiele in einem Ringzwischenspeicher.

import android.media.AudioRecord;
import org.tensorflow.lite.support.audio.TensorAudio;

// Create an `AudioRecord` instance.
AudioRecord record = AudioRecord(...)

// Create a `TensorAudio` object from Android AudioFormat.
TensorAudio tensorAudio = new TensorAudio(record.getFormat(), size)

// Load all audio samples available in the AudioRecord without blocking.
tensorAudio.load(record)

// Get the `TensorBuffer` for inference.
TensorBuffer buffer = tensorAudio.getTensorBuffer()

Ausgabeobjekte erstellen und Modell ausführen

Bevor Sie das Modell ausführen, müssen Sie die Containerobjekte erstellen, in denen das Ergebnis gespeichert wird:

import org.tensorflow.lite.DataType;
import org.tensorflow.lite.support.tensorbuffer.TensorBuffer;

// Create a container for the result and specify that this is a quantized model.
// Hence, the 'DataType' is defined as UINT8 (8-bit unsigned integer)
TensorBuffer probabilityBuffer =
    TensorBuffer.createFixedSize(new int[]{1, 1001}, DataType.UINT8);

Laden des Modells und Ausführen der Inferenz:

import java.nio.MappedByteBuffer;
import org.tensorflow.lite.InterpreterFactory;
import org.tensorflow.lite.InterpreterApi;

// Initialise the model
try{
    MappedByteBuffer tfliteModel
        = FileUtil.loadMappedFile(activity,
            "mobilenet_v1_1.0_224_quant.tflite");
    InterpreterApi tflite = new InterpreterFactory().create(
        tfliteModel, new InterpreterApi.Options());
} catch (IOException e){
    Log.e("tfliteSupport", "Error reading model", e);
}

// Running inference
if(null != tflite) {
    tflite.run(tImage.getBuffer(), probabilityBuffer.getBuffer());
}

Auf das Ergebnis zugreifen

Entwickler können direkt über probabilityBuffer.getFloatArray() auf die Ausgabe zugreifen. Wenn das Modell eine quantisierte Ausgabe erzeugt, denken Sie daran, das Ergebnis umzuwandeln. Für das quantisierte MobileNet-Modell muss der Entwickler jeden Ausgabewert durch 255 teilen, um die Wahrscheinlichkeit für jede Kategorie zwischen 0 (am wahrscheinlichsten) und 1 (höchstwahrscheinlich) zu erhalten.

Optional: Ergebnisse zu Labels zuordnen

Entwickler können die Ergebnisse auch optional Labels zuordnen. Kopieren Sie zuerst die Textdatei mit den Labels in das Asset-Verzeichnis des Moduls. Laden Sie als Nächstes die Labeldatei mit dem folgenden Code:

import org.tensorflow.lite.support.common.FileUtil;

final String ASSOCIATED_AXIS_LABELS = "labels.txt";
List<String> associatedAxisLabels = null;

try {
    associatedAxisLabels = FileUtil.loadLabels(this, ASSOCIATED_AXIS_LABELS);
} catch (IOException e) {
    Log.e("tfliteSupport", "Error reading label file", e);
}

Das folgende Snippet zeigt, wie die Wahrscheinlichkeiten Kategorielabels zugeordnet werden:

import java.util.Map;
import org.tensorflow.lite.support.common.TensorProcessor;
import org.tensorflow.lite.support.common.ops.NormalizeOp;
import org.tensorflow.lite.support.label.TensorLabel;

// Post-processor which dequantize the result
TensorProcessor probabilityProcessor =
    new TensorProcessor.Builder().add(new NormalizeOp(0, 255)).build();

if (null != associatedAxisLabels) {
    // Map of labels and their corresponding probability
    TensorLabel labels = new TensorLabel(associatedAxisLabels,
        probabilityProcessor.process(probabilityBuffer));

    // Create a map to access the result based on label
    Map<String, Float> floatMap = labels.getMapWithFloatValue();
}

Aktuelle Anwendungsfallabdeckung

Die aktuelle Version der TensorFlow Lite-Supportbibliothek umfasst Folgendes:

  • Gängige Datentypen (Float, uint8, Bilder, Audio und Array dieser Objekte) als Eingaben und Ausgaben von TFlite-Modellen.
  • grundlegende Bildvorgänge (Bild zuschneiden, Größe ändern und drehen)
  • Normalisierung und Quantisierung
  • Datei-Dienstprogramme

In zukünftigen Versionen wird die Unterstützung für textbezogene Anwendungen verbessert.

ImageProcessor-Architektur

Durch das Design der ImageProcessor konnten die Bildbearbeitungsvorgänge vorab definiert und während des Build-Prozesses optimiert werden. ImageProcessor unterstützt derzeit drei grundlegende Vorverarbeitungsvorgänge, wie in den drei Kommentaren im folgenden Code-Snippet beschrieben:

import org.tensorflow.lite.support.common.ops.NormalizeOp;
import org.tensorflow.lite.support.common.ops.QuantizeOp;
import org.tensorflow.lite.support.image.ops.ResizeOp;
import org.tensorflow.lite.support.image.ops.ResizeWithCropOrPadOp;
import org.tensorflow.lite.support.image.ops.Rot90Op;

int width = bitmap.getWidth();
int height = bitmap.getHeight();

int size = height > width ? width : height;

ImageProcessor imageProcessor =
    new ImageProcessor.Builder()
        // Center crop the image to the largest square possible
        .add(new ResizeWithCropOrPadOp(size, size))
        // Resize using Bilinear or Nearest neighbour
        .add(new ResizeOp(224, 224, ResizeOp.ResizeMethod.BILINEAR));
        // Rotation counter-clockwise in 90 degree increments
        .add(new Rot90Op(rotateDegrees / 90))
        .add(new NormalizeOp(127.5, 127.5))
        .add(new QuantizeOp(128.0, 1/128.0))
        .build();

Weitere Informationen zur Normalisierung und Quantisierung

Letztendlich soll die Supportbibliothek alle tf.image-Transformationen unterstützen. Das bedeutet, dass die Transformation mit TensorFlow identisch ist und die Implementierung vom Betriebssystem unabhängig ist.

Entwickler können auch benutzerdefinierte Prozessoren erstellen. In diesen Fällen ist es wichtig, sich auf den Trainingsprozess abzustimmen – die gleiche Vorverarbeitung sollte also sowohl für das Training als auch für die Inferenz gelten, um die Reproduzierbarkeit zu erhöhen.

Quantisierung

Wenn Sie Eingabe- oder Ausgabeobjekte wie TensorImage oder TensorBuffer initiieren, müssen Sie deren Typen als DataType.UINT8 oder DataType.FLOAT32 angeben.

TensorImage tensorImage = new TensorImage(DataType.UINT8);
TensorBuffer probabilityBuffer =
    TensorBuffer.createFixedSize(new int[]{1, 1001}, DataType.UINT8);

Mit TensorProcessor können Eingabetensoren quantisiert oder Ausgabetensoren dequantisiert werden. Bei der Verarbeitung einer quantisierten Ausgabe-TensorBuffer kann der Entwickler beispielsweise DequantizeOp verwenden, um das Ergebnis auf eine Gleitkommawahrscheinlichkeit zwischen 0 und 1 zu dequantisieren:

import org.tensorflow.lite.support.common.TensorProcessor;

// Post-processor which dequantize the result
TensorProcessor probabilityProcessor =
    new TensorProcessor.Builder().add(new DequantizeOp(0, 1/255.0)).build();
TensorBuffer dequantizedBuffer = probabilityProcessor.process(probabilityBuffer);

Die Quantisierungsparameter eines Tensors können mit der Metadaten-Extrahiererbibliothek ausgelesen werden.