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

يمكنك استخدام ML Kit لإجراء الاستدلال على الجهاز باستخدام نموذج TensorFlow Lite .

تتطلب واجهة برمجة التطبيقات هذه Android SDK المستوى 16 (Jelly Bean) أو أحدث.

قبل ان تبدأ

  1. إذا لم تكن قد قمت بذلك بالفعل، فأضف Firebase إلى مشروع Android الخاص بك .
  2. أضف تبعيات مكتبات 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'
    }
    
  3. قم بتحويل نموذج TensorFlow الذي تريد استخدامه إلى تنسيق TensorFlow Lite. راجع TOCO: محول TensorFlow Lite الأمثل .

قم باستضافة النموذج الخاص بك أو تجميعه

قبل أن تتمكن من استخدام نموذج TensorFlow Lite للاستدلال في تطبيقك، يجب عليك إتاحة النموذج لـ ML Kit. يمكن لـ ML Kit استخدام نماذج TensorFlow Lite المستضافة عن بعد باستخدام Firebase، أو المجمعة مع التطبيق الثنائي، أو كليهما.

من خلال استضافة نموذج على Firebase، يمكنك تحديث النموذج دون إصدار إصدار تطبيق جديد، ويمكنك استخدام Remote Config وA/B Testing لتقديم نماذج مختلفة ديناميكيًا لمجموعات مختلفة من المستخدمين.

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

من خلال تجميع النموذج الخاص بك مع تطبيقك، يمكنك التأكد من أن ميزات تعلم الآلة في تطبيقك لا تزال تعمل عندما لا يكون النموذج المستضاف في Firebase متاحًا.

استضافة النماذج على Firebase

لاستضافة نموذج TensorFlow Lite على Firebase:

  1. في قسم ML Kit بوحدة تحكم Firebase ، انقر فوق علامة التبويب مخصص .
  2. انقر فوق إضافة نموذج مخصص (أو إضافة نموذج آخر ).
  3. حدد اسمًا سيتم استخدامه لتحديد النموذج الخاص بك في مشروع Firebase الخاص بك، ثم قم بتحميل ملف نموذج TensorFlow Lite (ينتهي عادةً بـ .tflite أو .lite ).
  4. في بيان تطبيقك، أعلن أن إذن الإنترنت مطلوب:
    <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+KTX

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

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

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

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

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

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 بعدد وأبعاد ("الشكل") للصفائف التي يستخدمها نموذجك.

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

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 x224x224x3 من القيم float ، التي تمثل مجموعة من الصور ثلاثية القنوات (RGB) N 224x224، وتنتج كمخرجات قائمة من 1000 قيمة float ، تمثل كل منها احتمال أن تكون الصورة عضوًا في إحدى الفئات الـ 1000 التي يتنبأ بها النموذج.

بالنسبة لمثل هذا النموذج، يمكنك تكوين مدخلات وإخراج مترجم النموذج كما هو موضح أدناه:

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

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

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

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

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

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 المتسلسل القياسي في التخزين المحلي.

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

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

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