Memproses data input dan output dengan LiteRT Support Library

Developer aplikasi seluler biasanya berinteraksi dengan objek yang diketik seperti bitmap atau primitif seperti bilangan bulat. Namun, API penafsir LiteRT yang menjalankan model machine learning di perangkat menggunakan tensor dalam bentuk ByteBuffer, yang sulit di-debug dan dimanipulasi. Library Dukungan Android LiteRT dirancang untuk membantu memproses input dan output model LiteRT, serta mempermudah penggunaan penafsir LiteRT.

Memulai

Mengimpor dependensi Gradle dan setelan lainnya

Salin file model .tflite ke direktori aset modul Android tempat model akan dijalankan. Tentukan bahwa file tidak boleh dikompresi, dan tambahkan library LiteRT ke file build.gradle modul:

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

Jelajahi AAR Support Library LiteRT yang dihosting di MavenCentral untuk berbagai versi Support Library.

Manipulasi dan konversi gambar dasar

LiteRT Support Library memiliki serangkaian metode manipulasi gambar dasar seperti memangkas dan mengubah ukuran. Untuk menggunakannya, buat ImagePreprocessor dan tambahkan operasi yang diperlukan. Untuk mengonversi gambar ke format tensor yang diperlukan oleh penafsir LiteRT, buat TensorImage yang akan digunakan sebagai input:

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 tensor dapat dibaca melalui library pengekstraksi metadata serta informasi model lainnya.

Pemrosesan data audio dasar

LiteRT Support Library juga menentukan class TensorAudio yang membungkus beberapa metode pemrosesan data audio dasar. Sebagian besar digunakan bersama dengan AudioRecord dan merekam sampel audio dalam buffer ring.

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

Membuat objek output dan menjalankan model

Sebelum menjalankan model, kita perlu membuat objek penampung yang akan menyimpan hasilnya:

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

Memuat model dan menjalankan inferensi:

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

Mengakses hasil

Developer dapat mengakses output secara langsung melalui probabilityBuffer.getFloatArray(). Jika model menghasilkan output terkuantisasi, jangan lupa untuk mengonversi hasilnya. Untuk model MobileNet yang dikuantisasi, developer perlu membagi setiap nilai output dengan 255 untuk mendapatkan probabilitas yang berkisar dari 0 (paling tidak mungkin) hingga 1 (paling mungkin) untuk setiap kategori.

Opsional: Memetakan hasil ke label

Developer juga dapat memetakan hasil ke label secara opsional. Pertama, salin file teks yang berisi label ke direktori aset modul. Selanjutnya, muat file label menggunakan kode berikut:

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

Cuplikan berikut menunjukkan cara mengaitkan probabilitas dengan label kategori:

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

Cakupan kasus penggunaan saat ini

Versi LiteRT Support Library saat ini mencakup:

  • jenis data umum (float, uint8, gambar, audio, dan array objek ini) sebagai input dan output model tflite.
  • operasi gambar dasar (pangkas gambar, ubah ukuran, dan putar).
  • normalisasi dan kuantisasi
  • utilitas file

Versi mendatang akan meningkatkan dukungan untuk aplikasi terkait teks.

Arsitektur ImageProcessor

Desain ImageProcessor memungkinkan operasi manipulasi gambar ditentukan di awal dan dioptimalkan selama proses build. ImageProcessor saat ini mendukung tiga operasi praproses dasar, seperti yang dijelaskan dalam tiga komentar dalam cuplikan kode di bawah:

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

Lihat detail selengkapnya di sini tentang normalisasi dan kuantisasi.

Tujuan akhir dari support library adalah mendukung semua transformasi tf.image. Artinya, transformasi akan sama dengan TensorFlow dan implementasinya tidak bergantung pada sistem operasi.

Developer juga dapat membuat pemroses kustom. Dalam kasus ini, penting untuk menyelaraskan dengan proses pelatihan, yaitu praproses yang sama harus diterapkan pada pelatihan dan inferensi untuk meningkatkan reproduksibilitas.

Kuantisasi

Saat memulai objek input atau output seperti TensorImage atau TensorBuffer, Anda harus menentukan jenisnya menjadi DataType.UINT8 atau DataType.FLOAT32.

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

TensorProcessor dapat digunakan untuk menguantisasi tensor input atau mendekuantisasi tensor output. Misalnya, saat memproses output terkuantisasi TensorBuffer, developer dapat menggunakan DequantizeOp untuk mendekuantisasi hasil ke probabilitas floating point antara 0 dan 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);

Parameter kuantisasi tensor dapat dibaca melalui library metadata extractor.