Przetwarzaj dane wejściowe i wyjściowe za pomocą biblioteki pomocniczej TensorFlow Lite

Twórcy aplikacji mobilnych zazwyczaj wchodzą w interakcję z obiektami wpisanymi na klawiaturze, takimi jak mapy bitowe, lub elementami pierwotnymi, takimi jak liczby całkowite. Jednak interfejs API interpretera TensorFlow Lite, który uruchamia model uczenia maszynowego na urządzeniu, wykorzystuje tensory w postaci ByteBuffer, które mogą być trudne do debugowania i manipulowania. Biblioteka pomocy technicznej TensorFlow Lite dla systemu Android została zaprojektowana, aby pomóc w przetwarzaniu danych wejściowych i wyjściowych modeli TensorFlow Lite oraz ułatwić korzystanie z interpretera TensorFlow Lite.

Pierwsze kroki

Zaimportuj zależność Gradle i inne ustawienia

Skopiuj plik modelu .tflite do katalogu zasobów modułu Android, w którym model będzie uruchamiany. Określ, że plik nie powinien być kompresowany i dodaj bibliotekę TensorFlow Lite do pliku build.gradle modułu:

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'
}

Przeglądaj bibliotekę wsparcia TensorFlow Lite AAR hostowaną w MavenCentral dla różnych wersji Biblioteki wsparcia.

Podstawowa manipulacja i konwersja obrazu

Biblioteka pomocy technicznej TensorFlow Lite zawiera zestaw podstawowych metod manipulacji obrazami, takich jak kadrowanie i zmiana rozmiaru. Aby z niego skorzystać, utwórz ImagePreprocessor i dodaj wymagane operacje. Aby przekonwertować obraz do formatu tensora wymaganego przez interpreter TensorFlow Lite, utwórz TensorImage , który będzie używany jako dane wejściowe:

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 tensora można odczytać za pomocą biblioteki ekstraktora metadanych , a także innych informacji o modelu.

Podstawowe przetwarzanie danych audio

Biblioteka pomocy technicznej TensorFlow Lite definiuje również klasę TensorAudio obejmującą niektóre podstawowe metody przetwarzania danych audio. Jest najczęściej używany razem z AudioRecord i przechwytuje próbki audio w buforze pierścieniowym.

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

Utwórz obiekty wyjściowe i uruchom model

Przed uruchomieniem modelu musimy utworzyć obiekty kontenerowe, które będą przechowywać wynik:

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

Ładowanie modelu i uruchamianie wnioskowania:

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

Dostęp do wyniku

Programiści mogą uzyskać bezpośredni dostęp do danych wyjściowych poprzez probabilityBuffer.getFloatArray() . Jeśli model generuje skwantowany wynik, pamiętaj o przekształceniu wyniku. W przypadku skwantowanego modelu MobileNet programista musi podzielić każdą wartość wyjściową przez 255, aby uzyskać prawdopodobieństwo w zakresie od 0 (najmniej prawdopodobne) do 1 (najbardziej prawdopodobne) dla każdej kategorii.

Opcjonalnie: Mapowanie wyników na etykiety

Programiści mogą również opcjonalnie mapować wyniki na etykiety. Najpierw skopiuj plik tekstowy zawierający etykiety do katalogu zasobów modułu. Następnie załaduj plik etykiety, używając następującego kodu:

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

Poniższy fragment pokazuje, jak powiązać prawdopodobieństwa z etykietami kategorii:

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

Bieżące pokrycie przypadków użycia

Aktualna wersja biblioteki pomocniczej TensorFlow Lite obejmuje:

  • popularne typy danych (float, uint8, obrazy, audio i tablica tych obiektów) jako wejścia i wyjścia modeli tflite.
  • podstawowe operacje na obrazach (przycinanie obrazu, zmiana rozmiaru i obracanie).
  • normalizacja i kwantyzacja
  • narzędzia plików

Przyszłe wersje poprawią obsługę aplikacji tekstowych.

Architektura procesora obrazu

Konstrukcja procesora ImageProcessor umożliwiła zdefiniowanie operacji manipulacji obrazem na początku i optymalizację podczas procesu kompilacji. ImageProcessor obsługuje obecnie trzy podstawowe operacje przetwarzania wstępnego, zgodnie z opisem w trzech komentarzach we fragmencie kodu poniżej:

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

Zobacz więcej szczegółów na temat normalizacji i kwantyzacji tutaj .

Ostatecznym celem biblioteki pomocniczej jest obsługa wszystkich transformacji tf.image . Oznacza to, że transformacja będzie taka sama jak w przypadku TensorFlow, a implementacja będzie niezależna od systemu operacyjnego.

Programiści mogą również tworzyć niestandardowe procesory. W takich przypadkach ważne jest, aby zapewnić zgodność z procesem uczenia – tj. to samo przetwarzanie wstępne powinno mieć zastosowanie zarówno do uczenia, jak i wnioskowania, aby zwiększyć odtwarzalność.

Kwantyzacja

Inicjując obiekty wejściowe lub wyjściowe, takie jak TensorImage lub TensorBuffer , należy określić ich typy jako DataType.UINT8 lub DataType.FLOAT32 .

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

TensorProcessor może służyć do kwantyzacji tensorów wejściowych lub dekwantyzacji tensorów wyjściowych. Na przykład podczas przetwarzania skwantowanego wyjścia TensorBuffer programista może użyć DequantizeOp do dekwantyzacji wyniku do prawdopodobieństwa zmiennoprzecinkowego z zakresu od 0 do 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);

Parametry kwantyzacji tensora można odczytać za pomocą biblioteki ekstraktora metadanych .