Menggunakan model TensorFlow Lite kustom di Android

Jika aplikasi Anda menggunakan model TensorFlow Lite kustom, Anda dapat menggunakan Firebase ML untuk men-deploy model. Dengan men-deploy model menggunakan Firebase, Anda dapat memperkecil ukuran download awal aplikasi dan mengupdate model ML aplikasi tanpa merilis versi baru aplikasi. Selain itu, dengan Remote Config dan A/B Testing, Anda dapat menyalurkan berbagai model secara dinamis ke berbagai kelompok pengguna yang berbeda.

Model TensorFlow Lite

Model TensorFlow Lite adalah model ML yang dioptimalkan untuk berjalan di perangkat seluler. Untuk mendapatkan model TensorFlow Lite:

Sebelum memulai

  1. Tambahkan Firebase ke project Android jika Anda belum melakukannya.
  2. Dalam file Gradle modul (level aplikasi) (biasanya <project>/<app-module>/build.gradle.kts atau <project>/<app-module>/build.gradle), tambahkan dependensi untuk library downloader model Firebase ML untuk Android. Sebaiknya gunakan Firebase Android BoM untuk mengontrol pembuatan versi library.

    Selain itu, sebagai bagian dari penyiapan downloader model Firebase ML, Anda perlu menambahkan TensorFlow Lite SDK ke aplikasi Anda.

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

    Dengan menggunakan Firebase Android BoM, aplikasi Anda akan selalu menggunakan versi library Android Firebase yang kompatibel.

    (Alternatif)  Tambahkan dependensi library Firebase tanpa menggunakan BoM

    Jika memilih untuk tidak menggunakan Firebase BoM, Anda harus menentukan setiap versi library Firebase di baris dependensinya.

    Perlu diperhatikan bahwa jika Anda menggunakan beberapa library Firebase di aplikasi, sebaiknya gunakan BoM untuk mengelola versi library, yang memastikan bahwa semua versi kompatibel.

    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")
    }
    Mencari modul library khusus Kotlin? Mulai Oktober 2023 (Firebase BoM 32.5.0), developer Kotlin dan Java dapat bergantung pada modul library utama (untuk mengetahui detailnya, lihat FAQ tentang inisiatif ini).
  3. Di manifes aplikasi Anda, deklarasikan bahwa izin INTERNET diperlukan:
    <uses-permission android:name="android.permission.INTERNET" />

1. Deploy model Anda

Deploy model TensorFlow kustom Anda menggunakan Firebase console atau Firebase Admin Python dan Node.js SDK. Lihat Men-deploy dan mengelola model kustom.

Setelah menambahkan model kustom ke project Firebase, Anda dapat mereferensikan model tersebut di aplikasi menggunakan nama yang Anda tentukan. Anda dapat men-deploy model TensorFlow Lite baru kapan saja dan mendownload model baru tersebut ke perangkat pengguna dengan memanggil getModel() (lihat di bawah).

2. Download model ke perangkat dan lakukan inisialisasi penafsir TensorFlow Lite

Untuk menggunakan model TensorFlow Lite di aplikasi Anda, pertama-tama gunakan Firebase ML SDK untuk mendownload versi terbaru model ke perangkat. Kemudian, buat instance penafsir TensorFlow Lite dengan model tersebut.

Untuk mulai mendownload model, panggil metode getModel() downloader model, dengan menentukan nama yang Anda tetapkan untuk model saat menguploadnya, apakah Anda ingin selalu mendownload model terbaru, dan kondisi yang Anda inginkan untuk mengizinkan download.

Anda dapat memilih di antara tiga perilaku download:

