استخدام نموذج TensorFlow Lite المخصّص على Android

إذا كان تطبيقك يستخدم نماذج TensorFlow Lite مخصّصة، يمكنك استخدام Firebase ML لنشر نماذجك. من خلال نشر النماذج باستخدام Firebase، يمكنك تقليل حجم التنزيل الأولي لـ تطبيقك وتحديث نماذج تعلُّم الآلة في تطبيقك بدون إصدار إصدار جديد لـ تطبيقك. وباستخدام Remote Config وA/B Testing، يمكنك عرض نماذج مختلفة بشكل ديناميكي لمجموعات مختلفة من المستخدمين.

نماذج TensorFlow Lite

نماذج TensorFlow Lite هي نماذج تعلُّم آلة تم تحسينها للعمل على الأجهزة الجوّالة. للحصول على نموذج TensorFlow Lite:

قبل البدء

  1. أضِف Firebase إلى مشروع Android الخاص بك، في حال لم يسبق لك إجراء ذلك، .
  2. في ملف Gradle للوحدة (على مستوى التطبيق) (عادةً ما يكون <project>/<app-module>/build.gradle.kts أو <project>/<app-module>/build.gradle)، أضِف الاعتمادية لمكتبة أداة تنزيل نماذج Firebase ML لنظام التشغيل Android. ننصحك باستخدام الـ Firebase Android BoM للتحكّم في تحديد إصدار المكتبة.

    كجزء من إعداد أداة تنزيل نماذج Firebase ML، عليك أيضًا إضافة حزمة TensorFlow Lite SDK إلى تطبيقك.

    dependencies {
        // Import the BoM for the Firebase platform
        implementation(platform("com.google.firebase:firebase-bom:34.13.0"))
    
        // Add the dependency for the Firebase ML model downloader library
        // When using the BoM, you don't specify versions in Firebase library dependencies
        implementation("com.google.firebase:firebase-ml-modeldownloader")
    // Also add the dependency for the TensorFlow Lite library and specify its version implementation("org.tensorflow:tensorflow-lite:2.3.0")
    }

    باستخدام Firebase Android BoM، سيستخدم تطبيقك دائمًا إصدارات متوافقة من مكتبات Firebase لنظام التشغيل Android.

    (حلّ بديل)  إضافة تبعيات مكتبة Firebase بدون استخدام BoM

    إذا اخترت عدم استخدام Firebase BoM، عليك تحديد إصدار كل مكتبة من مكتبات Firebase في سطر التبعية الخاص بها.

    يُرجى العِلم أنّه إذا كنت تستخدم مكتبات متعدّدة من Firebase في تطبيقك، ننصحك بشدة باستخدام BoM لإدارة إصدارات المكتبات، ما يضمن توافق جميع الإصدارات.

    dependencies {
        // Add the dependency for the Firebase ML model downloader library
        // When NOT using the BoM, you must specify versions in Firebase library dependencies
        implementation("com.google.firebase:firebase-ml-modeldownloader:26.0.2")
    // Also add the dependency for the TensorFlow Lite library and specify its version implementation("org.tensorflow:tensorflow-lite:2.3.0")
    }
  3. في بيان تطبيقك، عليك الإفصاح عن أنّ إذن الوصول إلى الإنترنت (INTERNET) مطلوب:
    <uses-permission android:name="android.permission.INTERNET" />

1. نشر النموذج

يمكنك نشر نماذج TensorFlow المخصّصة باستخدام إما وحدة تحكّم Firebase أو حِزم Firebase Admin SDK بلغتَي Python وNode.js. يُرجى الاطّلاع على مقالة نشر النماذج المخصّصة وإدارتها.

بعد إضافة نموذج مخصّص إلى مشروع Firebase، يمكنك الإشارة إلى الـ نموذج في تطبيقاتك باستخدام الاسم الذي حدّدته. في أي وقت، يمكنك نشر نموذج TensorFlow Lite جديد وتنزيل النموذج الجديد على أجهزة المستخدمين من خلال استدعاء getModel() (راجِع أدناه).

