Eingabe- und Ausgabedaten mit der LiteRT-Supportbibliothek verarbeiten

Entwickler mobiler Anwendungen interagieren in der Regel mit typisierten Objekten wie Bitmaps oder Primitive wie Ganzzahlen. Der LiteRT-Interpreter Die API, die das On-Device-Modell für maschinelles Lernen ausführt, verwendet Tensoren in Form von ByteBuffer, dessen Debugging und Bearbeitung schwierig sein können. Die LiteRT-Android-Supportbibliothek wurde entwickelt, um die Eingabe und Ausgabe von LiteRT-Modellen zu verarbeiten. erleichtern die Nutzung des LiteRT-Interpreters.

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 LiteRT-Bibliothek zur Datei build.gradle 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 'com.google.ai.edge.litert:litert-gpu:0.0.0-nightly-SNAPSHOT'
    implementation 'com.google.ai.edge.litert:litert-support:0.0.0-nightly-SNAPSHOT'
}

Entdecken Sie die In MavenCentral gehostete LiteRT-Supportbibliothek für automatisch angewendete Empfehlungen für verschiedene Versionen der Support Library.

Grundlegende Bildbearbeitung und ‐konvertierung

Die LiteRT Support Library bietet eine Reihe grundlegender Bildbearbeitungsmöglichkeiten. wie Zuschneiden und Größe ändern. Erstellen Sie zur Verwendung ein ImagePreprocessor und und fügen Sie die erforderlichen Vorgänge hinzu. Um das Bild in das Tensor-Format zu konvertieren vom LiteRT-Interpreter benötigt, erstellen Sie eine TensorImage, die verwendet werden soll als Eingabe:

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 LiteRT 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 Metadaten-Extraktorbibliothek sowie andere Modellinformationen.

Grundlegende Audiodatenverarbeitung

Die LiteRT-Supportbibliothek definiert auch ein TensorAudio-Klassen-Wrapping grundlegende Methoden zur Verarbeitung von Audiodaten. Sie werden meist in Verbindung mit AudioRecord in einem Ringpuffer erfasst.

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 wir das Modell ausführen, müssen wir die Containerobjekte erstellen, speichern Sie das Ergebnis:

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);

Modell laden und Inferenz ausführen:

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() Wenn das Modell eine quantisierte Ausgabe erzeugt, denken Sie daran, das Ergebnis zu konvertieren. Für das quantisierte MobileNet-Modell hat der Entwickler jeden Ausgabewert durch 255 dividieren muss, um die Wahrscheinlichkeit zwischen 0 (niedrigste Wahrscheinlichkeit) bis 1 (höchste Wahrscheinlichkeit) für jede Kategorie.

Optional: Ergebnisse zu Labels zuordnen

Entwickler können die Ergebnisse auch Labels zuordnen. Kopieren Sie zuerst den Text mit Labels in das Asset-Verzeichnis des Moduls verschieben. Als Nächstes laden Sie das Label 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 mit Kategorie-Labels:

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();
}

Abdeckung aktueller Anwendungsfälle

Die aktuelle Version der LiteRT-Supportbibliothek umfasst Folgendes:

  • Gängige Datentypen (float, uint8, images, audio und Array dieser Objekte) als Ein- und Ausgaben von tflite-Modellen.
  • Grundlegende Bildoperationen (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

Das Design von ImageProcessor ermöglichte Bildbearbeitungsvorgänge im Vorfeld definiert und während des Build-Prozesses optimiert werden. Das ImageProcessor unterstützt derzeit drei grundlegende Vorverarbeitungsvorgänge, die in den drei Kommentare im folgenden Code-Snippet:

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 Details hier zum Thema Normalisierung und Quantisierung.

Das Ziel der Supportbibliothek ist es, tf.image Transformationen. Das bedeutet, dass die Transformation mit der TensorFlow-Version identisch ist, und die Implementierung erfolgt unabhängig vom Betriebssystem.

Entwickler können auch benutzerdefinierte Prozessoren erstellen. Es ist wichtig, um diese Fälle auf den Trainingsprozess abzustimmen, sollte die Vorverarbeitung sowohl auf das Training als auch auf die Inferenz angewendet werden, Reproduzierbarkeit.

Quantisierung

Beim Initiieren von Eingabe- oder Ausgabeobjekten wie TensorImage oder TensorBuffer müssen Sie als Typ 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 die Ausgabe dequantisiert werden Tensoren. Bei der Verarbeitung einer quantisierten Ausgabe TensorBuffer wird beispielsweise der Wert Entwickler kann DequantizeOp verwenden, um das Ergebnis in einen Gleitkommawert zu dequantisieren Wahrscheinlichkeit zwischen 0 und 1:

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 durch die Metadatenextraktorbibliothek.