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

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

طُرز TensorFlow Lite

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

قبل البدء

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

    بالإضافة إلى ذلك، وكجزء من عملية إعداد أداة تنزيل نموذج تعلُّم الآلة من Firebase، يجب إضافة حزمة تطوير البرامج (SDK) TensorFlow Lite إلى تطبيقك.

    dependencies {
        // Import the BoM for the Firebase platform
        implementation(platform("com.google.firebase:firebase-bom:33.1.1"))
    
        // 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")
    }

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

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

    إذا اخترت عدم استخدام قائمة العناصر في Firebase، يجب تحديد كل إصدار من مكتبة 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:25.0.0")
    // Also add the dependency for the TensorFlow Lite library and specify its version implementation("org.tensorflow:tensorflow-lite:2.3.0")
    }
    هل تبحث عن وحدة مكتبة خاصة بلغة Kotlin؟ اعتبارًا من تشرين الأول (أكتوبر) 2023 (الإصدار 32.5.0 من Firebase)، أصبح بإمكان مطوّري لغة Kotlin وJava الاعتماد على وحدة المكتبة الرئيسية (لمعرفة التفاصيل، يُرجى الاطّلاع على الأسئلة الشائعة حول هذه المبادرة).
  3. يُرجى توضيح أنّ إذن INTERNET مطلوب في بيان تطبيقك:
    <uses-permission android:name="android.permission.INTERNET" />

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

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

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

2- تنزيل النموذج على الجهاز وإعداد أداة الترجمة الفورية من TensorFlow Lite

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

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

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

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

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

Kotlin+KTX

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+KTX

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+KTX

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+KTX

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، يخزّنها تعلُّم الآلة في Firebase بتنسيق النموذج الأوّلي المتسلسل العادي في مساحة التخزين المحلية.

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

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

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