Menggunakan model TensorFlow Lite untuk inferensi dengan ML Kit di Android

Anda dapat menggunakan Kit ML untuk melakukan perangkat inferensi-dalam-perangkat dengan model TensorFlow Lite .

API ini membutuhkan Android SDK level 16 (Jelly Bean) atau yang lebih baru.

Sebelum memulai

  1. Tambahkan Firebase ke project Android jika Anda belum melakukannya.
  2. Tambahkan dependensi untuk library Android ML Kit ke file Gradle modul (level aplikasi), biasanya 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. Konversi model TensorFlow yang ingin Anda gunakan ke format TensorFlow Lite. Lihat TOCO: Konverter Optimalisasi TensorFlow Lite.

Menghosting atau memaketkan model

Sebelum dapat menggunakan model TensorFlow Lite untuk inferensi di aplikasi, Anda harus membuat model tersedia untuk ML Kit. ML Kit dapat menggunakan model TensorFlow Lite yang dihosting dari jarak jauh menggunakan Firebase, dipaketkan dengan biner aplikasi, atau keduanya.

Dengan menghosting model di Firebase, Anda dapat mengupdate model tanpa merilis versi baru aplikasi. Anda juga dapat menggunakan Remote Config dan A/B Testing untuk secara dinamis menyalurkan berbagai model ke berbagai kelompok pengguna.

Jika memilih untuk hanya menyediakan model dengan menghostingnya di Firebase, dan tidak memaketkannya dengan aplikasi, Anda dapat mengurangi ukuran download awal aplikasi. Namun, ingat bahwa jika model tidak dipaketkan dengan aplikasi, fungsi yang terkait dengan model tidak akan tersedia sebelum aplikasi mendownload model untuk pertama kalinya.

Dengan memaketkan model dengan aplikasi, Anda dapat memastikan bahwa fitur ML pada aplikasi tetap berfungsi jika model yang dihosting Firebase tidak tersedia.

Menghosting model di Firebase

Untuk menghosting model TensorFlow Lite di Firebase:

  1. Di bagian ML Kit pada Firebase console, klik tab Custom.
  2. Klik Add custom model (atau Add another model).
  3. Tentukan nama yang akan digunakan untuk mengidentifikasi model Anda di project Firebase, lalu upload file model TensorFlow Lite (biasanya diakhiri dengan .tflite atau .lite).
  4. Di manifes aplikasi Anda, deklarasikan bahwa izin INTERNET diperlukan:
    <uses-permission android:name="android.permission.INTERNET" />
    

Setelah menambahkan model kustom ke project Firebase, Anda dapat mereferensikan model tersebut di aplikasi menggunakan nama yang Anda tentukan. Anda dapat mengupload model TensorFlow Lite baru kapan saja, dan aplikasi akan mendownloadnya, lalu mulai menggunakannya setelah aplikasi dimulai ulang. Anda dapat menentukan kondisi perangkat yang diperlukan aplikasi untuk mencoba mengupdate model (lihat di bawah ini).

Memaketkan model dengan aplikasi

Untuk memaketkan model TensorFlow Lite dengan aplikasi, salin file model (biasanya diakhiri dengan .tflite atau .lite) ke folder assets/ aplikasi. (Anda mungkin perlu membuat folder terlebih dahulu dengan mengklik kanan folder app/, lalu mengklik Baru > Folder > Folder Aset.)

Kemudian, tambahkan hal berikut ini ke file build.gradle aplikasi untuk memastikan agar Gradle tidak mengompresi model saat mem-build aplikasi:

android {

    // ...

    aaptOptions {
        noCompress "tflite"  // Your model's file extension: "tflite", "lite", etc.
    }
}

File model akan disertakan ke dalam paket aplikasi dan tersedia untuk ML Kit sebagai aset mentah.

Memuat model

Untuk menggunakan model TensorFlow Lite di aplikasi, pertama-tama konfigurasi ML Kit dengan lokasi tempat model tersebut tersedia: menggunakan Firebase dari jarak jauh, di penyimpanan lokal, atau keduanya. Jika ML Kit dikonfigurasi dengan kedua model, Anda dapat menggunakan model jarak jauh jika tersedia, dan kembali ke model yang disimpan secara lokal jika model jarak jauh tidak tersedia.

Mengonfigurasi model yang dihosting Firebase

Jika Anda menghosting model dengan Firebase, buat objek FirebaseCustomRemoteModel dengan menentukan nama yang ditetapkan pada model ketika Anda menguploadnya:

Java

FirebaseCustomRemoteModel remoteModel =
        new FirebaseCustomRemoteModel.Builder("your_model").build();

