داده های ورودی و خروجی را با کتابخانه پشتیبانی LiteRT پردازش کنید

توسعه‌دهندگان برنامه‌های موبایل معمولاً با اشیاء تایپ‌شده مانند بیت‌مپ‌ها یا مقادیر اولیه مانند اعداد صحیح تعامل دارند. با این حال، API مفسر LiteRT که مدل یادگیری ماشین روی دستگاه را اجرا می‌کند، از تانسورهایی به شکل ByteBuffer استفاده می‌کند که اشکال‌زدایی و دستکاری آن می‌تواند دشوار باشد. کتابخانه پشتیبانی اندروید LiteRT برای کمک به پردازش ورودی و خروجی مدل‌های LiteRT طراحی شده است و استفاده از مفسر LiteRT را آسان‌تر می‌کند.

شروع به کار

وارد کردن وابستگی Gradle و سایر تنظیمات

فایل مدل .tflite را در دایرکتوری assets ماژول اندروید، جایی که مدل اجرا خواهد شد، کپی کنید. مشخص کنید که فایل نباید فشرده شود و کتابخانه 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 AAR که در MavenCentral میزبانی می‌شود را بررسی کنید.

دستکاری و تبدیل تصویر پایه

کتابخانه پشتیبانی LiteRT مجموعه‌ای از روش‌های اولیه دستکاری تصویر مانند برش و تغییر اندازه را دارد. برای استفاده از آن، یک ImagePreprocessor ایجاد کرده و عملیات مورد نیاز را اضافه کنید. برای تبدیل تصویر به فرمت تانسور مورد نیاز مفسر 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، توسعه‌دهنده باید هر مقدار خروجی را بر ۲۵۵ تقسیم کند تا احتمال را از ۰ (کمترین احتمال) تا ۱ (بیشترین احتمال) برای هر دسته به دست آورد.

اختیاری: نگاشت نتایج به برچسب‌ها

توسعه‌دهندگان همچنین می‌توانند به صورت اختیاری نتایج را به برچسب‌ها نگاشت کنند. ابتدا، فایل متنی حاوی برچسب‌ها را در دایرکتوری assets ماژول کپی کنید. سپس، فایل برچسب را با استفاده از کد زیر بارگذاری کنید:

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 موارد زیر را پوشش می‌دهد:

  • انواع داده‌های رایج (اعداد اعشاری، uint8، تصاویر، صدا و آرایه‌ای از این اشیاء) به عنوان ورودی‌ها و خروجی‌های مدل‌های tflite.
  • عملیات اولیه تصویر (برش تصویر، تغییر اندازه و چرخش).
  • نرمال‌سازی و کوانتیزاسیون
  • ابزارهای فایل

نسخه‌های آینده پشتیبانی از برنامه‌های مرتبط با متن را بهبود خواهند بخشید.

معماری پردازشگر تصویر

طراحی 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 برای دکوانتیزه کردن نتیجه به یک احتمال ممیز شناور بین ۰ و ۱ استفاده کند:

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

پارامترهای کوانتیزاسیون یک تانسور را می‌توان از طریق کتابخانه استخراج‌کننده فراداده خواند.