Android'de ML Kit ile çıkarım için TensorFlow Lite modeli kullanma

TensorFlow Lite modeliyle cihaz üzerinde çıkarım yapmak için ML Kit'i kullanabilirsiniz.

Bu API için Android SDK düzeyi 16 (Jelly Bean) veya daha yeni bir sürüm gerekir.

Başlamadan önce

  1. Henüz yapmadıysanız Firebase'i Android projenize ekleyin.
  2. ML Kit Android kitaplıklarının bağımlılıkları modülünüzün (uygulama düzeyinde) Gradle dosyasına (genellikle app/build.gradle) eklenmelidir:
    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. Kullanmak istediğiniz TensorFlow modelini TensorFlow Lite biçimine dönüştürün. Bkz. TOCO: TensorFlow Lite Optimizing Converter.

Modelinizi barındırma veya paketleme

Uygulamanızda çıkarım yapmak için TensorFlow Lite modelini kullanabilmenizden önce modeli ML Kit'e sunmanız gerekir. ML Kit, Firebase kullanılarak uzaktan barındırılan, uygulama ikilisiyle birlikte paketlenmiş veya her ikisi de olan TensorFlow Lite modellerini kullanabilir.

Firebase'de bir model barındırarak yeni bir uygulama sürümü yayınlamadan modeli güncelleyebilir ve farklı kullanıcı gruplarına dinamik olarak farklı modeller sunmak için Remote Config ve A/B Testing'ü kullanabilirsiniz.

Modeli yalnızca Firebase ile barındırarak sağlamak ve uygulamanızla birlikte paketlemek istemiyorsanız uygulamanızın ilk indirme boyutunu azaltabilirsiniz. Ancak model uygulamanızla birlikte paketlenmemişse uygulamanız modeli ilk kez indirene kadar modelle ilgili işlevlerin kullanılamayacağını unutmayın.

Modelinizi uygulamanızla birlikte paketleyerek Firebase'da barındırılan model kullanılamadığında uygulamanızın ML özelliklerinin çalışmaya devam etmesini sağlayabilirsiniz.

Firebase'de model barındırma

TensorFlow Lite modelinizi Firebase'de barındırmak için:

  1. Firebase Konsolu'nun ML Kiti bölümünde Özel sekmesini tıklayın.
  2. Özel model ekle'yi (veya Başka bir model ekle'yi) tıklayın.
  3. Firebase projenizde modelinizi tanımlamak için kullanılacak bir ad belirtin, ardından TensorFlow Lite model dosyasını (genellikle .tflite veya .lite ile biter) yükleyin.
  4. Uygulamanızın manifest dosyasında INTERNET izninin gerekli olduğunu beyan edin:
    <uses-permission android:name="android.permission.INTERNET" />

Firebase projenize özel bir model ekledikten sonra, belirttiğiniz adı kullanarak bu modele uygulamalarınızda referans verebilirsiniz. İstediğiniz zaman yeni bir TensorFlow Lite modeli yükleyebilirsiniz. Uygulamanız yeni modeli indirir ve bir sonraki yeniden başlatmada kullanmaya başlar. Uygulamanızın modeli güncellemeyi denemesi için gereken cihaz koşullarını tanımlayabilirsiniz (aşağıya bakın).

Modelleri uygulamayla paketleme

