O termo inferência refere-se ao processo de execução de um modelo do TensorFlow Lite no dispositivo para fazer previsões com base nos dados de entrada. Para realizar uma inferência com um modelo do TensorFlow Lite, é preciso executá-lo usando um intérprete. O intérprete do TensorFlow Lite foi projetado para ser enxuto e rápido. O intérprete usa uma ordenação de gráfico estático e um alocador de memória personalizado (menos dinâmico) para garantir carga, inicialização e latência de execução mínimas.
Nesta página, descrevemos como acessar o intérprete do TensorFlow Lite e realizar uma inferência usando C++, Java e Python, além de links para outros recursos para cada plataforma compatível.
Conceitos importantes
A inferência no TensorFlow Lite normalmente segue as seguintes etapas:
Como carregar um modelo
Carregue o modelo
.tflite
na memória, que contém o gráfico de execução do modelo.Transformação de dados
Os dados brutos de entrada do modelo geralmente não correspondem ao formato de dados de entrada esperado pelo modelo. Por exemplo, talvez seja necessário redimensionar uma imagem ou alterar o formato da imagem para que ela seja compatível com o modelo.
Como executar inferência
Esta etapa envolve o uso da API TensorFlow Lite para executar o modelo. Ela envolve algumas etapas, como criar o intérprete e alocar tensores, conforme descrito nas seções a seguir.
Como interpretar a saída
Quando você recebe resultados da inferência de modelo, precisa interpretar os tensores de uma maneira significativa que seja útil para seu aplicativo.
Por exemplo, um modelo pode retornar apenas uma lista de probabilidades. Cabe a você mapear as probabilidades para categorias relevantes e apresentá-las ao usuário final.
Plataformas compatíveis
As APIs de inferência do TensorFlow são fornecidas para a maioria das plataformas móveis/incorporadas comuns, como Android, iOS e Linux, em várias linguagens de programação.
Na maioria dos casos, o design da API reflete a preferência pela performance em vez da facilidade de uso. O TensorFlow Lite foi projetado para inferência rápida em dispositivos pequenos. Por isso, não é surpresa que as APIs tentem evitar cópias desnecessárias em detrimento da conveniência. Da mesma forma, a consistência com as APIs do TensorFlow não era um objetivo explícito, e é de se esperar alguma variação entre idiomas.
Em todas as bibliotecas, a API TensorFlow Lite permite carregar modelos, alimentar entradas e recuperar saídas de inferência.
Plataforma Android
No Android, a inferência do TensorFlow Lite pode ser realizada usando APIs Java ou C++. As APIs Java oferecem conveniência e podem ser usadas diretamente nas classes de atividade do Android. As APIs C++ oferecem mais flexibilidade e velocidade, mas podem exigir a criação de wrappers JNI para mover dados entre camadas Java e C++.
Veja abaixo detalhes sobre como usar o C++ e Java ou siga o Guia de início rápido do Android para ver um tutorial e um código de exemplo.
Gerador de código do wrapper do TensorFlow Lite para Android
Para o modelo do TensorFlow Lite aprimorado com metadados,
os desenvolvedores podem usar o gerador de código do wrapper do TensorFlow Lite para Android para criar
um código de wrapper específico da plataforma. O código do wrapper elimina a necessidade de interagir
diretamente com ByteBuffer
no Android. Em vez disso, os desenvolvedores podem interagir com o
modelo do TensorFlow Lite com objetos tipados, como Bitmap
e Rect
. Para mais
informações, consulte o
gerador de código do wrapper do TensorFlow Lite para Android.
Plataforma iOS
No iOS, o TensorFlow Lite está disponível com bibliotecas nativas do iOS escritas em Swift e Objective-C. Também é possível usar a API C diretamente nos códigos Objective-C.
Confira abaixo detalhes sobre como usar o Swift, Objective-C e a API C ou siga o guia de início rápido do iOS para ver um tutorial e um código de exemplo.
Plataforma Linux
Em plataformas Linux (incluindo Raspberry Pi), é possível executar inferências usando as APIs do TensorFlow Lite disponíveis em C++ e Python, conforme mostrado nas seções a seguir.
Como executar um modelo
A execução de um modelo do TensorFlow Lite envolve algumas etapas simples:
- Carregar o modelo na memória.
- Crie um
Interpreter
com base em um modelo atual. - Define os valores do tensor de entrada. Se os tamanhos predefinidos não forem desejados, redimensione os tensores de entrada.
- Invoque inferência.
- Lê os valores do tensor de saída.
As seções a seguir descrevem como realizar essas etapas em cada idioma.
Carregar e executar um modelo em Java
Plataforma: Android
A API Java para executar uma inferência com o TensorFlow Lite foi projetada principalmente
para uso com o Android. Por isso, está disponível como uma dependência de biblioteca do Android:
org.tensorflow:tensorflow-lite
.
Em Java, você usará a classe Interpreter
para carregar um modelo e impulsionar a
inferência dele. Em muitos casos, essa pode ser a única API de que você precisa.
É possível inicializar um Interpreter
usando um arquivo .tflite
:
public Interpreter(@NotNull File modelFile);
Ou com um MappedByteBuffer
:
public Interpreter(@NotNull MappedByteBuffer mappedByteBuffer);
Em ambos os casos, é necessário fornecer um modelo válido do TensorFlow Lite, ou a API vai gerar
IllegalArgumentException
. Se você usar MappedByteBuffer
para inicializar um
Interpreter
, ele precisará permanecer inalterado durante todo o ciclo de vida da
Interpreter
.
A maneira preferencial de executar inferência em um modelo é usar assinaturas. Disponível para modelos convertidos a partir do TensorFlow 2.5.
try (Interpreter interpreter = new Interpreter(file_of_tensorflowlite_model)) {
Map<String, Object> inputs = new HashMap<>();
inputs.put("input_1", input1);
inputs.put("input_2", input2);
Map<String, Object> outputs = new HashMap<>();
outputs.put("output_1", output1);
interpreter.runSignature(inputs, outputs, "mySignature");
}
O método runSignature
usa três argumentos:
Entradas : mapeia entradas do nome da entrada na assinatura para um objeto de entrada.
Saídas : mapeia o mapeamento de saída do nome da saída na assinatura para os dados de saída.
Nome da assinatura (opcional): nome da assinatura (pode ser deixado em branco se o modelo tiver uma única assinatura).
Outra maneira de executar uma inferência quando o modelo não
tem assinaturas definidas.
Basta chamar Interpreter.run()
. Exemplo:
try (Interpreter interpreter = new Interpreter(file_of_a_tensorflowlite_model)) {
interpreter.run(input, output);
}
O método run()
usa apenas uma entrada e retorna apenas uma saída. Portanto, se o modelo tiver várias entradas ou saídas, use:
interpreter.runForMultipleInputsOutputs(inputs, map_of_indices_to_outputs);
Nesse caso, cada entrada em inputs
corresponde a um tensor de entrada, e
map_of_indices_to_outputs
mapeia índices de tensores de saída para os dados
de saída correspondentes.
Em ambos os casos, os índices do tensor precisam corresponder aos valores fornecidos ao
TensorFlow Lite Converter quando você criou o modelo. Esteja
ciente de que a ordem dos tensores em input
precisa corresponder à ordem fornecida ao
TensorFlow Lite Converter.
A classe Interpreter
também fornece funções convenientes para você conseguir o
índice de qualquer entrada ou saída de modelo usando um nome de operação:
public int getInputIndex(String opName);
public int getOutputIndex(String opName);
Se opName
não é uma operação válida no modelo, ele gera uma
IllegalArgumentException
.
Esteja ciente também de que Interpreter
tem recursos. Para evitar vazamento de memória, os
recursos precisam ser liberados após o uso por:
interpreter.close();
Para conferir um exemplo de projeto com Java, consulte o exemplo de classificação de imagem do Android (link em inglês).
Tipos de dados compatíveis (em Java)
Para usar o TensorFlow Lite, os tipos de dados dos tensores de entrada e saída precisam ser um dos seguintes tipos primitivos:
float
int
long
byte
Os tipos String
também são compatíveis, mas são codificados de maneira diferente dos
tipos primitivos. Em particular, o formato de um Tensor de string determina o número e a organização das strings no Tensor, com cada elemento em si sendo uma string de comprimento variável. Nesse sentido, o tamanho (byte) do tensor não pode ser calculado apenas pela forma e pelo tipo e, consequentemente, as strings não podem ser fornecidas como um único argumento ByteBuffer
plano.
Se outros tipos de dados forem usados, incluindo em caixa, como Integer
e Float
,
uma IllegalArgumentException
será gerada.
Entradas
Cada entrada precisa ser uma matriz ou uma matriz multidimensional dos tipos primitivos compatíveis ou um ByteBuffer
bruto do tamanho apropriado. Se a entrada for uma matriz ou uma matriz multidimensional, o tensor de entrada associado será implicitamente redimensionado para as dimensões da matriz no momento da inferência. Se a entrada for
um ByteBuffer, o autor da chamada precisa primeiro redimensionar manualmente o tensor de entrada
associado (via Interpreter.resizeInput()
) antes de executar a inferência.
Ao usar ByteBuffer
, prefira usar buffers de byte diretos, porque isso permite que o
Interpreter
evite cópias desnecessárias. Se ByteBuffer
for um buffer de byte direto, a ordem precisará ser ByteOrder.nativeOrder()
. Depois de ser usada para uma
inferência de modelo, ela precisa permanecer inalterada até que a inferência do modelo seja concluída.
Saídas
Cada saída precisa ser uma matriz ou matriz multidimensional dos tipos primitivos compatíveis ou um ByteBuffer do tamanho apropriado. Observe que alguns modelos têm saídas dinâmicas, em que a forma dos tensores de saída pode variar dependendo da entrada. Não há uma maneira direta de lidar com isso com a API de inferência Java atual, mas as extensões planejadas tornarão isso possível.
Carregar e executar um modelo no Swift
Plataforma: iOS
A
API Swift
está disponível no pod TensorFlowLiteSwift
do Cocoapods.
Primeiro, você precisa importar o módulo TensorFlowLite
.
import TensorFlowLite
// Getting model path
guard
let modelPath = Bundle.main.path(forResource: "model", ofType: "tflite")
else {
// Error handling...
}
do {
// Initialize an interpreter with the model.
let interpreter = try Interpreter(modelPath: modelPath)
// Allocate memory for the model's input `Tensor`s.
try interpreter.allocateTensors()
let inputData: Data // Should be initialized
// input data preparation...
// Copy the input data to the input `Tensor`.
try self.interpreter.copy(inputData, toInputAt: 0)
// Run inference by invoking the `Interpreter`.
try self.interpreter.invoke()
// Get the output `Tensor`
let outputTensor = try self.interpreter.output(at: 0)
// Copy output to `Data` to process the inference results.
let outputSize = outputTensor.shape.dimensions.reduce(1, {x, y in x * y})
let outputData =
UnsafeMutableBufferPointer<Float32>.allocate(capacity: outputSize)
outputTensor.data.copyBytes(to: outputData)
if (error != nil) { /* Error handling... */ }
} catch error {
// Error handling...
}
Carregar e executar um modelo em Objective-C
Plataforma: iOS
A
API Objective-C
está disponível no pod TensorFlowLiteObjC
do Cocoapods.
Primeiro, você precisa importar o módulo TensorFlowLite
.
@import TensorFlowLite;
NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"model"
ofType:@"tflite"];
NSError *error;
// Initialize an interpreter with the model.
TFLInterpreter *interpreter = [[TFLInterpreter alloc] initWithModelPath:modelPath
error:&error];
if (error != nil) { /* Error handling... */ }
// Allocate memory for the model's input `TFLTensor`s.
[interpreter allocateTensorsWithError:&error];
if (error != nil) { /* Error handling... */ }
NSMutableData *inputData; // Should be initialized
// input data preparation...
// Get the input `TFLTensor`
TFLTensor *inputTensor = [interpreter inputTensorAtIndex:0 error:&error];
if (error != nil) { /* Error handling... */ }
// Copy the input data to the input `TFLTensor`.
[inputTensor copyData:inputData error:&error];
if (error != nil) { /* Error handling... */ }
// Run inference by invoking the `TFLInterpreter`.
[interpreter invokeWithError:&error];
if (error != nil) { /* Error handling... */ }
// Get the output `TFLTensor`
TFLTensor *outputTensor = [interpreter outputTensorAtIndex:0 error:&error];
if (error != nil) { /* Error handling... */ }
// Copy output to `NSData` to process the inference results.
NSData *outputData = [outputTensor dataWithError:&error];
if (error != nil) { /* Error handling... */ }
Como usar a API C no código Objective-C
Atualmente, a API Objective-C não suporta delegados. Para usar delegados com o código Objective-C, chame diretamente a API C subjacente.
#include "tensorflow/lite/c/c_api.h"
TfLiteModel* model = TfLiteModelCreateFromFile([modelPath UTF8String]);
TfLiteInterpreterOptions* options = TfLiteInterpreterOptionsCreate();
// Create the interpreter.
TfLiteInterpreter* interpreter = TfLiteInterpreterCreate(model, options);
// Allocate tensors and populate the input tensor data.
TfLiteInterpreterAllocateTensors(interpreter);
TfLiteTensor* input_tensor =
TfLiteInterpreterGetInputTensor(interpreter, 0);
TfLiteTensorCopyFromBuffer(input_tensor, input.data(),
input.size() * sizeof(float));
// Execute inference.
TfLiteInterpreterInvoke(interpreter);
// Extract the output tensor data.
const TfLiteTensor* output_tensor =
TfLiteInterpreterGetOutputTensor(interpreter, 0);
TfLiteTensorCopyToBuffer(output_tensor, output.data(),
output.size() * sizeof(float));
// Dispose of the model and interpreter objects.
TfLiteInterpreterDelete(interpreter);
TfLiteInterpreterOptionsDelete(options);
TfLiteModelDelete(model);
Carregar e executar um modelo em C++
Plataformas: Android, iOS e Linux
Em C++, o modelo é armazenado na
classe
FlatBufferModel
. Ele encapsula um modelo do TensorFlow Lite e é possível criá-lo de algumas
maneiras diferentes, dependendo de onde o modelo está armazenado:
class FlatBufferModel {
// Build a model based on a file. Return a nullptr in case of failure.
static std::unique_ptr<FlatBufferModel> BuildFromFile(
const char* filename,
ErrorReporter* error_reporter);
// Build a model based on a pre-loaded flatbuffer. The caller retains
// ownership of the buffer and should keep it alive until the returned object
// is destroyed. Return a nullptr in case of failure.
static std::unique_ptr<FlatBufferModel> BuildFromBuffer(
const char* buffer,
size_t buffer_size,
ErrorReporter* error_reporter);
};
Agora que você tem o modelo como um objeto FlatBufferModel
, é possível executá-lo
com um
Interpreter
.
Um único FlatBufferModel
pode ser usado simultaneamente por mais de uma
Interpreter
.
As partes importantes da API Interpreter
são mostradas no snippet de código abaixo. Observações importantes:
- Os tensores são representados por números inteiros para evitar comparações de strings e qualquer dependência fixa em bibliotecas de strings.
- Um intérprete não pode ser acessado em threads simultâneas.
- A alocação de memória para tensores de entrada e saída precisa ser acionada chamando
AllocateTensors()
logo após o redimensionamento dos tensores.
O uso mais simples do TensorFlow Lite com C++ é semelhante ao seguinte:
// Load the model
std::unique_ptr<tflite::FlatBufferModel> model =
tflite::FlatBufferModel::BuildFromFile(filename);
// Build the interpreter
tflite::ops::builtin::BuiltinOpResolver resolver;
std::unique_ptr<tflite::Interpreter> interpreter;
tflite::InterpreterBuilder(*model, resolver)(&interpreter);
// Resize input tensors, if desired.
interpreter->AllocateTensors();
float* input = interpreter->typed_input_tensor<float>(0);
// Fill `input`.
interpreter->Invoke();
float* output = interpreter->typed_output_tensor<float>(0);
Para ver mais exemplos de códigos, consulte
minimal.cc
e
label_image.cc
.
Carregar e executar um modelo em Python
Plataforma: Linux
A API Python para executar uma inferência precisa principalmente de
tf.lite.Interpreter
para carregar um modelo e executar uma inferência.
O exemplo a seguir mostra como usar o intérprete de Python para carregar um arquivo .tflite
e executar a inferência com dados de entrada aleatórios:
Este exemplo é recomendado se você estiver convertendo do SavedModel com uma SignatureDef definida. Disponível a partir do TensorFlow 2.5
class TestModel(tf.Module):
def __init__(self):
super(TestModel, self).__init__()
@tf.function(input_signature=[tf.TensorSpec(shape=[1, 10], dtype=tf.float32)])
def add(self, x):
'''
Simple method that accepts single input 'x' and returns 'x' + 4.
'''
# Name the output 'result' for convenience.
return {'result' : x + 4}
SAVED_MODEL_PATH = 'content/saved_models/test_variable'
TFLITE_FILE_PATH = 'content/test_variable.tflite'
# Save the model
module = TestModel()
# You can omit the signatures argument and a default signature name will be
# created with name 'serving_default'.
tf.saved_model.save(
module, SAVED_MODEL_PATH,
signatures={'my_signature':module.add.get_concrete_function()})
# Convert the model using TFLiteConverter
converter = tf.lite.TFLiteConverter.from_saved_model(SAVED_MODEL_PATH)
tflite_model = converter.convert()
with open(TFLITE_FILE_PATH, 'wb') as f:
f.write(tflite_model)
# Load the TFLite model in TFLite Interpreter
interpreter = tf.lite.Interpreter(TFLITE_FILE_PATH)
# There is only 1 signature defined in the model,
# so it will return it by default.
# If there are multiple signatures then we can pass the name.
my_signature = interpreter.get_signature_runner()
# my_signature is callable with input as arguments.
output = my_signature(x=tf.constant([1.0], shape=(1,10), dtype=tf.float32))
# 'output' is dictionary with all outputs from the inference.
# In this case we have single output 'result'.
print(output['result'])
Outro exemplo se o modelo não tiver SignatureDefs definido.
import numpy as np
import tensorflow as tf
# Load the TFLite model and allocate tensors.
interpreter = tf.lite.Interpreter(model_path="converted_model.tflite")
interpreter.allocate_tensors()
# Get input and output tensors.
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()
# Test the model on random input data.
input_shape = input_details[0]['shape']
input_data = np.array(np.random.random_sample(input_shape), dtype=np.float32)
interpreter.set_tensor(input_details[0]['index'], input_data)
interpreter.invoke()
# The function `get_tensor()` returns a copy of the tensor data.
# Use `tensor()` in order to get a pointer to the tensor.
output_data = interpreter.get_tensor(output_details[0]['index'])
print(output_data)
Como alternativa ao carregamento do modelo como um arquivo .tflite
pré-convertido, é possível
combinar seu código com a
API TensorFlow Lite Converter Python.
Assim, você pode converter seu modelo Keras para o
formato TensorFlow Lite e, em seguida, executar a inferência:
import numpy as np
import tensorflow as tf
img = tf.keras.Input(shape=(64, 64, 3), name="img")
const = tf.constant([1., 2., 3.]) + tf.constant([1., 4., 4.])
val = img + const
out = tf.identity(val, name="out")
# Convert to TF Lite format
converter = tf.lite.TFLiteConverter.from_keras_model(tf.keras.models.Model(inputs=[img], outputs=[out]))
tflite_model = converter.convert()
# Load the TFLite model and allocate tensors.
interpreter = tf.lite.Interpreter(model_content=tflite_model)
interpreter.allocate_tensors()
# Continue to get tensors and so forth, as shown above...
Para mais códigos de amostra em Python, consulte
label_image.py
.
Executar inferência com modelo de forma dinâmico
Se você quiser executar um modelo com forma de entrada dinâmica, redimensione a forma de entrada antes de executar a inferência.
Caso contrário, a forma None
nos modelos do Tensorflow será substituída por um
marcador de 1
nos modelos do TFLite.
Os exemplos a seguir mostram como redimensionar a forma de entrada antes
de executar a inferência em diferentes idiomas.
Todos os exemplos presumem que a forma de entrada está definida como [1/None, 10]
e
precisa ser redimensionada para [3, 10]
.
Exemplo em C++:
// Resize input tensors before allocate tensors
interpreter->ResizeInputTensor(/*tensor_index=*/0, std::vector<int>{3,10});
interpreter->AllocateTensors();
Exemplo em Python:
# Load the TFLite model in TFLite Interpreter
interpreter = tf.lite.Interpreter(model_path=TFLITE_FILE_PATH)
# Resize input shape for dynamic shape model and allocate tensor
interpreter.resize_tensor_input(interpreter.get_input_details()[0]['index'], [3, 10])
interpreter.allocate_tensors()
# Get input and output tensors.
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()
Operações compatíveis
O TensorFlow Lite é compatível com um subconjunto de operações do TensorFlow com algumas limitações. Para uma lista completa de operações e limitações, consulte a página de operações do TF Lite.