2. تنزيل النموذج على الجهاز وتهيئة مفسّر TensorFlow Lite

لاستخدام نموذج TensorFlow Lite في تطبيقك، عليك أولاً استخدام Firebase ML SDK لتنزيل أحدث إصدار من النموذج على الجهاز. بعد ذلك، يمكنك إنشاء نسخة من مفسّر TensorFlow Lite باستخدام النموذج.

لبدء تنزيل النموذج، استدعِ طريقة getModel() لأداة تنزيل النماذج، مع تحديد الاسم الذي خصّصته للنموذج عند تحميله، وما إذا كنت تريد تنزيل أحدث نموذج دائمًا، والشروط التي تريد السماح بموجبها بالتنزيل.

يمكنك الاختيار من بين ثلاثة سلوكيات للتنزيل:

نوع التنزيل الوصف
LOCAL_MODEL الحصول على النموذج المحلي من الجهاز إذا لم يكن هناك نموذج محلي متاح، سيتصرف هذا الخيار مثل LATEST_MODEL. استخدِم هذا النوع من التنزيل إذا لم تكن مهتمًا بالتحقّق من توفّر تحديثات للنموذج. على سبيل المثال، أنت تستخدم ميزة "الإعداد عن بُعد" لاسترداد أسماء النماذج وتحمّل النماذج دائمًا بأسماء جديدة (ننصحك بذلك).
LOCAL_MODEL_UPDATE_IN_BACKGROUND الحصول على النموذج المحلي من الجهاز وبدء تحديث النموذج في الخلفية إذا لم يكن هناك نموذج محلي متاح، سيتصرف هذا الخيار مثل LATEST_MODEL.
LATEST_MODEL الحصول على أحدث نموذج إذا كان النموذج المحلي هو أحدث إصدار، سيتم عرض النموذج المحلي. بخلاف ذلك، سيتم تنزيل أحدث نموذج. سيتم حظر هذا السلوك إلى أن يتم تنزيل أحدث إصدار (لا ننصحك بذلك). لا تستخدِم هذا السلوك إلا في الحالات التي تحتاج فيها صراحةً إلى أحدث إصدار.

عليك إيقاف الوظائف المتعلقة بالنموذج، مثلاً، إخفاء جزء من واجهة المستخدم أو عرضه باللون الرمادي، إلى أن تتأكد من تنزيل النموذج.

Kotlin

val conditions = CustomModelDownloadConditions.Builder()
        .requireWifi()  // Also possible: .requireCharging() and .requireDeviceIdle()
        .build()
FirebaseModelDownloader.getInstance()
        .getModel("your_model", DownloadType.LOCAL_MODEL_UPDATE_IN_BACKGROUND,
            conditions)
        .addOnSuccessListener { model: CustomModel? ->
            // Download complete. Depending on your app, you could enable the ML
            // feature, or switch from the local model to the remote model, etc.

            // The CustomModel object contains the local path of the model file,
            // which you can use to instantiate a TensorFlow Lite interpreter.
            val modelFile = model?.file
            if (modelFile != null) {
                interpreter = Interpreter(modelFile)
            }
        }

Java

CustomModelDownloadConditions conditions = new CustomModelDownloadConditions.Builder()
    .requireWifi()  // Also possible: .requireCharging() and .requireDeviceIdle()
    .build();
FirebaseModelDownloader.getInstance()
    .getModel("your_model", DownloadType.LOCAL_MODEL_UPDATE_IN_BACKGROUND, conditions)
    .addOnSuccessListener(new OnSuccessListener<CustomModel>() {
      @Override
      public void onSuccess(CustomModel model) {
        // Download complete. Depending on your app, you could enable the ML
        // feature, or switch from the local model to the remote model, etc.

        // The CustomModel object contains the local path of the model file,
        // which you can use to instantiate a TensorFlow Lite interpreter.
        File modelFile = model.getFile();
        if (modelFile != null) {
            interpreter = new Interpreter(modelFile);
        }
      }
    });

