Вы можете использовать ML Kit для выполнения логических выводов на устройстве с помощью модели TensorFlow Lite .
Для этого API требуется Android SDK уровня 16 (Jelly Bean) или новее.
Прежде чем вы начнете
- Если вы еще этого не сделали, добавьте Firebase в свой проект Android .
- Добавьте зависимости для библиотек 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' }
- Преобразуйте модель TensorFlow, которую вы хотите использовать, в формат TensorFlow Lite. См. TOCO: оптимизирующий преобразователь TensorFlow Lite .
Разместите или свяжите свою модель
Прежде чем вы сможете использовать модель TensorFlow Lite для вывода в своем приложении, вы должны сделать модель доступной для ML Kit. ML Kit может использовать модели TensorFlow Lite, размещенные удаленно с помощью Firebase, в комплекте с двоичным файлом приложения или и то, и другое.
Разместив модель в Firebase, вы можете обновить модель, не выпуская новую версию приложения, и вы можете использовать удаленную настройку и A/B-тестирование для динамического предоставления различных моделей различным группам пользователей.
Если вы решите предоставить модель только путем размещения ее в Firebase, а не в комплекте с приложением, вы можете уменьшить первоначальный размер загружаемого приложения. Однако имейте в виду, что если модель не связана с вашим приложением, любые функции, связанные с моделью, будут недоступны до тех пор, пока ваше приложение не загрузит модель в первый раз.
Связав свою модель с приложением, вы можете гарантировать, что функции машинного обучения вашего приложения по-прежнему работают, когда модель, размещенная в Firebase, недоступна.
Хост-модели в Firebase
Чтобы разместить модель TensorFlow Lite в Firebase:
- В разделе ML Kit консоли Firebase перейдите на вкладку «Пользовательский» .
- Нажмите «Добавить пользовательскую модель» (или «Добавить другую модель » ).
- Укажите имя, которое будет использоваться для идентификации вашей модели в проекте Firebase, затем загрузите файл модели TensorFlow Lite (обычно заканчивающийся на
.tflite
или.lite
). - В манифесте вашего приложения объявите, что требуется разрешение INTERNET:
<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
, указав имя, которое вы присвоили модели при ее загрузке:
Java
FirebaseCustomRemoteModel remoteModel =
new FirebaseCustomRemoteModel.Builder("your_model").build();
Kotlin+KTX
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+KTX
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+KTX
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+KTX
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+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)
}
Если у вас есть только удаленно размещенная модель, вам следует отключить функции, связанные с моделью, например затенить или скрыть часть пользовательского интерфейса, пока вы не подтвердите загрузку модели. Вы можете сделать это, подключив прослушиватель к методу 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+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.
}
Задайте вход и выход модели
Затем настройте входные и выходные форматы интерпретатора модели.
Модель 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
.
Например, модель классификации изображений с плавающей запятой может принимать в качестве входных данных массив значений с float
N x 224x224x3, представляющий пакет трехканальных (RGB) изображений размером N 224x224, и создавать на выходе список из 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+KTX
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+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 } }
Затем создайте объект 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+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 // ... }
Если вызов выполнен успешно, вы можете получить выходные данные, вызвав метод getOutput()
объекта, который передается обработчику успеха. Например:
Java
float[][] output = result.getOutput(0); float[] probabilities = output[0];
Kotlin+KTX
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+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])) }
Приложение: Безопасность модели
Независимо от того, как вы делаете свои модели TensorFlow Lite доступными для ML Kit, ML Kit сохраняет их в стандартном сериализованном формате protobuf в локальном хранилище.
Теоретически это означает, что любой может скопировать вашу модель. Однако на практике большинство моделей настолько зависят от приложения и запутаны оптимизацией, что риск аналогичен риску дизассемблирования и повторного использования вашего кода конкурентами. Тем не менее, вы должны знать об этом риске, прежде чем использовать пользовательскую модель в своем приложении.
В Android API уровня 21 (Lollipop) и новее модель загружается в каталог, который исключен из автоматического резервного копирования .
В Android API уровня 20 и старше модель загружается в каталог с именем com.google.firebase.ml.custom.models
во внутренней памяти приложения. Если вы включили резервное копирование файлов с помощью BackupAgent
, вы можете исключить этот каталог.