معالجة بيانات الإدخال والإخراج باستخدام مكتبة دعم LiteRT

يتفاعل مطورو تطبيقات الأجهزة المحمولة عادةً مع الكائنات المكتوبة مثل الصور النقطية أو العناصر الأساسية مثل الأعداد الصحيحة. ومع ذلك، يمكن للمترجم الفوري تستخدم واجهة برمجة التطبيقات التي تشغّل نموذج تعلُّم الآلة على الجهاز ملفات العشرات في شكل ByteBuffer الذي قد يكون من الصعب تصحيح أخطائه ومعالجتها. تشير رسالة الأشكال البيانية مكتبة دعم LiteRT Android للمساعدة في معالجة مدخلات ومخرجات نماذج LiteRT، لجعل مترجم LiteRT أسهل في الاستخدام.

البدء

استيراد تبعية Gradle وإعدادات أخرى

انسخ ملف النموذج .tflite إلى دليل مواد العرض في وحدة Android. ومكان تطبيق النموذج. حدِّد أنّه يجب عدم ضغط الملف. إضافة مكتبة LiteRT إلى ملف 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 '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'
}

استكشاف مكتبة دعم LiteRT Support Library والتي تمت استضافتها على MavenCentral للإصدارات المختلفة من Support Library.

معالجة الصور الأساسية وتحويلها

تضم مكتبة دعم LiteRT مجموعة من تقنيات معالجة الصور الأساسية طرق مثل الاقتصاص وتغيير الحجم. لاستخدامها، يجب إنشاء ImagePreprocessor وإضافة العمليات المطلوبة. لتحويل الصورة إلى تنسيق Tenor الذي يتطلبه مترجم LiteRT، أنشئ TensorImage ليتم استخدامه كإدخال:

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 لموتر متوتر من خلال مكتبة أداة استخراج البيانات الوصفية بالإضافة إلى معلومات النموذج الأخرى.

المعالجة الأساسية للبيانات الصوتية

تحدِّد مكتبة دعم LiteRT أيضًا التفاف فئة TensorAudio. بعض الطرق الأساسية لمعالجة البيانات الصوتية. يتم استخدامه في الغالب مع AudioRecord وتلتقط العيّنات الصوتية في مخزن مؤقت دائري.

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

إنشاء عناصر إخراج وتنفيذ النموذج

قبل تشغيل النموذج، نحتاج إلى إنشاء كائنات الحاوية التي تخزين النتيجة:

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

تحميل النموذج وتنفيذ الاستنتاج:

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

الوصول إلى النتيجة

يمكن للمطوّرين الوصول إلى النتائج مباشرةً من خلال probabilityBuffer.getFloatArray() إذا أنتج النموذج مخرجًا كميًا، تذكر تحويل النتيجة. وبالنسبة إلى نموذج MobileNet الكمي، يسعى المطوّر إلى قسمة كل قيمة ناتج على 255 للحصول على احتمالية تتراوح من 0 (الأقل احتمالاً) إلى 1 (المرجّح أكثر) لكل فئة.

اختياري: ربط النتائج بالتصنيفات

يمكن للمطوّرين أيضًا ربط النتائج بالتصنيفات بشكل اختياري. أولاً، انسخ النص ملف يحتوي على تصنيفات في دليل أصول الوحدة. بعد ذلك، قم بتحميل التصنيف ملف باستخدام التعليمة البرمجية التالية:

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

يوضح المقتطف التالي كيفية ربط الاحتمالات بـ تسميات الفئات:

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

تغطية حالات الاستخدام الحالية

يشمل الإصدار الحالي من مكتبة دعم LiteRT ما يلي:

  • أنواع البيانات الشائعة (float، وuint8، والصور، والصوت، وصفيف هذه الكائنات) كمدخلات ومخرجات لنماذج tflite.
  • العمليات الأساسية للصورة (اقتصاص الصورة وتغيير الحجم والتدوير).
  • تسوية وكمية
  • ملفات utils

ستعمل الإصدارات المستقبلية على تحسين دعم التطبيقات المتعلقة بالنص.

بنية معالج الصور

أتاح تصميم ImageProcessor لعمليات معالجة الصور مقدمًا وتحسينها خلال عملية الإنشاء. ImageProcessor في الوقت الحالي ثلاث عمليات أساسية للمعالجة المسبقة، كما هو موضح في قسم ثلاثة تعليقات في مقتطف الرمز أدناه:

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

الاطّلاع على المزيد من التفاصيل هنا حول للتسوية والتحديد الكمي.

إن الهدف النهائي من مكتبة الدعم هو دعم جميع tf.image والتحولات. وهذا يعني أنّ التحويل سيكون مثل TensorFlow. وسيكون التنفيذ مستقلاً عن نظام التشغيل.

نرحّب أيضًا بالمطوّرين لإنشاء معالجات مخصَّصة. من المهم في هذه الحالات تتماشى مع عملية التدريب - أي يجب أن تنطبق المعالجة المسبقة على كل من التدريب والاستنتاج لزيادة قابلية التجدد.

الكمية

عند بدء عناصر الإدخال أو الإخراج، مثل TensorImage أو TensorBuffer يجب تحديد أنواعها على أن تكون DataType.UINT8 أو DataType.FLOAT32.

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

يمكن استخدام TensorProcessor لتحديد قياسات المدخلات أو إزالة كمية المخرجات. وأجهزة تنس. على سبيل المثال، عند معالجة ناتج كمي TensorBuffer، يمكن للمطوِّر استخدام DequantizeOp لتحديد كمية النتيجة إلى نقطة عائمة. الاحتمالية بين 0 و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);

يمكن قراءة معاملات الكمي لمتينور من خلال مكتبة أداة استخراج البيانات الوصفية.