Menggunakan model TensorFlow Lite untuk inferensi dengan ML Kit di Android

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

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

Lihat sampel panduan memulai ML Kit di GitHub untuk mengetahui contoh penggunaan API ini, atau coba codelab.

Sebelum memulai

  1. Tambahkan Firebase ke project Android jika Anda belum melakukannya.
  2. Dalam file build.gradle level project, pastikan Anda memasukkan repositori Maven Google di bagian buildscript dan allprojects.
  3. Tambahkan dependensi untuk library Android ML Kit ke file Gradle modul (level aplikasi) Anda (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'
    }
    
  4. Konversikan model TensorFlow yang ingin Anda gunakan ke dalam format TensorFlow Lite. Lihat TOCO: TensorFlow Lite Konverter Optimalisasi.

Menghosting atau membuat paket model Anda

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 Pengujian A/B untuk menerapkan berbagai model secara dinamis ke sekelompok pengguna yang berbeda.

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

Dengan memaketkan model dengan aplikasi, Anda dapat memastikan bahwa fitur ML pada aplikasi Anda masih 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 Kustom.
  2. Klik Tambahkan model kustom (atau Tambahkan model lain).
  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 file model TensorFlow Lite baru kapan saja untuk sebuah model, dan aplikasi Anda akan mendownload model baru itu dan mulai menggunakannya begitu 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 Anda, salin file model (biasanya diakhiri dengan .tflite atau .lite) ke folder assets/ aplikasi Anda. (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 Anda untuk memastikan agar Gradle tidak mengompresi model saat membuat 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 Anda di aplikasi, pertama-tama konfigurasikan ML Kit dengan lokasi tempat model Anda tersedia: menggunakan Firebase dari jarak jauh, di penyimpanan lokal, atau keduanya. Jika Anda menetapkan model lokal dan jarak jauh, 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 Anda dengan Firebase, buat objek FirebaseCustomRemoteModel, yang menentukan nama yang Anda tetapkan pada model ketika 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 Anda memaketkan model dengan aplikasi, buat objek FirebaseCustomLocalModel, dengan menentukan 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 Anda

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

Jika Anda 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 Anda hanya perlu mengonfirmasi hal ini sebelum menjalankan penafsir, jika Anda memiliki model yang dihosting dari jarak jauh dan model yang dipaketkan secara lokal, mungkin masuk akal untuk melakukan pemeriksaan ini 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 fungsi yang berkaitan dengan model—misalnya, tidak menampilkan atau menyembunyikan sebagian UI—sampai Anda mengonfirmasi bahwa model 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, konfigurasikan 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 Anda tidak tahu bentuk dan tipe data dari 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 1.000 nilai float. Setiap nilai ini mewakili probabilitas bahwa gambar tersebut merupakan anggota dari salah satu 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 dari bentuk yang tepat untuk model Anda.

Misalnya, jika Anda memiliki model klasifikasi gambar dengan bentuk input dari nilai floating point [1 224 224 3], Anda bisa 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() dari objek yang diteruskan ke pemroses yang berhasil. 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 Anda 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

Apa pun cara Anda dalam menyediakan model TensorFlow Lite untuk ML Kit, ML Kit 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 khusus aplikasi dan dikaburkan oleh 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.

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.