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

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

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

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

  1. Если вы еще этого не сделали, добавьте Firebase в свой проект Android .
  2. Добавьте зависимости для библиотек Android ML Kit в файл 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 .

Разместите или свяжите свою модель

Прежде чем вы сможете использовать модель 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 перейдите на вкладку «Пользовательский» .
  2. Нажмите «Добавить пользовательскую модель» (или «Добавить другую модель» ).
  3. Укажите имя, которое будет использоваться для идентификации вашей модели в проекте Firebase, затем загрузите файл модели TensorFlow Lite (обычно заканчивающийся на .tflite или .lite ).
  4. В манифесте вашего приложения укажите, что требуется разрешение ИНТЕРНЕТ:
    <uses-permission android:name="android.permission.INTERNET" />

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

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

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

Затем добавьте следующее в файл 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 , указав имя, которое вы присвоили модели при ее загрузке:

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

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

JavaKotlin
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.
            }
        });
val conditions = FirebaseModelDownloadConditions.Builder()
    .requireWifi()
    .build()
FirebaseModelManager.getInstance().download(remoteModel, conditions)
    .addOnCompleteListener {
        // Success.
    }

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

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

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

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

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

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

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

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

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

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

JavaKotlin
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);
                // ...
            }
        });
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() менеджера моделей:

JavaKotlin
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.
            }
        });
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, указав количество и размеры («форму») массивов, которые использует ваша модель.

Если вы не знаете форму и тип входных и выходных данных вашей модели, вы можете использовать интерпретатор 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 .

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

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

JavaKotlin
FirebaseModelInputOutputOptions inputOutputOptions =
        new FirebaseModelInputOutputOptions.Builder()
                .setInputFormat(0, FirebaseModelDataType.FLOAT32, new int[]{1, 224, 224, 3})
                .setOutputFormat(0, FirebaseModelDataType.FLOAT32, new int[]{1, 5})
                .build();
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 как показано в следующем примере:

JavaKotlin
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;
    }
}
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 интерпретатора модели :

JavaKotlin
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
                        // ...
                    }
                });
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() объекта, который передается прослушивателю успеха. Например:

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

Как вы используете выходные данные, зависит от используемой модели.

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

JavaKotlin
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]));
}
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) и более поздних версиях модель загружается в каталог, исключенный из автоматического резервного копирования .

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

Firebase ML lets you bring powerful machine learning features to your app whether it's for Android or iOS, and whether you're an experienced machine learning developer or you're just getting started.

Обновлено Oct 3, 2024