تبدأ العديد من التطبيقات مهمة التنزيل في رمز التهيئة، ولكن يمكنك إجراء ذلك في أي وقت قبل أن تحتاج إلى استخدام النموذج.

3. إجراء الاستدلال على بيانات الإدخال

الحصول على أشكال الإدخال والإخراج للنموذج

يأخذ مفسّر نموذج TensorFlow Lite كمُدخل ويُنتج كمُخرج مصفوفة واحدة أو أكثر من المصفوفات المتعدّدة الأبعاد. تحتوي هذه المصفوفات على قيم byte أو int أو long أو float. قبل أن تتمكّن من تمرير البيانات إلى نموذج أو استخدام نتيجته، عليك معرفة عدد المصفوفات التي يستخدمها نموذجك وأبعادها ("شكلها").

إذا أنشأت النموذج بنفسك، أو إذا تم توثيق تنسيق الإدخال والإخراج للنموذج، قد تكون لديك هذه المعلومات. إذا كنت لا تعرف الـ شكل ونوع بيانات الإدخال والإخراج للنموذج، يمكنك استخدام الـ مفسّر TensorFlow Lite لفحص النموذج. على سبيل المثال:

Python

import tensorflow as tf

interpreter = tf.lite.Interpreter(model_path="your_model.tflite")
interpreter.allocate_tensors()

# Print input shape and type
inputs = interpreter.get_input_details()
print('{} input(s):'.format(len(inputs)))
for i in range(0, len(inputs)):
    print('{} {}'.format(inputs[i]['shape'], inputs[i]['dtype']))

# Print output shape and type
outputs = interpreter.get_output_details()
print('\n{} output(s):'.format(len(outputs)))
for i in range(0, len(outputs)):
    print('{} {}'.format(outputs[i]['shape'], outputs[i]['dtype']))

مثال على الإخراج:

1 input(s):
[  1 224 224   3] <class 'numpy.float32'>

1 output(s):
[1 1000] <class 'numpy.float32'>

تشغيل المفسّر

بعد تحديد تنسيق الإدخال والإخراج للنموذج، احصل على بيانات الإدخال وأجرِ أي عمليات تحويل على البيانات الضرورية للحصول على إدخال بالشكل المناسب لنموذجك.

على سبيل المثال، إذا كان لديك نموذج لتصنيف الصور بشكل الإدخال [1 224 224 3] لقيم الفاصلة العائمة، يمكنك إنشاء إدخال ByteBuffer من كائن Bitmap كما هو موضّح في المثال التالي:

Kotlin

val bitmap = Bitmap.createScaledBitmap(yourInputImage, 224, 224, true)
val input = ByteBuffer.allocateDirect(224*224*3*4).order(ByteOrder.nativeOrder())
for (y in 0 until 224) {
    for (x in 0 until 224) {
        val px = bitmap.getPixel(x, y)

        // Get channel values from the pixel value.
        val r = Color.red(px)
        val g = Color.green(px)
        val b = Color.blue(px)

        // Normalize channel values to [-1.0, 1.0]. This requirement depends on the model.
        // For example, some models might require values to be normalized to the range
        // [0.0, 1.0] instead.
        val rf = (r - 127) / 255f
        val gf = (g - 127) / 255f
        val bf = (b - 127) / 255f

        input.putFloat(rf)
        input.putFloat(gf)
        input.putFloat(bf)
    }
}

Java

Bitmap bitmap = Bitmap.createScaledBitmap(yourInputImage, 224, 224, true);
ByteBuffer input = ByteBuffer.allocateDirect(224 * 224 * 3 * 4).order(ByteOrder.nativeOrder());
for (int y = 0; y < 224; y++) {
    for (int x = 0; x < 224; x++) {
        int px = bitmap.getPixel(x, y);

        // Get channel values from the pixel value.
        int r = Color.red(px);
        int g = Color.green(px);
        int b = Color.blue(px);

        // Normalize channel values to [-1.0, 1.0]. This requirement depends
        // on the model. For example, some models might require values to be
        // normalized to the range [0.0, 1.0] instead.
        float rf = (r - 127) / 255.0f;
        float gf = (g - 127) / 255.0f;
        float bf = (b - 127) / 255.0f;

        input.putFloat(rf);
        input.putFloat(gf);
        input.putFloat(bf);
    }
}

