Разработчики мобильных приложений обычно взаимодействуют с типизированными объектами, такими как растровые изображения, или примитивами, такими как целые числа. Однако API-интерфейс интерпретатора LiteRT, который запускает модель машинного обучения на устройстве, использует тензоры в форме ByteBuffer, отладка которых и манипулирование ими может оказаться затруднительным. Библиотека поддержки LiteRT Android предназначена для облегчения обработки входных и выходных данных моделей LiteRT и упрощения использования интерпретатора LiteRT.
Начиная
Импортируйте зависимость Gradle и другие настройки.
Скопируйте файл модели .tflite
в каталог ресурсов модуля Android, где будет запускаться модель. Укажите, что файл не должен быть сжат, и добавьте библиотеку LiteRT в файл build.gradle
модуля:
android {
// Other settings
// Specify tflite file should not be compressed for the app apk
aaptOptions {
noCompress "tflite"
}
}
dependencies {
// Other dependencies
// Import tflite dependencies
implementation 'org.tensorflow:tensorflow-lite:0.0.0-nightly-SNAPSHOT'
// The GPU delegate library is optional. Depend on it as needed.
implementation 'com.google.ai.edge.litert:litert-gpu:0.0.0-nightly-SNAPSHOT'
implementation 'com.google.ai.edge.litert:litert-support:0.0.0-nightly-SNAPSHOT'
}
Изучите AAR библиотеки поддержки LiteRT, размещенную в MavenCentral, для различных версий библиотеки поддержки.
Базовые манипуляции и преобразование изображений
Библиотека поддержки LiteRT содержит набор основных методов манипулирования изображениями, таких как обрезка и изменение размера. Чтобы использовать его, создайте ImagePreprocessor
и добавьте необходимые операции. Чтобы преобразовать изображение в тензорный формат, необходимый интерпретатору LiteRT, создайте TensorImage
, который будет использоваться в качестве входных данных:
import org.tensorflow.lite.DataType;
import org.tensorflow.lite.support.image.ImageProcessor;
import org.tensorflow.lite.support.image.TensorImage;
import org.tensorflow.lite.support.image.ops.ResizeOp;
// Initialization code
// Create an ImageProcessor with all ops required. For more ops, please
// refer to the ImageProcessor Architecture section in this README.
ImageProcessor imageProcessor =
new ImageProcessor.Builder()
.add(new ResizeOp(224, 224, ResizeOp.ResizeMethod.BILINEAR))
.build();
// Create a TensorImage object. This creates the tensor of the corresponding
// tensor type (uint8 in this case) that the LiteRT interpreter needs.
TensorImage tensorImage = new TensorImage(DataType.UINT8);
// Analysis code for every frame
// Preprocess the image
tensorImage.load(bitmap);
tensorImage = imageProcessor.process(tensorImage);
DataType
тензора можно прочитать через библиотеку экстрактора метаданных , а также другую информацию о модели.
Базовая обработка аудиоданных
Библиотека поддержки LiteRT также определяет класс TensorAudio
, обертывающий некоторые базовые методы обработки аудиоданных. В основном он используется вместе с AudioRecord и записывает образцы звука в кольцевой буфер.
import android.media.AudioRecord;
import org.tensorflow.lite.support.audio.TensorAudio;
// Create an `AudioRecord` instance.
AudioRecord record = AudioRecord(...)
// Create a `TensorAudio` object from Android AudioFormat.
TensorAudio tensorAudio = new TensorAudio(record.getFormat(), size)
// Load all audio samples available in the AudioRecord without blocking.
tensorAudio.load(record)
// Get the `TensorBuffer` for inference.
TensorBuffer buffer = tensorAudio.getTensorBuffer()
Создайте выходные объекты и запустите модель
Перед запуском модели нам необходимо создать объекты-контейнеры, в которых будет храниться результат:
import org.tensorflow.lite.DataType;
import org.tensorflow.lite.support.tensorbuffer.TensorBuffer;
// Create a container for the result and specify that this is a quantized model.
// Hence, the 'DataType' is defined as UINT8 (8-bit unsigned integer)
TensorBuffer probabilityBuffer =
TensorBuffer.createFixedSize(new int[]{1, 1001}, DataType.UINT8);
Загрузка модели и запуск вывода:
import java.nio.MappedByteBuffer;
import org.tensorflow.lite.InterpreterFactory;
import org.tensorflow.lite.InterpreterApi;
// Initialise the model
try{
MappedByteBuffer tfliteModel
= FileUtil.loadMappedFile(activity,
"mobilenet_v1_1.0_224_quant.tflite");
InterpreterApi tflite = new InterpreterFactory().create(
tfliteModel, new InterpreterApi.Options());
} catch (IOException e){
Log.e("tfliteSupport", "Error reading model", e);
}
// Running inference
if(null != tflite) {
tflite.run(tImage.getBuffer(), probabilityBuffer.getBuffer());
}
Доступ к результату
Разработчики могут получить доступ к выходным данным напрямую через probabilityBuffer.getFloatArray()
. Если модель выдает квантованный результат, не забудьте преобразовать результат. Для квантовой модели MobileNet разработчику необходимо разделить каждое выходное значение на 255, чтобы получить вероятность в диапазоне от 0 (наименее вероятно) до 1 (наиболее вероятно) для каждой категории.
Необязательно: сопоставление результатов с метками.
Разработчики также могут при необходимости сопоставить результаты с метками. Сначала скопируйте текстовый файл, содержащий метки, в каталог ресурсов модуля. Затем загрузите файл метки, используя следующий код:
import org.tensorflow.lite.support.common.FileUtil;
final String ASSOCIATED_AXIS_LABELS = "labels.txt";
List<String> associatedAxisLabels = null;
try {
associatedAxisLabels = FileUtil.loadLabels(this, ASSOCIATED_AXIS_LABELS);
} catch (IOException e) {
Log.e("tfliteSupport", "Error reading label file", e);
}
Следующий фрагмент демонстрирует, как связать вероятности с метками категорий:
import java.util.Map;
import org.tensorflow.lite.support.common.TensorProcessor;
import org.tensorflow.lite.support.common.ops.NormalizeOp;
import org.tensorflow.lite.support.label.TensorLabel;
// Post-processor which dequantize the result
TensorProcessor probabilityProcessor =
new TensorProcessor.Builder().add(new NormalizeOp(0, 255)).build();
if (null != associatedAxisLabels) {
// Map of labels and their corresponding probability
TensorLabel labels = new TensorLabel(associatedAxisLabels,
probabilityProcessor.process(probabilityBuffer));
// Create a map to access the result based on label
Map<String, Float> floatMap = labels.getMapWithFloatValue();
}
Текущий охват вариантов использования
Текущая версия библиотеки поддержки LiteRT включает:
- общие типы данных (float, uint8, изображения, аудио и массив этих объектов) в качестве входных и выходных данных моделей tflite.
- основные операции с изображениями (обрезка изображения, изменение размера и поворот).
- нормализация и квантование
- файловые утилиты
В будущих версиях будет улучшена поддержка текстовых приложений.
Архитектура процессора изображений
Конструкция ImageProcessor
позволяла заранее определять операции манипулирования изображениями и оптимизировать их в процессе сборки. ImageProcessor
в настоящее время поддерживает три основные операции предварительной обработки, как описано в трех комментариях в приведенном ниже фрагменте кода:
import org.tensorflow.lite.support.common.ops.NormalizeOp;
import org.tensorflow.lite.support.common.ops.QuantizeOp;
import org.tensorflow.lite.support.image.ops.ResizeOp;
import org.tensorflow.lite.support.image.ops.ResizeWithCropOrPadOp;
import org.tensorflow.lite.support.image.ops.Rot90Op;
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int size = height > width ? width : height;
ImageProcessor imageProcessor =
new ImageProcessor.Builder()
// Center crop the image to the largest square possible
.add(new ResizeWithCropOrPadOp(size, size))
// Resize using Bilinear or Nearest neighbour
.add(new ResizeOp(224, 224, ResizeOp.ResizeMethod.BILINEAR));
// Rotation counter-clockwise in 90 degree increments
.add(new Rot90Op(rotateDegrees / 90))
.add(new NormalizeOp(127.5, 127.5))
.add(new QuantizeOp(128.0, 1/128.0))
.build();
Более подробную информацию о нормализации и квантовании см. здесь .
Конечная цель библиотеки поддержки — поддержка всех преобразований tf.image
. Это означает, что преобразование будет таким же, как и в TensorFlow, и реализация будет независима от операционной системы.
Разработчики также могут создавать собственные процессоры. В этих случаях важно, чтобы данные были согласованы с процессом обучения, то есть одна и та же предварительная обработка должна применяться как к обучению, так и к выводу, чтобы повысить воспроизводимость.
Квантование
При запуске объектов ввода или вывода, таких как TensorImage
или TensorBuffer
вам необходимо указать их типы: DataType.UINT8
или DataType.FLOAT32
.
TensorImage tensorImage = new TensorImage(DataType.UINT8);
TensorBuffer probabilityBuffer =
TensorBuffer.createFixedSize(new int[]{1, 1001}, DataType.UINT8);
TensorProcessor
можно использовать для квантования входных тензоров или деквантования выходных тензоров. Например, при обработке квантованного вывода TensorBuffer
разработчик может использовать DequantizeOp
для деквантования результата до вероятности с плавающей запятой между 0 и 1:
import org.tensorflow.lite.support.common.TensorProcessor;
// Post-processor which dequantize the result
TensorProcessor probabilityProcessor =
new TensorProcessor.Builder().add(new DequantizeOp(0, 1/255.0)).build();
TensorBuffer dequantizedBuffer = probabilityProcessor.process(probabilityBuffer);
Параметры квантования тензора можно прочитать через библиотеку экстрактора метаданных .