Przetwarzanie danych wejściowych i wyjściowych przy użyciu biblioteki pomocy LiteRT

Programiści aplikacji mobilnych zwykle wchodzą w interakcję z typami obiektów, takimi jak map bitowych ani elementów podstawowych, takich jak liczby całkowite. Tłumacz LiteRT Interfejs API, który uruchamia model systemów uczących się na urządzeniu, używa tensorów w postaci: ByteBuffer, który może być trudny do debugowania i manipulacji. Biblioteka pomocy LiteRT Android ma pomóc w przetwarzaniu danych wejściowych i wyjściowych modeli LiteRT oraz ułatwienie tłumaczenia przy użyciu LiteRT.

Pierwsze kroki

Importuj zależność Gradle i inne ustawienia

Skopiuj plik modelu .tflite do katalogu zasobów modułu Androida w którym będzie uruchamiany model. Określ, że plik nie powinien być skompresowany. dodaj bibliotekę LiteRT 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 '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'
}

Dowiedz się więcej AAR w bibliotece pomocy LiteRT hostowane w MavenCentral dla różnych wersji Biblioteki pomocy.

Podstawowe operacje na obrazach i ich konwertowaniu

Biblioteka pomocy LiteRT obejmuje pakiet podstawowych operacji na obrazach takie jak przycinanie i zmiana rozmiaru. Aby go używać, utwórz ImagePreprocessor i dodaj wymagane operacje. Aby przekonwertować obraz na format tensora wymagane przez tłumacza LiteRT, utwórz TensorImage do użycia 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 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 tensora można odczytać za pomocą funkcji biblioteka wyodrębniania metadanych oraz inne informacje o modelu.

Podstawowe przetwarzanie danych audio

Biblioteka pomocy LiteRT definiuje też pakowanie klas TensorAudio za pomocą kilku podstawowych metod przetwarzania danych audio. Jest używany głównie razem z AudioRecord i rejestruje próbki dźwięku 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 kontenera, które będą zapisz 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);

Wczytuję model i uruchomione wnioskowanie:

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

Uzyskiwanie dostępu do wyniku

Programiści mogą uzyskać dostęp do danych wyjściowych bezpośrednio probabilityBuffer.getFloatArray() Jeśli model generuje kwantyzowane dane wyjściowe, pamiętaj o przekonwertowaniu wyniku. W przypadku kwantowego modelu MobileNet musi podzielić każdą wartość wyjściową przez 255, aby uzyskać prawdopodobieństwo w zakresie od Od 0 (najmniejsze prawdopodobieństwo) do 1 (najprawdopodobniej) w każdej kategorii.

Opcjonalnie: mapowanie wyników na etykiety

Programiści mogą też opcjonalnie zmapować wyniki na etykiety. Najpierw skopiuj tekst zawierający etykiety do katalogu zasobów modułu. Następnie wczytaj etykietę za pomocą tego 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);
}

Ten fragment kodu pokazuje, jak powiązać prawdopodobieństwa z etykiety 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();
}

Obecny zasięg przypadku użycia

Obecna wersja Biblioteki pomocy LiteRT obejmuje:

  • najpopularniejsze typy danych (liczba zmiennoprzecinkowa, uint8, obrazy, dźwięk oraz tablica tych obiektów) jako dane wejściowe i wyjściowe modeli tflite.
  • podstawowych operacji na obrazie (przycinanie obrazu, zmiana rozmiaru i obracanie).
  • normalizacja i kwantyzacja
  • pliki narzędzi

Kolejne wersje ulepszą obsługę aplikacji związanych z tekstem.

Architektura procesora ImageProcessor

ImageProcessor umożliwia operacje manipulacji obrazami. powinny zostać zdefiniowane na początku i zoptymalizowane w trakcie procesu tworzenia. ImageProcessor obecnie obsługuje trzy podstawowe operacje wstępnego przetwarzania, zgodnie z opisem trzy komentarze w poniższym fragmencie kodu:

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

Pokaż więcej szczegółów tutaj o ich normalizacji i kwantyzacji.

Głównym celem biblioteki pomocy jest obsługa wszystkich tf.image przekształcenia danych. Oznacza to, że przekształcenie będzie takie samo jak w TensorFlow a implementacja będzie niezależna od systemu operacyjnego.

Programiści mogą również tworzyć niestandardowe procesory. Ważne jest, aby dostosować je do procesu trenowania. Trzeba więc pamiętać, wstępne przetwarzanie powinno mieć zastosowanie zarówno do trenowania, jak i wnioskowania, aby zwiększyć odtwarzalność.

Kwantyfikacja

Podczas inicjowania obiektów wejściowych lub wyjściowych, np. TensorImage lub TensorBuffer musisz 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);

Interfejsu TensorProcessor można używać do kwantyzacji tensorów wejściowych lub dekwantyzacji danych wyjściowych tensorów. Na przykład podczas przetwarzania kwantyzowanych danych wyjściowych TensorBuffer funkcja deweloper może użyć DequantizeOp do dekwantyzacji wyniku do postaci zmiennoprzecinkowej prawdopodobieństwo między 0 a 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ą funkcji bibliotekę wyodrębniania metadanych.