En esta guía, se presenta el proceso de ejecutar un modelo LiteRT en el dispositivo para y hacer predicciones basadas en los datos de entrada. Esto se logra con LiteRT. de Python, que usa un orden de grafo estático y un intérprete personalizado (menos dinámico) de memoria para garantizar una carga, inicialización y latencia de ejecución mínimas.
Por lo general, la inferencia de LiteRT sigue los siguientes pasos:
Carga un modelo: Carga el modelo
.tflite
en la memoria, que contiene lo siguiente: el grafo de ejecución del modelo.Transformación de datos: Transforma los datos de entrada al formato esperado. dimensiones. Por lo general, los datos de entrada sin procesar del modelo no coinciden con la entrada el formato de datos que espera el modelo. Por ejemplo, es posible que debas cambiar el tamaño de un o cambiar su formato para que sea compatible con el modelo.
Ejecución de inferencia: Ejecuta el modelo LiteRT para hacer predicciones. Esta implica usar la API de LiteRT para ejecutar el modelo. Incluye algunas como compilar el intérprete y asignar tensores.
Interpretar los tensores de salida: Interpreta los tensores de salida de manera significativa. que será útil en tu aplicación. Por ejemplo, un modelo puede devolver solo un una lista de probabilidades. Depende de ti asignar las probabilidades a áreas relevantes categorías y dar formato al resultado.
En esta guía, se describe cómo acceder al intérprete de LiteRT y cómo realizar una inferencias con C++, Java y Python.
Plataformas compatibles
Las APIs de inferencia de TensorFlow se proporcionan para los dispositivos móviles e incorporados plataformas como Android, iOS y Linux, en varios lenguajes de programación.
En la mayoría de los casos, el diseño de la API refleja una preferencia por el rendimiento sobre la facilidad usar. LiteRT está diseñado para la inferencia rápida en dispositivos pequeños, de manera que las APIs evitan copias innecesarias a expensas de la conveniencia.
En todas las bibliotecas, la API de LiteRT te permite cargar modelos, ingresar entradas y recuperar resultados de inferencia.
Plataforma de Android
En Android, la inferencia LiteRT se puede realizar con las APIs de Java o C++. El Las APIs de Java brindan comodidad y se pueden usar directamente en tu Clases de actividad. Las APIs de C++ ofrecen más flexibilidad y velocidad, pero pueden requerir Escribir wrappers de JNI para mover datos entre capas de Java y C++.
Consulta las secciones C++ y Java para obtener más información. sigue la guía de inicio rápido de Android.
Plataforma iOS
En iOS, LiteRT está disponible en Swift y Objective‐C bibliotecas de iOS. También puedes usar C API directamente en el código de Objective-C.
Consulta Swift, Objective-C y la API de C. o sigue la guía de inicio rápido de iOS.
Plataforma Linux
En plataformas de Linux, puedes ejecutar inferencias con las APIs de LiteRT disponibles C++
Carga y ejecuta un modelo
Para cargar y ejecutar un modelo LiteRT, sigue estos pasos:
- Cargar el modelo en la memoria
- Se está compilando un
Interpreter
basado en un modelo existente. - Configuración de los valores de tensor de entrada.
- Invocar inferencias.
- Salida de valores de tensor
Android (Java)
La API de Java para ejecutar inferencias con LiteRT está diseñada principalmente para usarse
con Android, por lo que está disponible como una dependencia de biblioteca de Android:
com.google.ai.edge.litert
En Java, usarás la clase Interpreter
para cargar un modelo y un modelo de unidad.
la inferencia. En muchos casos, es posible que esta sea la única API que necesites.
Puedes inicializar un Interpreter
con un archivo FlatBuffers (.tflite
):
public Interpreter(@NotNull File modelFile);
O con un MappedByteBuffer
:
public Interpreter(@NotNull MappedByteBuffer mappedByteBuffer);
En ambos casos, debes proporcionar un modelo LiteRT válido; de lo contrario, la API arrojará
IllegalArgumentException
Si usas MappedByteBuffer
para inicializar un
Interpreter
, debe permanecer igual durante toda la vida útil de la
Interpreter
La forma preferida de ejecutar inferencias en un modelo es usar firmas: disponibles para los modelos convertidos a partir de 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");
}
El método runSignature
toma tres argumentos:
Entradas : asigna entradas del nombre de entrada en la firma a una entrada. .
Salidas : asigna la salida del nombre del resultado en la firma al resultado de datos no estructurados.
Signature Name (opcional): Nombre de la firma (puede dejarse en blanco si el modelo tiene una sola firma).
Es otra forma de ejecutar inferencias cuando el modelo no tiene firmas definidas.
Simplemente llama a Interpreter.run()
. Por ejemplo:
try (Interpreter interpreter = new Interpreter(file_of_a_tensorflowlite_model)) {
interpreter.run(input, output);
}
El método run()
toma solo una entrada y muestra solo una salida. Entonces, si tu
modelo tiene varias entradas o salidas, en su lugar, usa lo siguiente:
interpreter.runForMultipleInputsOutputs(inputs, map_of_indices_to_outputs);
En este caso, cada entrada en inputs
corresponde a un tensor de entrada y
map_of_indices_to_outputs
asigna los índices de los tensores de salida al correspondiente
de datos de salida.
En ambos casos, los índices del tensor deben corresponder a los valores que proporcionaste a
el convertidor de LiteRT cuando creaste el modelo. Atención
que el orden de los tensores en input
debe coincidir con el que se asignó a LiteRT
Convertidor.
La clase Interpreter
también proporciona funciones prácticas para que obtengas el
índice de cualquier entrada o salida de modelo con un nombre de operación:
public int getInputIndex(String opName);
public int getOutputIndex(String opName);
Si opName
no es una operación válida en el modelo, arrojará un
IllegalArgumentException
También ten en cuenta que Interpreter
es propietario de los recursos. Para evitar fugas de memoria, la
Los recursos deben liberarse después de que los usa:
interpreter.close();
Para ver un proyecto de ejemplo con Java, consulta el ejemplo de detección de objetos de Android de la app.
Tipos de datos admitidos
Para usar LiteRT, los tipos de datos de los tensores de entrada y salida deben ser uno de los los siguientes tipos primitivos:
float
int
long
byte
También se admiten los tipos String
, pero se codifican de forma diferente a la
tipos primitivos. En particular, la forma de un tensor de cadena dicta el número
y la disposición de cadenas en el tensor, en el que cada elemento es un
una cadena de longitud variable. En este sentido, el tamaño (en bytes) del tensor no se puede
calculada a partir de la forma y el tipo solo y, por lo tanto, las cadenas no pueden
Se proporciona como un argumento ByteBuffer
único y plano.
Si se usan otros tipos de datos, incluidos los tipos encuadrados como Integer
y Float
,
se arroja una IllegalArgumentException
.
Entradas
Cada entrada debe ser un array o un array multidimensional de los
tipos primitivos o un ByteBuffer
sin procesar del tamaño adecuado. Si la entrada es
en un array o array multidimensional, el tensor de entrada asociado será
Se cambiará el tamaño de manera implícita a las dimensiones del array en el momento de la inferencia. Si la entrada es
un ByteBuffer, el llamador primero debe cambiar manualmente el tamaño de la entrada asociada
tensor (a través de Interpreter.resizeInput()
) antes de ejecutar la inferencia.
Cuando uses ByteBuffer
, es preferible usar búferes de bytes directos, ya que esto permite
Interpreter
para evitar copias innecesarias. Si ByteBuffer
es un byte directo
búfer, su orden debe ser ByteOrder.nativeOrder()
. Después de usarlo durante un
inferencia de modelo, debe permanecer igual hasta que finalice la inferencia de modelo.
Salidas
Cada resultado debe ser un array o un array multidimensional de los tipos primitivos o un ByteBuffer del tamaño adecuado. Ten en cuenta que algunos modelos tienen salidas dinámicas, en las que la forma de los tensores de salida puede variar según la entrada. No hay una manera directa de manejar esto con la solución API de inferencia de Java, pero las extensiones planificadas lo harán posible.
iOS (Swift)
La opción Swift
API
está disponible en el Pod TensorFlowLiteSwift
de CocoaPods.
Primero, debes importar el 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...
}
iOS (Objective‐C)
El objetivo Objective-C
API
está disponible en el Pod LiteRTObjC
de CocoaPods.
Primero, debes importar el módulo TensorFlowLiteObjC
.
@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... */ }
API de C en código Objective-C
La API de Objective-C no admite delegados. Para usar delegados con Objective-C, debes llamar directamente a la C subyacente de la API de Google Ads.
#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);
C++
La API de C++ para ejecutar inferencias con LiteRT es compatible con Android, iOS, y plataformas de Linux. La API de C++ en iOS solo está disponible cuando se usa Bazel.
En C++, el modelo se almacena en
FlatBufferModel
.
Encapsula un modelo LiteRT y puedes compilarlo en diferentes
según dónde esté almacenado el modelo.
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);
};
Ahora que tienes el modelo como un objeto FlatBufferModel
, puedes ejecutarlo.
con un
Interpreter
Más de una puede usar un mismo elemento FlatBufferModel
de forma simultánea
Interpreter
Las partes importantes de la API de Interpreter
se muestran en el fragmento de código.
a continuación. Ten en cuenta lo siguiente:
- Los tensores se representan con números enteros para evitar las comparaciones de cadenas. (y cualquier dependencia fija de bibliotecas de cadenas).
- No se debe acceder a un intérprete desde subprocesos simultáneos.
- La asignación de memoria para los tensores de entrada y salida se debe activar llamando
AllocateTensors()
justo después de cambiar el tamaño de los tensores.
El uso más simple de LiteRT con C++ tiene el siguiente aspecto:
// 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 needed.
interpreter->AllocateTensors();
float* input = interpreter->typed_input_tensor<float>(0);
// Fill `input`.
interpreter->Invoke();
float* output = interpreter->typed_output_tensor<float>(0);
Para ver más ejemplos de código, consulta
minimal.cc
y
label_image.cc
Python
La API de Python para ejecutar inferencias usa el
Interpreter
para cargar un modelo y
ejecutar inferencias.
Instala el paquete LiteRT:
$ python3 -m pip install ai-edge-litert
Cómo importar el intérprete de LiteRT
from ai_edge_litert.interpreter import Interpreter
Interpreter = Interpreter(model_path=args.model.file)
En el siguiente ejemplo, se muestra cómo usar el intérprete de Python para cargar una
FlatBuffers (.tflite
) y ejecuta inferencias con datos de entrada aleatorios:
Se recomienda este ejemplo si realizas la conversión de un modelo guardado con un SignatureDef.
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 LiteRT model in LiteRT Interpreter
from ai_edge_litert.interpreter import Interpreter
interpreter = 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'])
Este es otro ejemplo si el modelo no tiene SignatureDefs
definido.
import numpy as np
import tensorflow as tf
# Load the LiteRT model and allocate tensors.
from ai_edge_litert.interpreter import Interpreter
interpreter = Interpreter(TFLITE_FILE_PATH)
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 a cargar el modelo como un archivo .tflite
previamente convertido, puedes
puede combinar tu código con LiteRT.
Compilador
, lo que te permite convertir tu modelo de Keras al formato LiteRT y, luego, ejecutarlo.
inferencia:
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 LiteRT format
converter = tf.lite.TFLiteConverter.from_keras_model(tf.keras.models.Model(inputs=[img], outputs=[out]))
tflite_model = converter.convert()
# Load the LiteRT model and allocate tensors.
from ai_edge_litert.interpreter import Interpreter
interpreter = Interpreter(model_content=tflite_model)
interpreter.allocate_tensors()
# Continue to get tensors and so forth, as shown above...
Para obtener más códigos de muestra de Python, consulta
label_image.py
Ejecuta inferencias con un modelo de forma dinámica
Si quieres ejecutar un modelo con forma de entrada dinámica, cambia el tamaño de la forma de entrada.
antes de ejecutar la inferencia. De lo contrario, la forma None
en los modelos de TensorFlow
Se reemplazará por un marcador de posición 1
en los modelos LiteRT.
En los siguientes ejemplos, se muestra cómo cambiar el tamaño de la forma de entrada antes de la ejecución
la inferencia en diferentes lenguajes. En todos los ejemplos, se supone que la forma de entrada
se define como [1/None, 10]
y debe cambiarse de tamaño a [3, 10]
.
Ejemplo de C++:
// Resize input tensors before allocate tensors
interpreter->ResizeInputTensor(/*tensor_index=*/0, std::vector<int>{3,10});
interpreter->AllocateTensors();
Ejemplo de Python:
# Load the LiteRT model in LiteRT Interpreter
from ai_edge_litert.interpreter import Interpreter
interpreter = 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()