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

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

نماذج TensorFlow لايت

نماذج TensorFlow Lite هي نماذج ML تم تحسينها للتشغيل على الأجهزة المحمولة. للحصول على نموذج TensorFlow Lite:

قبل ان تبدأ

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

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

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

1. انشر النموذج الخاص بك

انشر نماذج TensorFlow المخصصة لديك باستخدام إما وحدة تحكم Firebase أو Firebase Admin Python وNode.js SDKs. راجع نشر النماذج المخصصة وإدارتها .

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

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

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

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

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

نوع التنزيل وصف
LOCAL_MODEL احصل على النموذج المحلي من الجهاز. إذا لم يكن هناك نموذج محلي متاح، فسيتصرف مثل LATEST_MODEL . استخدم نوع التنزيل هذا إذا لم تكن مهتمًا بالتحقق من تحديثات النموذج. على سبيل المثال، أنت تستخدم Remote Config لاسترداد أسماء النماذج وتقوم دائمًا بتحميل النماذج بأسماء جديدة (مستحسن).
LOCAL_MODEL_UPDATE_IN_BACKGROUND احصل على النموذج المحلي من الجهاز وابدأ في تحديث النموذج في الخلفية. إذا لم يكن هناك نموذج محلي متاح، فسيتصرف مثل 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 لفحص النموذج الخاص بك. على سبيل المثال:

بايثون

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 ML، فإن Firebase ML يخزنها بتنسيق protobuf المتسلسل القياسي في التخزين المحلي.

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

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

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