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ına ait bağımlılıkları modülünüzün (uygulama düzeyinde) Gradle dosyasına (genellikle app/build.gradle) ekleyin:
    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 için TensorFlow Lite modelini kullanabilmeniz için modeli ML Kit'in kullanımına 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ılara farklı modelleri dinamik olarak sunmak için Remote Config ve A/B Testing kullanabilirsiniz.

Modeli yalnızca Firebase ile barındırarak sağlamayı ve uygulamanızla birlikte paketlememeyi tercih ederseniz uygulamanızın ilk indirme boyutunu küçültebilirsiniz. Ancak modeli uygulamanızla birlikte paketlemezseniz modelle ilgili işlevlerin, uygulamanız modeli ilk kez indirene kadar kullanılamayacağını unutmayın.

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

Modelleri Firebase'de barındırma

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

  1. Firebase konsolunun ML Kit bölümünde Custom (Ö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 uygulamalarınızdaki modele referansta bulunabilirsiniz. Dilediğiniz zaman yeni bir TensorFlow Lite modeli yükleyebilirsiniz. Uygulamanız, yeni modeli indirir ve uygulama bir sonraki yeniden başlatma işleminde bu modeli kullanmaya başlar. Uygulamanızın modeli güncellemeye çalışması 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'ın uygulamayı oluştururken modelleri sıkıştırmadığından emin olmak 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 ML Kit'e ham öğe olarak sunulur.

Modeli yükleme

TensorFlow Lite modelinizi uygulamanızda kullanmak için öncelikle ML Kit'i modelinizin kullanılabildiği konumlarla (Firebase kullanılarak uzaktan, yerel depolama alanında veya her ikisi de) yapılandırın. Hem yerel hem de uzak bir model belirtirseniz uzak model varsa onu kullanabilir, yoksa da yerel olarak depolanan modele geri dönebilirsiniz.

Firebase'de 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, başlatma kodunda indirme görevini başlatır ancak modeli kullanmanız gerekmeden önce istediğiniz zaman bu işlemi yapabilirsiniz.

Yerel bir modeli 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 yorumlayıcı oluşturma

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

Yalnızca yerel olarak paketlenmiş bir modeliniz varsa FirebaseCustomLocalModel nesnenizden 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 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 gerekse de hem uzaktan barındırılan hem de yerel olarak paketlenmiş bir modeliniz varsa bu kontrolü model yorumlayıcıyı oluştururken yapmanız mantıklı olabilir: İndirildiyse 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ş ve çıkışını belirtme

Ardından, model yorumlayıcı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 bunları üretir. Bu diziler byte, int, long veya float değerlerini 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 görüntü sınıflandırma modeli, N 224x224 üç kanallı (RGB) görüntü grubunu temsil eden float değerlerinden oluşan bir Nx224x224x3 dizisini giriş olarak alabilir ve çıkış olarak 1.000 float değerinden oluşan bir liste üretebilir. Bu değerlerin her biri, görüntünün modelin tahmin ettiği 1.000 kategoriden birinin üyesi olma olasılığını temsil eder.

Böyle bir model için model yorumlayıcının giriş 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ş verileri üzerinde çıkarım yapma

Son olarak, modeli kullanarak çıkarım yapmak için giriş verilerinizi alın ve veriler üzerinde, modeliniz için doğru şekle sahip bir giriş dizisi elde etmek üzere gerekli olan 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 modelin giriş ve çıkış spesifikasyonuyla birlikte 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
            // ...
        }

Arama başarılı olursa başarı işleyicisine 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]

Çıkışı nasıl kullanacağınız, kullandığınız modele bağlıdır.

Örneğin, sınıflandırma işlemi yapıyorsanız bir sonraki adım olarak 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 sunarsanız sunun, ML Kit bunları yerel depolama alanında standart seri hale getirilmiş protobuf biçiminde saklar.

Bu, teorik olarak herkesin modelinizi kopyalayabileceği anlamına gelir. Ancak uygulamada, çoğu model uygulamaya özel ve optimizasyonlarla karartılmış olduğundan risk, rakiplerin kodunuzu söküp yeniden kullanmasıyla aynıdır. Bununla birlikte, 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 yedeklemenin dışında tutulan bir dizine indirilir.

Android API düzeyi 20 ve daha eski sürümlerde model, uygulamaya özel dahili depolama alanında com.google.firebase.ml.custom.models adlı bir dizine indirilir. BackupAgent kullanarak dosya yedeklemeyi etkinleştirdiyseniz bu dizini hariç tutabilirsiniz.