از یک مدل TensorFlow Lite برای استنباط با ML Kit در اندروید استفاده کنید

می‌توانید از ML Kit برای استنتاج روی دستگاه با مدل TensorFlow Lite استفاده کنید.

این API به Android SDK سطح 16 (Jelly Bean) یا جدیدتر نیاز دارد.

قبل از اینکه شروع کنی

  1. اگر قبلاً این کار را نکرده‌اید، Firebase را به پروژه Android خود اضافه کنید .
  2. وابستگی های کتابخانه های اندروید ML Kit را به فایل 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 Optimizing Converter مراجعه کنید.

مدل خود را میزبان یا بسته بندی کنید

قبل از اینکه بتوانید از یک مدل TensorFlow Lite برای استنباط در برنامه خود استفاده کنید، باید مدل را برای ML Kit در دسترس قرار دهید. ML Kit می‌تواند از مدل‌های TensorFlow Lite که از راه دور با استفاده از Firebase میزبانی می‌شوند، همراه با برنامه باینری یا هر دو استفاده کند.

با میزبانی یک مدل در Firebase، می‌توانید مدل را بدون انتشار نسخه جدید برنامه به‌روزرسانی کنید و می‌توانید از Remote Config و A/B Testing برای ارائه پویا مدل‌های مختلف به مجموعه‌های مختلف کاربران استفاده کنید.

اگر ترجیح می‌دهید مدل را فقط با میزبانی آن در Firebase ارائه دهید، و آن را با برنامه خود همراه نکنید، می‌توانید حجم دانلود اولیه برنامه خود را کاهش دهید. البته به خاطر داشته باشید که اگر مدل با برنامه شما همراه نباشد، تا زمانی که برنامه شما برای اولین بار مدل را دانلود نکند، هیچ عملکرد مرتبط با مدل در دسترس نخواهد بود.

با بسته‌بندی مدل خود با برنامه‌تان، می‌توانید مطمئن شوید که ویژگی‌های ML برنامه‌تان همچنان در زمانی که مدل میزبانی شده توسط Firebase در دسترس نیست، کار می‌کنند.

مدل های میزبان در Firebase

برای میزبانی مدل TensorFlow Lite خود در Firebase:

  1. در بخش ML Kit کنسول Firebase ، روی برگه Custom کلیک کنید.
  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/ پوشه، سپس روی New > Folder > Assets Folder، پوشه را ایجاد کنید.)

سپس موارد زیر را به فایل 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 آنها را در قالب استاندارد پروتوباف سریالی در حافظه محلی ذخیره می‌کند.

در تئوری، این بدان معنی است که هر کسی می تواند مدل شما را کپی کند. با این حال، در عمل، بیشتر مدل‌ها به قدری برنامه‌های کاربردی خاص هستند و به‌وسیله بهینه‌سازی‌ها مبهم هستند که خطر آن مشابه خطر جداسازی و استفاده مجدد کد شما توسط رقبا است. با این وجود، قبل از استفاده از یک مدل سفارشی در برنامه خود، باید از این خطر آگاه باشید.

در Android API سطح 21 (Lollipop) و جدیدتر، مدل در فهرستی دانلود می‌شود که از پشتیبان‌گیری خودکار حذف شده است.

در Android API سطح 20 و بالاتر، مدل در فهرستی به نام com.google.firebase.ml.custom.models در حافظه داخلی برنامه-خصوصی دانلود می شود. اگر پشتیبان‌گیری از فایل را با استفاده از BackupAgent فعال کرده‌اید، می‌توانید این فهرست را حذف کنید.