Os desenvolvedores de aplicativos para dispositivos móveis normalmente interagem com objetos tipados, como bitmaps, ou primitivos, como números inteiros. No entanto, a API de intérprete do TensorFlow Lite que executa o modelo de machine learning no dispositivo usa tensores na forma de ByteBuffer, que podem ser difíceis de depurar e manipular. A Biblioteca de Suporte do TensorFlow Lite para Android foi projetada para processar a entrada e a saída de modelos do TensorFlow Lite e facilitar o uso do intérprete do TensorFlow Lite.
Como começar
Importar dependência do Gradle e outras configurações
Copie o arquivo modelo .tflite
para o diretório de assets do módulo do Android
em que o modelo será executado. Especifique que o arquivo não pode ser compactado e
adicione a biblioteca do TensorFlow Lite ao arquivo build.gradle
do módulo:
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 'org.tensorflow:tensorflow-lite-gpu:0.0.0-nightly-SNAPSHOT'
implementation 'org.tensorflow:tensorflow-lite-support:0.0.0-nightly-SNAPSHOT'
}
Confira o AAR da Biblioteca de Suporte do TensorFlow Lite hospedado no MavenCentral para diferentes versões da Biblioteca de Suporte.
Manipulação e conversão básicas de imagens
A Biblioteca de Suporte do TensorFlow Lite tem um pacote de métodos básicos de manipulação
de imagem, como cortar e redimensionar. Para usá-la, crie um ImagePreprocessor
e
adicione as operações necessárias. Para converter a imagem para o formato de tensor
exigido pelo intérprete do TensorFlow Lite, crie um TensorImage
a ser usado
como entrada:
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 TensorFlow Lite interpreter needs.
TensorImage tensorImage = new TensorImage(DataType.UINT8);
// Analysis code for every frame
// Preprocess the image
tensorImage.load(bitmap);
tensorImage = imageProcessor.process(tensorImage);
O DataType
de um tensor pode ser lido por meio da biblioteca de extração de metadados, bem como de outras informações do modelo.
Processamento básico de dados de áudio
A Biblioteca de Suporte do TensorFlow Lite também define uma classe TensorAudio
que encapsula
alguns métodos básicos de processamento de dados de áudio. Ele é usado principalmente com
AudioRecord
e captura amostras de áudio em um buffer de anel.
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()
Criar objetos de saída e executar o modelo
Antes de executar o modelo, é preciso criar os objetos de contêiner que armazenarão o resultado:
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);
Como carregar o modelo e executar a inferência:
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());
}
Acessar o resultado
Os desenvolvedores podem acessar a saída diretamente por
probabilityBuffer.getFloatArray()
. Se o modelo produzir uma saída quantizada,
lembre-se de converter o resultado. Para o modelo quantizado do MobileNet, o desenvolvedor
precisa dividir cada valor de saída por 255 para chegar à probabilidade que varia de
0 (menos provável) a 1 (mais provável) para cada categoria.
Opcional: mapeamento de resultados para rótulos
Os desenvolvedores também podem mapear os resultados para os rótulos. Primeiro, copie o arquivo de texto que contém os rótulos para o diretório de assets do módulo. Em seguida, carregue o arquivo de rótulo usando o seguinte código:
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);
}
O snippet a seguir demonstra como associar as probabilidades aos rótulos de categoria:
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();
}
Cobertura do caso de uso atual
A versão atual da Biblioteca de Suporte do TensorFlow Lite abrange:
- tipos de dados comuns (flutuante, uint8, imagens, áudio e matriz desses objetos) como entradas e saídas de modelos tflite.
- operações básicas de imagem (cortar, redimensionar e girar imagens).
- normalização e quantização
- utilitários de arquivo
Versões futuras vão melhorar o suporte a aplicativos de texto.
Arquitetura do ImageProcessor
O design do ImageProcessor
permitiu que as operações de manipulação de imagem fossem
definidas antecipadamente e otimizadas durante o processo de build. Atualmente, o ImageProcessor
é compatível com três operações básicas de pré-processamento, conforme descrito nos
três comentários no snippet de código abaixo:
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();
Confira mais detalhes neste link sobre normalização e quantização.
O objetivo final da Biblioteca de Suporte é oferecer suporte a todas
as transformações
tf.image
. Isso significa que a transformação será igual à do TensorFlow, e a implementação será independente do sistema operacional.
Os desenvolvedores também podem criar processadores personalizados. Nesses casos, é importante estar alinhado ao processo de treinamento, ou seja, o mesmo pré-processamento precisa se aplicar ao treinamento e à inferência para aumentar a reprodutibilidade.
Quantização.
Ao iniciar objetos de entrada ou saída, como TensorImage
ou TensorBuffer
,
você precisa especificar os tipos como DataType.UINT8
ou DataType.FLOAT32
.
TensorImage tensorImage = new TensorImage(DataType.UINT8);
TensorBuffer probabilityBuffer =
TensorBuffer.createFixedSize(new int[]{1, 1001}, DataType.UINT8);
O TensorProcessor
pode ser usado para quantificar os tensores de entrada ou desquantificar os tensores de saída. Por exemplo, ao processar uma saída quantizada TensorBuffer
, o
desenvolvedor pode usar DequantizeOp
para desquantizar o resultado para uma probabilidade de ponto flutuante
entre 0 e 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);
Os parâmetros de quantização de um tensor podem ser lidos por meio da biblioteca do extrator de metadados.