استخدام نموذج TensorFlow Lite للاستنتاج باستخدام "مجموعة أدوات تعلُّم الآلة" على Android

يمكنك استخدام أدوات تعلُّم الآلة لإجراء استنتاج على الجهاز باستخدام TensorFlow Lite.

وتتطلّب واجهة برمجة التطبيقات هذه المستوى 16 من حزمة تطوير البرامج (SDK) لنظام التشغيل Android (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 Adapter.

استضافة النموذج أو تجميعه

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

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

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

يمكنك ضمان ميزات تعلُّم الآلة في تطبيقك من خلال دمج نموذجك مع تطبيقك. لا يزال يعمل في حال عدم توفُّر النموذج المستضاف على Firebase.

نماذج المضيف على Firebase

لاستضافة نموذج TensorFlow Lite على Firebase، اتّبِع الخطوات التالية:

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

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

تحميل النموذج

لاستخدام نموذج TensorFlow Lite في تطبيقك، عليك أولاً ضبط حزمة تعلّم الآلة باستخدام المواقع التي يتوفر فيها نموذجك: استخدام 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. يجب اضبط حزمة تعلّم الآلة باستخدام عدد وأبعاد ("شكل") الصفائف استخدامات النموذج.

إذا كنت لا تعرف شكل ونوع البيانات لمدخلات النموذج ومخرجاته، يمكنك استخدام مترجم 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.

على سبيل المثال، قد يتخذ نموذج تصنيف الصور ذات النقطة العائمة كمدخل مصفوفة Nx224x224x3 مكونة من float قيمة، تمثل مجموعة من N صور ثلاثية القنوات (RGB) مقاس 224×224، ويتم إنتاج قائمة بالإخراج 1, 000 قيمة 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 القياسي القياسي في التخزين المحلي.

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

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

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