يتفاعل مطوّرو تطبيقات الأجهزة الجوّالة عادةً مع عناصر مكتوبة مثل صور نقطية أو أنواع أساسية مثل الأعداد الصحيحة. ومع ذلك، تستخدم واجهة برمجة التطبيقات الخاصة بمترجم 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 AAR المستضاف على MavenCentral لإصدارات مختلفة من Support Library.
التعامل مع الصور وتحويلها بشكل أساسي
تتضمّن مكتبة LiteRT Support Library مجموعة من الطرق الأساسية لمعالجة الصور، مثل الاقتصاص وتغيير الحجم. لاستخدامها، أنشئ 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 Support Library أيضًا فئة 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 Support Library ما يلي:
- أنواع البيانات الشائعة (مثل float وuint8 والصور والمقاطع الصوتية ومصفوفة هذه العناصر) كمدخلات ومخرجات لنماذج tflite.
- عمليات أساسية على الصور (اقتصاص الصورة وتغيير حجمها وتدويرها)
- التسوية والتقسيم إلى فئات
- أدوات الملفات
ستعمل الإصدارات المستقبلية على تحسين التوافق مع التطبيقات ذات الصلة بالنصوص.
بنية ImageProcessor
سمح تصميم 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);
يمكن قراءة مَعلمات التكميم لموتر من خلال مكتبة استخراج البيانات الوصفية.