بعد ذلك، خصِّص ByteBuffer كبيرًا بما يكفي لاحتواء ناتج النموذج، ومرِّر مخزن الإدخال ومخزن الإخراج إلى طريقة run() لمفسّر TensorFlow Lite. على سبيل المثال، بالنسبة إلى شكل الإخراج [1 1000] لقيم الفاصلة العائمة:

Kotlin

val bufferSize = 1000 * java.lang.Float.SIZE / java.lang.Byte.SIZE
val modelOutput = ByteBuffer.allocateDirect(bufferSize).order(ByteOrder.nativeOrder())
interpreter?.run(input, modelOutput)

Java

int bufferSize = 1000 * java.lang.Float.SIZE / java.lang.Byte.SIZE;
ByteBuffer modelOutput = ByteBuffer.allocateDirect(bufferSize).order(ByteOrder.nativeOrder());
interpreter.run(input, modelOutput);

تعتمد طريقة استخدام الإخراج على النموذج الذي تستخدمه.

على سبيل المثال، إذا كنت تجري عملية تصنيف، يمكنك في الخطوة التالية ربط فهارس النتيجة بالتصنيفات التي تمثّلها:

Kotlin

modelOutput.rewind()
val probabilities = modelOutput.asFloatBuffer()
try {
    val reader = BufferedReader(
            InputStreamReader(assets.open("custom_labels.txt")))
    for (i in probabilities.capacity()) {
        val label: String = reader.readLine()
        val probability = probabilities.get(i)
        println("$label: $probability")
    }
} catch (e: IOException) {
    // File not found?
}

Java

modelOutput.rewind();
FloatBuffer probabilities = modelOutput.asFloatBuffer();
try {
    BufferedReader reader = new BufferedReader(
            new InputStreamReader(getAssets().open("custom_labels.txt")));
    for (int i = 0; i < probabilities.capacity(); i++) {
        String label = reader.readLine();
        float probability = probabilities.get(i);
        Log.i(TAG, String.format("%s: %1.4f", label, probability));
    }
} catch (IOException e) {
    // File not found?
}

الملحق: أمان النموذج

بغض النظر عن كيفية إتاحة نماذج TensorFlow Lite لـ Firebase ML، يخزّنها Firebase ML بتنسيق protobuf التسلسلي العادي في وحدة التخزين المحلية.

من الناحية النظرية، يعني ذلك أنّه يمكن لأي شخص نسخ النموذج. ومع ذلك، من الناحية العملية، تكون معظم النماذج خاصة بالتطبيق إلى حد كبير ويتم إخفاؤها من خلال عمليات التحسين، ما يجعل المخاطر مماثلة لمخاطر تفكيك المنافسين للرمز وإعادة استخدامه. ومع ذلك، عليك أن تكون على دراية بهذا الخطر قبل استخدام نموذج مخصّص في تطبيقك.

على المستوى 21 من واجهة برمجة التطبيقات لنظام التشغيل Android (Lollipop) والإصدارات الأحدث، يتم تنزيل النموذج إلى دليل مستبعَد من النسخ الاحتياطي التلقائي.

على المستوى 20 من واجهة برمجة التطبيقات لنظام التشغيل Android والإصدارات الأقدم، يتم تنزيل النموذج إلى دليل باسم com.google.firebase.ml.custom.models في وحدة التخزين الداخلية الخاصة بالتطبيق. إذا فعّلت ميزة النسخ الاحتياطي للملفات باستخدام BackupAgent، يمكنك اختيار استبعاد هذا الدليل.