Jenis download Deskripsi
LOCAL_MODEL Mendapatkan model lokal dari perangkat. Jika tidak ada model lokal yang tersedia, jenis ini akan berperilaku seperti LATEST_MODEL. Gunakan jenis download ini jika Anda tidak ingin memeriksa update model. Misalnya, Anda menggunakan Remote Config untuk mengambil nama model dan Anda selalu mengupload model dengan nama baru (direkomendasikan).
LOCAL_MODEL_UPDATE_IN_BACKGROUND Mendapatkan model lokal dari perangkat dan mulai mengupdate model di latar belakang. Jika tidak ada model lokal yang tersedia, jenis ini akan berperilaku seperti LATEST_MODEL.
LATEST_MODEL Mendapatkan model terbaru. Jika model lokal adalah versi terbaru, jenis ini akan menggunakan model lokal. Jika bukan, model terbaru akan didownload. Perilaku ini akan memblokir model hingga versi terbarunya didownload (tidak direkomendasikan). Gunakan perilaku ini hanya jika Anda memerlukan versi terbaru secara eksplisit.

Anda harus menonaktifkan fungsionalitas terkait model—misalnya, menyamarkan atau menyembunyikan sebagian UI—hingga Anda mengonfirmasi model tersebut telah didownload.

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);
        }
      }
    });

Banyak aplikasi memulai tugas download dalam kode inisialisasinya, tetapi Anda dapat melakukannya kapan saja sebelum menggunakan model.

3. Lakukan inferensi pada data input

Mendapatkan bentuk input dan output model Anda

Penafsir model TensorFlow Lite mengambil satu atau beberapa array multidimensi sebagai input dan menghasilkannya sebagai output. Array ini berisi nilai byte, int, long, atau float. Sebelum dapat meneruskan data ke model atau menggunakan hasilnya, Anda harus mengetahui jumlah dan dimensi ("bentuk") array yang digunakan oleh model Anda.

Jika model Anda build sendiri, atau jika format input dan output model didokumentasikan, Anda mungkin sudah memiliki informasi ini. Jika tidak mengetahui bentuk dan jenis data input serta output model, Anda dapat menggunakan penafsir TensorFlow Lite untuk memeriksa model Anda. Contoh:

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']))

Contoh output:

1 input(s):
[  1 224 224   3] <class 'numpy.float32'>

1 output(s):
[1 1000] <class 'numpy.float32'>

Menjalankan penafsir

Setelah Anda menentukan format input dan output model, dapatkan data input dan jalankan transformasi pada data yang diperlukan untuk mendapatkan input dengan bentuk yang tepat untuk model Anda.

Misalnya, jika Anda memiliki model klasifikasi gambar dengan bentuk input nilai floating point [1 224 224 3], Anda dapat menghasilkan ByteBuffer input dari objek Bitmap seperti yang ditunjukkan dalam contoh berikut:

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);
    }
}

Lalu, alokasikan ByteBuffer yang cukup besar untuk memuat output model dan teruskan buffering input dan buffering output ke metode run() penafsir TensorFlow Lite. Misalnya, untuk bentuk output nilai floating point [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);

Cara penggunaan output bergantung pada model yang Anda gunakan.

Misalnya, jika melakukan klasifikasi, sebagai langkah berikutnya, Anda mungkin dapat memetakan indeks hasil ke label yang diwakilinya:

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?
}

Lampiran: Keamanan model

Terlepas dari cara Anda menyediakan model TensorFlow Lite untuk Firebase ML, Firebase ML akan menyimpannya dalam format protobuf serial standar di penyimpanan lokal.

Secara teori, ini artinya siapa saja dapat menyalin model Anda. Namun, dalam praktiknya, sebagian besar model bersifat spesifik aplikasi dan di-obfuscate melalui pengoptimalan sehingga risikonya serupa dengan jika pesaing membongkar dan menggunakan kembali kode Anda. Meskipun demikian, Anda harus menyadari risiko ini sebelum menggunakan model kustom di aplikasi Anda.

Pada Android API level 21 (Lollipop) dan yang lebih baru, model didownload ke direktori yang dikecualikan dari pencadangan otomatis.

Pada Android API level 20 dan yang lebih lama, model didownload ke direktori bernama com.google.firebase.ml.custom.models di penyimpanan internal pribadi aplikasi. Jika mengaktifkan pencadangan file menggunakan BackupAgent, Anda dapat memilih untuk tidak menyertakan direktori ini.