TensorFlow Lite modelinizi uygulamanızla paketlemek için model dosyasını (genellikle .tflite veya .lite ile biter) uygulamanızın assets/ klasörüne kopyalayın. (Önce app/ klasörünü sağ tıklayıp Yeni > Klasör > Öğeler Klasörü'nü tıklayarak klasörü oluşturmanız gerekebilir.)

Ardından, Gradle'in uygulamayı oluştururken modelleri sıkıştırmamasını sağlamak için uygulamanızın build.gradle dosyasına aşağıdakileri ekleyin:

android {

    // ...

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

Model dosyası uygulama paketine dahil edilir ve ham öğe olarak ML Kit'e sunulur.

Modeli yükleme

TensorFlow Lite modelinizi uygulamanızda kullanmak için önce ML Kit'i, modelinizin bulunduğu konumlarla yapılandırın: Firebase'i kullanarak uzaktan, yerel depolama alanında veya her ikisini birden. Hem yerel hem de uzak bir model belirtirseniz uzak model mevcutsa uzak modeli kullanabilir, uzak model mevcut değilse yerel olarak depolanan modele geri dönebilirsiniz.

Firebase tarafından barındırılan bir modeli yapılandırma

Modelinizi Firebase ile barındırdıysanız modeli yüklerken atadığınız adı belirterek bir FirebaseCustomRemoteModel nesnesi oluşturun:

Java

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

Kotlin

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

Ardından, indirmeye izin vermek istediğiniz koşulları belirterek model indirme görevini başlatın. Model cihazda yoksa veya modelin daha yeni bir sürümü varsa görev, modeli Firebase'den eşzamansız olarak indirir:

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

val conditions = FirebaseModelDownloadConditions.Builder()
    .requireWifi()
    .build()
FirebaseModelManager.getInstance().download(remoteModel, conditions)
    .addOnCompleteListener {
        // Success.
    }

Birçok uygulama, indirme görevini başlatma kodunda başlatır ancak modeli kullanmadan önce istediğiniz zaman bu işlemi yapabilirsiniz.

Yerel bir model yapılandırma

Modeli uygulamanızla birlikte paketlediyseniz TensorFlow Lite modelinin dosya adını belirterek bir FirebaseCustomLocalModel nesnesi oluşturun:

Java

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

Kotlin

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

Modelinizden yorumcu oluşturma

Model kaynaklarınızı yapılandırdıktan sonra bunlardan birinde FirebaseModelInterpreter nesnesi oluşturun.

Yalnızca yerel olarak paketlenmiş bir modeliniz varsa FirebaseCustomLocalModel nesnenizle bir yorumlayıcı oluşturmanız yeterlidir:

Java

FirebaseModelInterpreter interpreter;
try {
    FirebaseModelInterpreterOptions options =
            new FirebaseModelInterpreterOptions.Builder(localModel).build();
    interpreter = FirebaseModelInterpreter.getInstance(options);
} catch (FirebaseMLException e) {
    // ...
}

Kotlin

val options = FirebaseModelInterpreterOptions.Builder(localModel).build()
val interpreter = FirebaseModelInterpreter.getInstance(options)

Uzaktan barındırılan bir modeliniz varsa çalıştırmadan önce modelin indirildiğinden emin olmanız gerekir. Model yöneticisinin isModelDownloaded() yöntemini kullanarak model indirme görevinin durumunu kontrol edebilirsiniz.

Bunu yalnızca yorumlayıcıyı çalıştırmadan önce onaylamanız gerekir. Ancak hem uzaktan barındırılan hem de yerel olarak paketlenmiş bir modeliniz varsa model yorumlayıcısını örneklendirirken bu kontrolü gerçekleştirmeniz yararlı olabilir: İndirilmişse uzak modelden, aksi takdirde yerel modelden bir yorumlayıcı oluşturun.

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

FirebaseModelManager.getInstance().isModelDownloaded(remoteModel)
    .addOnSuccessListener { isDownloaded -> 
    val options =
        if (isDownloaded) {
            FirebaseModelInterpreterOptions.Builder(remoteModel).build()
        } else {
            FirebaseModelInterpreterOptions.Builder(localModel).build()
        }
    val interpreter = FirebaseModelInterpreter.getInstance(options)
}

Yalnızca uzaktan barındırılan bir modeliniz varsa modelin indirildiğini onaylayana kadar modelle ilgili işlevleri (ör. kullanıcı arayüzünüzün bir bölümünü devre dışı bırakma veya gizleme) devre dışı bırakmanız gerekir. Bunu, model yöneticisinin download() yöntemine bir dinleyici ekleyerek yapabilirsiniz:

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

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

Modelin girişini ve çıkışını belirtme

Ardından model yorumlayıcısının giriş ve çıkış biçimlerini yapılandırın.

TensorFlow Lite modeli, giriş olarak bir veya daha fazla çok boyutlu dizi alır ve çıkış olarak bir veya daha fazla çok boyutlu dizi oluşturur. Bu diziler byte, int, long veya float değerleri içerir. ML Kit'i, modelinizin kullandığı dizilerin sayısı ve boyutlarıyla ("şekil") yapılandırmanız gerekir.

Modelinizin giriş ve çıkışının şeklini ve veri türünü bilmiyorsanız modelinizi incelemek için TensorFlow Lite Python yorumlayıcısını kullanabilirsiniz. Örneğin:

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

Modelinizin giriş ve çıkış biçimini belirledikten sonra FirebaseModelInputOutputOptions nesnesi oluşturarak uygulamanızın model yorumlayıcısını yapılandırabilirsiniz.

Örneğin, kayan noktalı bir resim sınıflandırma modeli, giriş olarak N 224x224 üç kanallı (RGB) resim grubunu temsil eden floatx224x224x3 float değeri dizisi alabilir ve çıkış olarak her biri resmin, modelin tahmin ettiği 1.000 kategoriden birinin üyesi olma olasılığını temsil eden 1.000 float değeri listesi oluşturabilir.N

Bu tür bir model için model yorumlayıcının girişini ve çıkışını aşağıdaki gibi yapılandırırsınız:

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

val inputOutputOptions = FirebaseModelInputOutputOptions.Builder()
        .setInputFormat(0, FirebaseModelDataType.FLOAT32, intArrayOf(1, 224, 224, 3))
        .setOutputFormat(0, FirebaseModelDataType.FLOAT32, intArrayOf(1, 5))
        .build()

Giriş verilerinde çıkarım yapma

Son olarak, modeli kullanarak çıkarım yapmak için giriş verilerinizi alın ve modeliniz için doğru biçimde bir giriş dizisi elde etmek üzere veriler üzerinde gerekli tüm dönüşümleri gerçekleştirin.

Örneğin, [1 224 224 3] kayan nokta değerleri giriş şekline sahip bir resim sınıflandırma modeliniz varsa aşağıdaki örnekte gösterildiği gibi bir Bitmap nesnesinden giriş dizisi oluşturabilirsiniz:

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

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

Ardından, giriş verilerinizle bir FirebaseModelInputs nesnesi oluşturun ve bu nesneyi ile modelin giriş ve çıkış spesifikasyonunu model yorumlayıcısının run yöntemine iletin:

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

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

Çağrı başarılı olursa başarı dinleyicisine iletilen nesnenin getOutput() yöntemini çağırarak çıkışı alabilirsiniz. Örneğin:

Java

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

Kotlin

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

Çıktıyı kullanma şekliniz, kullandığınız modele bağlıdır.

Örneğin, sınıflandırma işlemi yapıyorsanız sonraki adımda sonucun dizinlerini temsil ettikleri etiketlerle eşleyebilirsiniz:

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

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

Ek: Model güvenliği

TensorFlow Lite modellerinizi ML Kit'e nasıl sunduğunuzdan bağımsız olarak ML Kit, bunları yerel depolama alanında standart serileştirilmiş protobuf biçiminde depolar.

Teorik olarak bu, herkesin modelinizi kopyalayabileceği anlamına gelir. Ancak uygulamada çoğu model uygulamaya o kadar özeldir ve optimizasyonlarla o kadar karartılmıştır ki risk, rakiplerin kodunuzu söküp yeniden kullanması riskine benzer. Yine de uygulamanızda özel bir model kullanmadan önce bu riskin farkında olmanız gerekir.

Android API düzeyi 21 (Lollipop) ve sonraki sürümlerde model, otomatik yedekleme kapsamından hariç tutulan bir dizine indirilir.

Android API düzeyi 20 ve önceki sürümlerde model, uygulamaya özel dahili depolama alanındaki com.google.firebase.ml.custom.models adlı bir dizine indirilir. Dosya yedeklemeyi BackupAgent kullanarak etkinleştirdiyseniz bu dizini hariç tutmayı seçebilirsiniz.