Verarbeiten Sie Eingabe- und Ausgabedaten mit der TensorFlow Lite Support Library

Entwickler mobiler Anwendungen interagieren normalerweise mit typisierten Objekten wie Bitmaps oder Grundelementen wie Ganzzahlen. Der TensorFlow Lite Interpreter, auf dem das Modell für maschinelles Lernen auf dem Gerät ausgeführt wird, verwendet jedoch Tensoren in Form von ByteBuffer, die möglicherweise schwer zu debuggen und zu manipulieren sind. Die TensorFlow Lite Android-Supportbibliothek soll die Eingabe und Ausgabe von TensorFlow Lite-Modellen verarbeiten und die Verwendung des TensorFlow Lite-Interpreters vereinfachen.

Einstieg

Importieren Sie die Gradle-Abhängigkeit und andere Einstellungen

Kopieren Sie die .tflite Modelldatei in das Assets-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 zur build.gradle Datei des Moduls build.gradle :

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 AAR der TensorFlow Lite-Supportbibliothek, die bei MavenCentral gehostet wird, für verschiedene Versionen der Supportbibliothek.

Grundlegende Bildbearbeitung und -konvertierung

Die TensorFlow Lite-Unterstützungsbibliothek verfügt über eine Reihe grundlegender Bildbearbeitungsmethoden wie Zuschneiden und Ändern der Größe. Um es zu verwenden, erstellen Sie einen ImagePreprocessor und fügen Sie die erforderlichen Operationen hinzu. Erstellen Sie ein TensorImage , das als Eingabe verwendet werden soll, um das Bild in das vom TensorFlow Lite-Interpreter erforderliche TensorImage zu TensorImage :

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 sowie durch andere Modellinformationen gelesen werden.

Baisc-Audiodatenverarbeitung

Die TensorFlow Lite-Unterstützungsbibliothek definiert auch eine TensorAudio Klasse, die einige grundlegende Methoden zur Verarbeitung von Audiodaten enthält. Es wird hauptsächlich zusammen mit AudioRecord verwendet und erfasst Audio-Samples in einem Ringpuffer.

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

Erstellen Sie Ausgabeobjekte und führen Sie das Modell aus

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

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 von Inferenz:

import org.tensorflow.lite.support.model.Model;

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

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

Zugriff auf das Ergebnis

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

Optional: Zuordnen von Ergebnissen zu Beschriftungen

Entwickler können die Ergebnisse optional auch Labels zuordnen. Kopieren Sie zunächst die Textdatei mit den Beschriftungen in das Assets-Verzeichnis des Moduls. Laden Sie als Nächstes die Etikettendatei 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 Kategoriebeschriftungen verknüpft werden:

import org.tensorflow.lite.support.common.TensorProcessor;
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 Support Library umfasst:

  • gängige Datentypen (float, uint8, images, audio und array dieser Objekte) als Ein- und Ausgänge von tflite-Modellen.
  • Grundlegende Bildoperationen (Bild zuschneiden, Größe ändern und drehen).
  • Normalisierung und Quantisierung
  • Datei-Utils

Zukünftige Versionen werden die Unterstützung für textbezogene Anwendungen verbessern.

ImageProcessor-Architektur

Durch das Design des ImageProcessor konnten die Bildbearbeitungsvorgänge im ImageProcessor definiert und während des Erstellungsprozesses optimiert werden. Der ImageProcessor unterstützt derzeit drei grundlegende Vorverarbeitungsvorgänge:

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

Sehen Sie mehr Details hier um Normalisierung und Quantisierung.

Das letztendliche Ziel der Unterstützungsbibliothek besteht darin, alle tf.image zu unterstützen. Dies 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, auf den Trainingsprozess abgestimmt zu sein - dh die gleiche Vorverarbeitung sollte sowohl für das Training als auch für die Inferenz gelten, um die Reproduzierbarkeit zu verbessern.

Quantisierung

Wenn Sie Eingabe- oder Ausgabeobjekte wie TensorImage oder TensorBuffer , 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 dem TensorProcessor können Eingangstensoren quantisiert oder Ausgangstensoren dequantisiert werden. Wenn der Entwickler beispielsweise einen quantisierten Ausgabe- TensorBuffer , kann er DequantizeOp , 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 durch die Metadatenextraktorbibliothek gelesen werden .