Используйте модель TensorFlow Lite для вывода с помощью ML Kit на Android. Используйте модель TensorFlow Lite для вывода с помощью ML Kit на Android.

С помощью ML Kit можно выполнять вывод данных непосредственно на устройстве с использованием модели TensorFlow Lite .

Для работы этого API требуется Android SDK уровня 16 (Jelly Bean) или более новая версия.

Прежде чем начать

  1. Если вы еще этого не сделали, добавьте Firebase в свой Android-проект .
  2. Добавьте зависимости для библиотек ML Kit Android в файл Gradle вашего модуля (на уровне приложения) (обычно 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. Преобразуйте модель TensorFlow, которую вы хотите использовать, в формат TensorFlow Lite. См. TOCO: TensorFlow Lite Optimizing Converter .

Разместите или объедините вашу модель.

Прежде чем использовать модель TensorFlow Lite для вывода результатов в вашем приложении, необходимо сделать эту модель доступной для ML Kit. ML Kit может использовать модели TensorFlow Lite, размещенные удаленно с помощью Firebase, входящие в состав исполняемого файла приложения или и то, и другое.

Размещая модель в Firebase, вы можете обновлять ее без выпуска новой версии приложения, а также использовать Remote Config и A/B Testing для динамического предоставления разных моделей разным группам пользователей.

Если вы решите предоставлять модель только путем ее размещения в Firebase, а не включать ее в состав приложения, вы сможете уменьшить размер первоначальной загрузки вашего приложения. Однако имейте в виду, что если модель не включена в состав вашего приложения, любая связанная с ней функциональность будет недоступна до тех пор, пока ваше приложение не загрузит модель в первый раз.

Включив вашу модель в приложение, вы можете гарантировать, что функции машинного обучения вашего приложения будут работать даже тогда, когда модель, размещенная в Firebase, недоступна.

Размещайте модели на Firebase

Чтобы разместить вашу модель TensorFlow Lite в Firebase:

  1. В разделе ML Kit консоли Firebase перейдите на вкладку «Custom» .
  2. Нажмите «Добавить пользовательскую модель» (или «Добавить другую модель »).
  3. Укажите имя, которое будет использоваться для идентификации вашей модели в проекте Firebase, затем загрузите файл модели TensorFlow Lite (обычно с расширением .tflite или .lite ).
  4. В манифесте вашего приложения укажите, что требуется разрешение INTERNET:
    <uses-permission android:name="android.permission.INTERNET" />

После добавления пользовательской модели в ваш проект Firebase вы можете ссылаться на нее в своих приложениях, используя указанное вами имя. В любой момент вы можете загрузить новую модель TensorFlow Lite, и ваше приложение загрузит новую модель и начнет ее использовать при следующем перезапуске. Вы можете определить условия устройства, необходимые для того, чтобы ваше приложение попыталось обновить модель (см. ниже).

Объединяйте модели с приложением.

Чтобы включить модель TensorFlow Lite в ваше приложение, скопируйте файл модели (обычно с расширением .tflite или .lite ) в папку assets/ вашего приложения. (Возможно, вам потребуется сначала создать папку, щелкнув правой кнопкой мыши по папке app/ , а затем выбрав New > Folder > Assets Folder .)

Затем добавьте следующее в файл build.gradle вашего приложения, чтобы Gradle не сжимал модели при сборке приложения:

android {

    // ...

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

Файл модели будет включен в пакет приложения и будет доступен для ML Kit в качестве исходного ресурса.

Загрузите модель

Чтобы использовать вашу модель TensorFlow Lite в вашем приложении, сначала настройте ML Kit, указав места, где ваша модель доступна: удаленно через Firebase, в локальном хранилище или и там, и там. Если вы укажете как локальную, так и удаленную модель, вы сможете использовать удаленную модель, если она доступна, и переключиться на локально хранящуюся модель, если удаленная модель недоступна.

Настройте модель, размещенную в Firebase.

Если вы разместили свою модель в Firebase, создайте объект FirebaseCustomRemoteModel , указав имя, которое вы присвоили модели при ее загрузке:

Java

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

Kotlin

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

Затем запустите задачу загрузки модели, указав условия, при которых вы хотите разрешить загрузку. Если модель отсутствует на устройстве или если доступна более новая версия модели, задача асинхронно загрузит модель из Firebase:

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

Во многих приложениях задача загрузки запускается в коде инициализации, но вы можете сделать это в любой момент до того, как вам понадобится использовать модель.

Настройте локальную модель

Если вы включили модель в состав своего приложения, создайте объект FirebaseCustomLocalModel , указав имя файла модели TensorFlow Lite:

Java

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

Kotlin

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

Создайте интерпретатор на основе вашей модели.

После настройки источников моделей создайте объект FirebaseModelInterpreter на основе одного из них.

Если у вас есть только локально упакованная модель, просто создайте интерпретатор из объекта FirebaseCustomLocalModel :

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)

Если у вас есть удаленно размещенная модель, вам необходимо убедиться, что она загружена, прежде чем запускать ее. Вы можете проверить статус задачи загрузки модели, используя метод isModelDownloaded() менеджера моделей.

Хотя подтверждение этого требуется только перед запуском интерпретатора, если у вас есть как удаленно размещенная модель, так и локально упакованная модель, имеет смысл выполнить эту проверку при создании экземпляра интерпретатора модели: создать интерпретатор из удаленной модели, если она была загружена, и из локальной модели в противном случае.

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

Если у вас есть только удалённо размещённая модель, следует отключить связанные с ней функции — например, сделать часть пользовательского интерфейса неактивной или скрытой — до тех пор, пока вы не подтвердите загрузку модели. Это можно сделать, добавив обработчик событий к методу download() менеджера моделей:

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

Укажите входные и выходные данные модели.

Далее настройте форматы ввода и вывода интерпретатора модели.

Модель TensorFlow Lite принимает на вход один или несколько многомерных массивов и выдает на выходе один или несколько массивов. Эти массивы содержат значения типов byte , int , long или float . Необходимо настроить ML Kit, указав количество и размерность («shape») массивов, используемых вашей моделью.

Если вам не известны структура и тип данных входных и выходных данных вашей модели, вы можете использовать интерпретатор Python TensorFlow Lite для проверки вашей модели. Например:

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

После определения формата входных и выходных данных вашей модели вы можете настроить интерпретатор модели вашего приложения, создав объект FirebaseModelInputOutputOptions .

Например, модель классификации изображений с плавающей запятой может принимать на вход массив значений float размером N x 224 x 224 x 3, представляющий собой набор из N трехканальных (RGB) изображений размером 224 x 224, и выдавать на выходе список из 1000 значений float запятой, каждое из которых представляет вероятность того, что изображение принадлежит к одной из 1000 категорий, предсказанных моделью.

Для такой модели необходимо настроить входные и выходные данные интерпретатора модели, как показано ниже:

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

Выполнить логический вывод на основе входных данных

Наконец, чтобы выполнить вывод с использованием модели, получите входные данные и выполните над ними необходимые преобразования, чтобы получить входной массив нужной формы для вашей модели.

Например, если у вас есть модель классификации изображений с входными данными в формате [1 224 224 3] с плавающей запятой, вы можете сгенерировать входной массив из объекта Bitmap , как показано в следующем примере:

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

Затем создайте объект FirebaseModelInputs с входными данными и передайте его, а также спецификацию входных и выходных данных модели, в метод run интерпретатора модели :

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

Если вызов пройден успешно, вы можете получить результат, вызвав метод getOutput() объекта, переданного обработчику успешного выполнения. Например:

Java

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

Kotlin

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

Способ использования полученных данных зависит от используемой модели.

Например, если вы выполняете классификацию, то на следующем этапе вы можете сопоставить индексы результата с метками, которые они представляют:

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

Приложение: Безопасность модели

Независимо от способа предоставления моделей TensorFlow Lite для ML Kit, ML Kit хранит их в стандартном сериализованном формате protobuf в локальном хранилище.

Теоретически это означает, что любой может скопировать вашу модель. Однако на практике большинство моделей настолько специфичны для конкретного приложения и завуалированы оптимизациями, что риск сопоставим с риском того, что конкуренты разберут и повторно используют ваш код. Тем не менее, вам следует помнить об этом риске, прежде чем использовать собственную модель в своем приложении.

В Android API уровня 21 (Lollipop) и более новых версиях модель загружается в каталог, который исключен из автоматического резервного копирования .

В Android API уровня 20 и выше модель загружается в каталог с именем com.google.firebase.ml.custom.models во внутренней памяти приложения. Если вы включили резервное копирование файлов с помощью BackupAgent , вы можете исключить этот каталог из резервного копирования.