Përpunoni të dhënat e hyrjes dhe daljes me Bibliotekën e Mbështetjes LiteRT

Zhvilluesit e aplikacioneve celulare zakonisht ndërveprojnë me objekte të shtypura si bitmaps ose primitivë si numra të plotë. Sidoqoftë, API-ja e interpretuesit LiteRT që ekzekuton modelin e mësimit të makinerisë në pajisje përdor tensorë në formën e ByteBuffer, të cilat mund të jenë të vështira për t'u korrigjuar dhe manipuluar. Biblioteka e mbështetjes LiteRT Android është krijuar për të ndihmuar në përpunimin e hyrjes dhe daljes së modeleve LiteRT dhe për ta bërë më të lehtë për t'u përdorur përkthyesin LiteRT.

Fillimi

Importoni varësinë Gradle dhe cilësime të tjera

Kopjoni skedarin e modelit .tflite në drejtorinë e aseteve të modulit Android ku do të ekzekutohet modeli. Specifikoni që skedari të mos jetë i ngjeshur dhe shtoni bibliotekën LiteRT në skedarin build.gradle të modulit:

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

Eksploroni LiteRT Biblioteka e Mbështetjes AAR të pritur në MavenCentral për versione të ndryshme të Bibliotekës Mbështetëse.

Manipulimi dhe konvertimi bazë i imazhit

Biblioteka e Mbështetjes LiteRT ka një grup metodash bazë të manipulimit të imazhit, si prerja dhe ndryshimi i madhësisë. Për ta përdorur atë, krijoni një ImagePreprocessor dhe shtoni operacionet e kërkuara. Për të kthyer imazhin në formatin tensor të kërkuar nga interpretuesi LiteRT, krijoni një TensorImage që do të përdoret si hyrje:

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 të një tensori mund të lexohet përmes bibliotekës së nxjerrësit të meta të dhënave si dhe informacioneve të tjera të modelit.

Përpunimi bazë i të dhënave audio

Biblioteka e Mbështetjes LiteRT përcakton gjithashtu një klasë TensorAudio që mbështjell disa metoda bazë të përpunimit të të dhënave audio. Përdoret kryesisht së bashku me AudioRecord dhe kap mostrat e audios në një tampon zileje.

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

Krijoni objekte dalëse dhe ekzekutoni modelin

Para se të ekzekutojmë modelin, duhet të krijojmë objektet e kontejnerit që do të ruajnë rezultatin:

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

Ngarkimi i modelit dhe ekzekutimi i konkluzionit:

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

Qasja në rezultat

Zhvilluesit mund t'i qasen daljes drejtpërdrejt përmes probabilityBuffer.getFloatArray() . Nëse modeli prodhon një dalje të kuantizuar, mos harroni të konvertoni rezultatin. Për modelin e kuantizuar MobileNet, zhvilluesi duhet të ndajë çdo vlerë dalëse me 255 për të marrë probabilitetin që varion nga 0 (më pak e mundshme) në 1 (më e mundshme) për secilën kategori.

Opsionale: Hartimi i rezultateve në etiketa

Zhvilluesit gjithashtu mund t'i hartojnë sipas dëshirës rezultatet në etiketa. Së pari, kopjoni skedarin e tekstit që përmban etiketa në drejtorinë e aseteve të modulit. Më pas, ngarkoni skedarin e etiketës duke përdorur kodin e mëposhtëm:

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

Fragmenti i mëposhtëm tregon se si të lidhen probabilitetet me etiketat e kategorive:

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

Mbulimi aktual i rastit të përdorimit

Versioni aktual i Bibliotekës së Mbështetjes LiteRT mbulon:

  • Llojet e zakonshme të të dhënave (float, uint8, imazhe, audio dhe grup i këtyre objekteve) si hyrje dhe dalje të modeleve tflite.
  • operacionet bazë të imazhit (prerja e imazhit, ndryshimi i madhësisë dhe rrotullimi).
  • normalizimi dhe kuantizimi
  • skedarë utils

Versionet e ardhshme do të përmirësojnë mbështetjen për aplikacionet e lidhura me tekstin.

Arkitektura e procesorit të imazhit

Dizajni i ImageProcessor lejoi që operacionet e manipulimit të imazhit të përcaktoheshin përpara dhe të optimizoheshin gjatë procesit të ndërtimit. ImageProcessor aktualisht mbështet tre operacione bazë të parapërpunimit, siç përshkruhet në tre komentet në fragmentin e kodit më poshtë:

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

Shikoni më shumë detaje këtu rreth normalizimit dhe kuantizimit.

Qëllimi përfundimtar i bibliotekës mbështetëse është të mbështesë të gjitha transformimet tf.image . Kjo do të thotë se transformimi do të jetë i njëjtë me TensorFlow dhe zbatimi do të jetë i pavarur nga sistemi operativ.

Zhvilluesit janë gjithashtu të mirëpritur të krijojnë procesorë me porosi. Është e rëndësishme në këto raste që të përafrohet me procesin e trajnimit - dmth. i njëjti parapërpunim duhet të zbatohet si për trajnimin ashtu edhe për konkluzionet për të rritur riprodhueshmërinë.

Kuantizimi

Kur filloni objektet hyrëse ose dalëse si TensorImage ose TensorBuffer , duhet të specifikoni llojet e tyre që të jenë DataType.UINT8 ose DataType.FLOAT32 .

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

TensorProcessor mund të përdoret për të kuantizuar tensorët e hyrjes ose për të dekuantizuar tensorët e daljes. Për shembull, kur përpunon një dalje të kuantizuar TensorBuffer , zhvilluesi mund të përdorë DequantizeOp për të dekuantizuar rezultatin në një probabilitet me pikë lundruese midis 0 dhe 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);

Parametrat e kuantizimit të një tensori mund të lexohen përmes bibliotekës së nxjerrjes së meta të dhënave .