Kotlin+KTX

val remoteModel = FirebaseCustomRemoteModel.Builder("your_model").build()

Kemudian, mulai tugas download model, dengan menentukan kondisi yang Anda inginkan untuk mengizinkan download. Jika model tidak ada di perangkat, atau jika versi model yang lebih baru tersedia, tugas ini akan mendownload model dari Firebase secara asinkron:

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

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

Mengonfigurasi model lokal

Jika memaketkan model dengan aplikasi, buat objek FirebaseCustomLocalModel, dengan menyebutkan nama file model TensorFlow Lite:

Java

FirebaseCustomLocalModel localModel = new FirebaseCustomLocalModel.Builder()
        .setAssetFilePath("your_model.tflite")
        .build();

Kotlin+KTX

val localModel = FirebaseCustomLocalModel.Builder()
    .setAssetFilePath("your_model.tflite")
    .build()

Membuat penafsir dari model

Setelah mengonfigurasi sumber model, buat objek FirebaseModelInterpreter dari salah satu sumber tersebut.

Jika hanya memiliki model yang dipaketkan secara lokal, cukup buat penafsir dari objek 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)

Jika Anda memiliki model yang dihosting dari jarak jauh, Anda harus memeriksa apakah model tersebut sudah didownload sebelum menjalankannya. Anda dapat memeriksa status tugas download model menggunakan metode isModelDownloaded() pengelola model.

Meskipun hanya perlu memastikan hal ini sebelum menjalankan penafsir, jika Anda memiliki model yang dihosting dari jarak jauh dan model yang dipaketkan secara lokal, mungkin pemeriksaan ini perlu dilakukan saat membuat instance penafsir model: buat penafsir dari model jarak jauh jika model tersebut sudah didownload, dan dari model lokal jika belum didownload.

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

Jika Anda hanya memiliki model yang dihosting dari jarak jauh, Anda harus menonaktifkan fungsionalitas terkait model, misalnya membuat sebagian UI berwarna abu-abu atau menyembunyikannya, hingga Anda mengonfirmasi model tersebut telah didownload. Anda dapat melakukannya dengan menambahkan pemroses ke metode download() pengelola model:

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

Menentukan input dan output model

Selanjutnya, konfigurasi format input dan output penafsir model.

Model TensorFlow Lite mengambil satu atau beberapa array multidimensi sebagai input dan menghasilkannya sebagai output. Array ini berisi nilai byte, int, long, atau float. Anda harus mengonfigurasi ML Kit dengan jumlah dan dimensi ("bentuk") array yang digunakan oleh model Anda.

Jika tidak tahu bentuk dan tipe data input dan output model, Anda dapat menggunakan penafsir TensorFlow Lite Python untuk memeriksa model. Misalnya:

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

Setelah menentukan format input dan output model, Anda dapat mengonfigurasi penafsir model aplikasi dengan membuat objek FirebaseModelInputOutputOptions.

Misalnya, model klasifikasi gambar floating point mungkin akan mengambil array Nx224x224x3 berisi nilai float sebagai input, yang mewakili sekumpulan gambar N 224x224 tiga saluran (RGB), dan menghasilkan output berupa daftar berisi 1.000 nilai float. Setiap nilai mewakili probabilitas bahwa gambar tersebut merupakan anggota salah satu di antara 1.000 kategori yang diprediksi oleh model.

Untuk model seperti itu, Anda akan mengonfigurasi input dan output penafsir model, seperti yang ditunjukkan di bawah ini:

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()

Melakukan inferensi pada data input

Terakhir, untuk melakukan inferensi menggunakan model, dapatkan data input Anda, dan jalankan transformasi pada data yang diperlukan untuk mendapatkan array input bentuk yang tepat untuk model Anda.

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

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

Kemudian, buat objek FirebaseModelInputs dengan data input Anda, dan teruskan objek tersebut dengan spesifikasi model input dan output ke metode run penafsir model:

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
            // ...
        }

Jika panggilan berhasil, Anda bisa mendapatkan output dengan memanggil metode getOutput() objek yang diteruskan ke pemroses peristiwa sukses. Contoh:

Java

float[][] output = result.getOutput(0);
float[] probabilities = output[0];

Kotlin+KTX

val output = result.getOutput<Array<FloatArray>>(0)
val probabilities = output[0]

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:

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

Lampiran: Keamanan model

Cara apa pun yang Anda lakukan guna menyediakan model TensorFlow Lite untuk ML Kit, ML Kit akan menyimpannya dalam format protobuf berseri 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.