يمكنك استخدام "حزمة تعلُّم الآلة" لإجراء الاستنتاج على الجهاز باستخدام نموذج TensorFlow Lite.
تتطلّب واجهة برمجة التطبيقات هذه استخدام الإصدار 16 من حزمة تطوير البرامج (SDK) لنظام التشغيل Android (Jelly Bean) أو إصدار أحدث.
قبل البدء
- أضِف Firebase إلى مشروع Android، في حال لم يسبق لك إجراء ذلك.
- أضِف التبعيات لمكتبات ML Kit لنظام التشغيل Android إلى ملف Gradle
(على مستوى التطبيق) للوحدة (عادةً
app/build.gradle
):apply plugin: 'com.android.application' apply plugin: 'com.google.gms.google-services' dependencies { // ... implementation 'com.google.firebase:firebase-ml-model-interpreter:22.0.3' }
- حوِّل نموذج TensorFlow الذي تريد استخدامه إلى تنسيق TensorFlow Lite. اطّلِع على TOCO: TensorFlow Lite Optimizing Converter.
استضافة النموذج أو تجميعه
قبل أن تتمكّن من استخدام نموذج TensorFlow Lite للاستنتاج في تطبيقك، عليك إتاحة النموذج لخدمة ML Kit. يمكن أن تستخدم حزمة ML Kit نماذج TensorFlow Lite المستضافة عن بُعد باستخدام Firebase أو المُدمجة مع ملف التطبيق الثنائي أو كليهما.
من خلال استضافة نموذج على Firebase، يمكنك تعديل النموذج بدون طرح إصدار جديد من التطبيق، ويمكنك استخدام Remote Config وA/B Testing لعرض نماذج مختلفة بشكل ديناميكي لمجموعات مختلفة من المستخدمين.
إذا اخترت توفير النموذج فقط من خلال استضافته باستخدام Firebase وعدم تجميعه مع تطبيقك، يمكنك تقليل حجم التنزيل الأولي لتطبيقك. يُرجى العِلم أنّه في حال عدم تجميع النموذج مع تطبيقك، لن تكون أي وظيفة ذات صلة بالنموذج متاحة إلى أن ينزِّل تطبيقك النموذج للمرة الأولى.
من خلال تجميع النموذج مع تطبيقك، يمكنك التأكّد من أنّ ميزات الذكاء الاصطناعي في تطبيقك ستظل تعمل حتى في حال عدم توفّر النموذج المستضاف على Firebase.
استضافة النماذج على Firebase
لاستضافة نموذج TensorFlow Lite على Firebase:
- في قسم ML Kit ضمن وحدة تحكّم Firebase، انقر على علامة التبويب مخصّص.
- انقر على إضافة نموذج مخصّص (أو إضافة نموذج آخر).
- حدِّد اسمًا سيتم استخدامه لتحديد النموذج في مشروعك على Firebase، ثم حمِّل ملف نموذج TensorFlow Lite (ينتهي عادةً ب
.tflite
أو.lite
). - في بيان تطبيقك، يجب الإفصاح عن أنّ إذن INTERNET مطلوب:
<uses-permission android:name="android.permission.INTERNET" />
بعد إضافة نموذج مخصّص إلى مشروعك على Firebase، يمكنك الإشارة إلى النموذج في تطبيقاتك باستخدام الاسم الذي حدّدته. يمكنك في أي وقت تحميل نموذج TensorFlow Lite جديد، وسينزِّل تطبيقك النموذج الجديد وي يبدأ استخدامه عند إعادة تشغيل التطبيق في المرة التالية. يمكنك تحديد شروط الجهاز المطلوبة لمحاولة تطبيقك تحديث النموذج (راجِع ما يلي).
تجميع النماذج مع تطبيق
لتجميع نموذج TensorFlow Lite مع تطبيقك، انسخ ملف النموذج (الذي ينتهي عادةً ب.tflite
أو .lite
) إلى مجلد assets/
في تطبيقك. (قد تحتاج إلى
إنشاء المجلد أولاً من خلال النقر بزر الماوس الأيمن على مجلد app/
، ثم النقر على
جديد > مجلد > مجلد مواد العرض).
بعد ذلك، أضِف ما يلي إلى ملف build.gradle
في تطبيقك لضمان عدم ضغط Gradle
للنماذج عند إنشاء التطبيق:
android {
// ...
aaptOptions {
noCompress "tflite" // Your model's file extension: "tflite", "lite", etc.
}
}
سيتم تضمين ملف النموذج في حِزمة التطبيق وسيتوفّر لمجموعة ML Kit بصفته مادة عرض أولية.
تحميل النموذج
لاستخدام نموذج TensorFlow Lite في تطبيقك، عليك أولاً ضبط حزمة ML Kit باستخدام المواقع الجغرافية التي يتوفّر فيها النموذج: عن بُعد باستخدام Firebase أو في مساحة التخزين المحلية أو كليهما. في حال تحديد نموذجَين محلي وخارجي، يمكنك استخدام النموذج الخارجي إذا كان متاحًا، والرجوع إلى النموذج المخزَّن على الجهاز إذا لم يكن النموذج الخارجي متاحًا.ضبط نموذج مستضاف على Firebase
إذا استضفت النموذج باستخدام Firebase، أنشئ FirebaseCustomRemoteModel
عنصرًا، مع تحديد الاسم الذي منحته للنموذج عند تحميله:
Java
FirebaseCustomRemoteModel remoteModel =
new FirebaseCustomRemoteModel.Builder("your_model").build();
Kotlin
val remoteModel = FirebaseCustomRemoteModel.Builder("your_model").build()
بعد ذلك، ابدأ مهمة تنزيل النموذج، مع تحديد الشروط التي تريد السماح بالتنزيل بموجبها. إذا لم يكن النموذج متوفّرًا على الجهاز أو إذا كان هناك إصدار أحدث من النموذج، ستنزِّل المهمة النموذج بشكل غير متزامن من Firebase:
Java
FirebaseModelDownloadConditions conditions = new FirebaseModelDownloadConditions.Builder()
.requireWifi()
.build();
FirebaseModelManager.getInstance().download(remoteModel, conditions)
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
// Success.
}
});
Kotlin
val conditions = FirebaseModelDownloadConditions.Builder()
.requireWifi()
.build()
FirebaseModelManager.getInstance().download(remoteModel, conditions)
.addOnCompleteListener {
// Success.
}
تبدأ العديد من التطبيقات مهمة التنزيل في رمز الإعداد، ولكن يمكنك انجامه في أي وقت قبل الحاجة إلى استخدام النموذج.
ضبط نموذج على الجهاز
إذا حزمت النموذج مع تطبيقك، أنشئ عنصر FirebaseCustomLocalModel
وحدِّد اسم ملف نموذج TensorFlow Lite:
Java
FirebaseCustomLocalModel localModel = new FirebaseCustomLocalModel.Builder()
.setAssetFilePath("your_model.tflite")
.build();
Kotlin
val localModel = FirebaseCustomLocalModel.Builder()
.setAssetFilePath("your_model.tflite")
.build()
إنشاء مترجم من النموذج
بعد ضبط مصادر النماذج، أنشئ عنصرًا FirebaseModelInterpreter
من أحدها.
إذا كان لديك نموذج مجمّع محليًا فقط، ما عليك سوى إنشاء مترجم من عنصر
FirebaseCustomLocalModel
:
Java
FirebaseModelInterpreter interpreter;
try {
FirebaseModelInterpreterOptions options =
new FirebaseModelInterpreterOptions.Builder(localModel).build();
interpreter = FirebaseModelInterpreter.getInstance(options);
} catch (FirebaseMLException e) {
// ...
}
Kotlin
val options = FirebaseModelInterpreterOptions.Builder(localModel).build()
val interpreter = FirebaseModelInterpreter.getInstance(options)
إذا كان لديك نموذج مستضاف عن بُعد، عليك التأكّد من أنّه
تم تنزيله قبل تشغيله. يمكنك التحقّق من حالة مهمة تنزيل النموذج
باستخدام طريقة isModelDownloaded()
في أداة إدارة النماذج.
على الرغم من أنّه عليك تأكيد ذلك فقط قبل تشغيل المُفسِّر، إذا كان لديك نموذج مستضاف عن بُعد ونموذج مجمَّع محليًا، قد يكون من المفيد إجراء هذا التحقّق عند إنشاء مثيل لمُفسِّر النموذج: أنشئ مفسِّرًا من النموذج عن بُعد إذا تم تنزيله، ومن النموذج المحلي في الحالات الأخرى.
Java
FirebaseModelManager.getInstance().isModelDownloaded(remoteModel)
.addOnSuccessListener(new OnSuccessListener<Boolean>() {
@Override
public void onSuccess(Boolean isDownloaded) {
FirebaseModelInterpreterOptions options;
if (isDownloaded) {
options = new FirebaseModelInterpreterOptions.Builder(remoteModel).build();
} else {
options = new FirebaseModelInterpreterOptions.Builder(localModel).build();
}
FirebaseModelInterpreter interpreter = FirebaseModelInterpreter.getInstance(options);
// ...
}
});
Kotlin
FirebaseModelManager.getInstance().isModelDownloaded(remoteModel)
.addOnSuccessListener { isDownloaded ->
val options =
if (isDownloaded) {
FirebaseModelInterpreterOptions.Builder(remoteModel).build()
} else {
FirebaseModelInterpreterOptions.Builder(localModel).build()
}
val interpreter = FirebaseModelInterpreter.getInstance(options)
}
إذا كان لديك نموذج مستضاف عن بُعد فقط، عليك إيقاف الوظائف المتعلقة بالنموذج، مثل حجب جزء من واجهة المستخدم أو إخفائه، إلى أن يتم تأكيد تنزيل النموذج. ويمكنك إجراء ذلك من خلال إرفاق مستمع
بطريقة download()
الخاصة بمدير النماذج:
Java
FirebaseModelManager.getInstance().download(remoteModel, conditions)
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void v) {
// Download complete. Depending on your app, you could enable
// the ML feature, or switch from the local model to the remote
// model, etc.
}
});
Kotlin
FirebaseModelManager.getInstance().download(remoteModel, conditions)
.addOnCompleteListener {
// Download complete. Depending on your app, you could enable the ML
// feature, or switch from the local model to the remote model, etc.
}
تحديد مدخلات النموذج ومخارجه
بعد ذلك، عليك ضبط تنسيقات الإدخال والإخراج لمُفسِّر النموذج.
يأخذ نموذج TensorFlow Lite صفيفًا واحدًا أو أكثر
بمتّجهات متعدّدة كمدخل وينتج صفيفًا واحدًا أو أكثر
بمتّجهات متعدّدة كمخرج. تحتوي هذه الصفائف على قيم byte
أو
int
أو long
أو float
. يجب
ضبط ML Kit بعدد المصفوفات التي يستخدمها
النموذج وأبعادها ("شكلها").
إذا لم تكن تعرف شكل الإدخال والإخراج لنموذجك ونوع البيانات، يمكنك استخدام مفسِّر Python في TensorFlow Lite لفحص النموذج. على سبيل المثال:
import tensorflow as tf interpreter = tf.lite.Interpreter(model_path="my_model.tflite") interpreter.allocate_tensors() # Print input shape and type print(interpreter.get_input_details()[0]['shape']) # Example: [1 224 224 3] print(interpreter.get_input_details()[0]['dtype']) # Example: <class 'numpy.float32'> # Print output shape and type print(interpreter.get_output_details()[0]['shape']) # Example: [1 1000] print(interpreter.get_output_details()[0]['dtype']) # Example: <class 'numpy.float32'>
بعد تحديد تنسيق إدخال النموذج وإخراجه، يمكنك
ضبط مفسِّر النموذج في تطبيقك من خلال إنشاء عنصر
FirebaseModelInputOutputOptions
.
على سبيل المثال، قد يأخذ نموذج تصنيف الصور بنقطة عائمة كمدخل Nصفيفًا بحجم 224×224×3 من قيم float
، يمثّل مجموعة منN صور بدقة 224×224 وثلاث قنوات (RGB)، وينتج كمخرج قائمة بقيمة float
1,000، يمثّل كلّ منها احتمال أن تكون الصورة عضوًا في
إحدى الفئات الـ 1,000 التي يتوقّعها النموذج.
بالنسبة إلى هذا النموذج، عليك ضبط إدخال وإخراج مفسّر النموذج كما هو موضّح أدناه:
Java
FirebaseModelInputOutputOptions inputOutputOptions = new FirebaseModelInputOutputOptions.Builder() .setInputFormat(0, FirebaseModelDataType.FLOAT32, new int[]{1, 224, 224, 3}) .setOutputFormat(0, FirebaseModelDataType.FLOAT32, new int[]{1, 5}) .build();
Kotlin
val inputOutputOptions = FirebaseModelInputOutputOptions.Builder() .setInputFormat(0, FirebaseModelDataType.FLOAT32, intArrayOf(1, 224, 224, 3)) .setOutputFormat(0, FirebaseModelDataType.FLOAT32, intArrayOf(1, 5)) .build()
إجراء استنتاجات على بيانات الإدخال
أخيرًا، لإجراء الاستنتاج باستخدام النموذج، احصل على بيانات الإدخال وأدخِل أيّ عمليات تحويل على البيانات اللازمة للحصول على صفيف إدخال بالشكل الصحيح لنموذجك.على سبيل المثال، إذا كان لديك نموذج لتصنيف الصور يتضمّن شكل إدخال من قيم النقطة العائمة
[1 224 224 3]، يمكنك إنشاء صفيف إدخال من عنصر
Bitmap
كما هو موضّح في المثال التالي:
Java
Bitmap bitmap = getYourInputImage(); bitmap = Bitmap.createScaledBitmap(bitmap, 224, 224, true); int batchNum = 0; float[][][][] input = new float[1][224][224][3]; for (int x = 0; x < 224; x++) { for (int y = 0; y < 224; y++) { int pixel = bitmap.getPixel(x, y); // Normalize channel values to [-1.0, 1.0]. This requirement varies by // model. For example, some models might require values to be normalized // to the range [0.0, 1.0] instead. input[batchNum][x][y][0] = (Color.red(pixel) - 127) / 128.0f; input[batchNum][x][y][1] = (Color.green(pixel) - 127) / 128.0f; input[batchNum][x][y][2] = (Color.blue(pixel) - 127) / 128.0f; } }
Kotlin
val bitmap = Bitmap.createScaledBitmap(yourInputImage, 224, 224, true) val batchNum = 0 val input = Array(1) { Array(224) { Array(224) { FloatArray(3) } } } for (x in 0..223) { for (y in 0..223) { val pixel = bitmap.getPixel(x, y) // Normalize channel values to [-1.0, 1.0]. This requirement varies by // model. For example, some models might require values to be normalized // to the range [0.0, 1.0] instead. input[batchNum][x][y][0] = (Color.red(pixel) - 127) / 255.0f input[batchNum][x][y][1] = (Color.green(pixel) - 127) / 255.0f input[batchNum][x][y][2] = (Color.blue(pixel) - 127) / 255.0f } }
بعد ذلك، أنشئ عنصر FirebaseModelInputs
باستخدام
بيانات الإدخال، ثم مرِّره مع مواصفات إدخال وإخراج النموذج إلى run
في
مُفسر النموذج:
Java
FirebaseModelInputs inputs = new FirebaseModelInputs.Builder() .add(input) // add() as many input arrays as your model requires .build(); firebaseInterpreter.run(inputs, inputOutputOptions) .addOnSuccessListener( new OnSuccessListener<FirebaseModelOutputs>() { @Override public void onSuccess(FirebaseModelOutputs result) { // ... } }) .addOnFailureListener( new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });
Kotlin
val inputs = FirebaseModelInputs.Builder() .add(input) // add() as many input arrays as your model requires .build() firebaseInterpreter.run(inputs, inputOutputOptions) .addOnSuccessListener { result -> // ... } .addOnFailureListener { e -> // Task failed with an exception // ... }
في حال نجاح المكالمة، يمكنك الحصول على الإخراج من خلال استدعاء طريقة getOutput()
للعنصر الذي يتم تمريره إلى مستمع الحدث الناجح. على سبيل المثال:
Java
float[][] output = result.getOutput(0); float[] probabilities = output[0];
Kotlin
val output = result.getOutput<Array<FloatArray>>(0) val probabilities = output[0]
تعتمد طريقة استخدام الإخراج على النموذج الذي تستخدمه.
على سبيل المثال، إذا كنت تُجري عملية تصنيف، يمكنك في الخطوة التالية ربط فهارس النتيجة بالتسميات التي تمثّلها:
Java
BufferedReader reader = new BufferedReader( new InputStreamReader(getAssets().open("retrained_labels.txt"))); for (int i = 0; i < probabilities.length; i++) { String label = reader.readLine(); Log.i("MLKit", String.format("%s: %1.4f", label, probabilities[i])); }
Kotlin
val reader = BufferedReader( InputStreamReader(assets.open("retrained_labels.txt"))) for (i in probabilities.indices) { val label = reader.readLine() Log.i("MLKit", String.format("%s: %1.4f", label, probabilities[i])) }
الملحق: أمان النماذج
بغض النظر عن الطريقة التي توفّر بها نماذج TensorFlow Lite لتطبيق ML Kit، تخزِّنها أداة ML Kit بتنسيق protobuf التسلسلي العادي في مساحة التخزين المحلية.
من الناحية النظرية، يعني ذلك أنّه يمكن لأي شخص نسخ النموذج الخاص بك. ومع ذلك، في الممارسة، تكون معظم النماذج خاصة بالتطبيق ومموَّهة للغاية بسبب التحسينات، ما يجعل الخطر مشابهًا لخطر قيام المنافسين بتفكيك رمزك البرمجي وإعادة استخدامه. ومع ذلك، يجب أن تكون على دراية بهذا الخطر قبل استخدام نموذج مخصّص في تطبيقك.
في الإصدار 21 من واجهة برمجة التطبيقات لنظام التشغيل Android (Lollipop) والإصدارات الأحدث، يتم تنزيل النموذج إلى ملف تعريف تم استبعاده من ميزة "الاحتفاظ بنسخة احتياطية تلقائية".
في الإصدار 20 من Android API والإصدارات الأقدم، يتم تنزيل النموذج إلى دليل
باسم com.google.firebase.ml.custom.models
في وحدة التخزين الداخلية
الخاصة بالتطبيق. إذا فعّلت ميزة "الاحتفاظ بنسخة احتياطية" من الملفات باستخدام BackupAgent
،
يمكنك اختيار استبعاد هذا الدليل.