¡El Día de la Comunidad de ML es el 9 de noviembre! Únase a nosotros para recibir actualizaciones de TensorFlow, JAX, y más Más información

Inferencia de TensorFlow Lite

El término inferencia se refiere al proceso de ejecución de un modelo TensorFlow Lite en el dispositivo con el fin de hacer predicciones basadas en datos de entrada. Para realizar una inferencia con un modelo TensorFlow Lite, debe ejecutar a través de un intérprete. El intérprete de TensorFlow Lite está diseñado para ser ágil y rápido. El intérprete utiliza un orden de gráficos estáticos y un asignador de memoria personalizado (menos dinámico) para garantizar una carga mínima, inicialización y latencia de ejecución.

Esta página describe cómo el acceso al intérprete TensorFlow Lite y realizar una inferencia usando C ++, Java y Python, además de enlaces a otros recursos para cada plataforma soportada .

Conceptos importantes

La inferencia de TensorFlow Lite normalmente sigue los siguientes pasos:

  1. Cargando un modelo

    Debe cargar el .tflite modelo en la memoria, que contiene el gráfico de ejecución del modelo.

  2. Transformando datos

    Los datos de entrada sin procesar para el modelo generalmente no coinciden con el formato de datos de entrada esperado por el modelo. Por ejemplo, es posible que deba cambiar el tamaño de una imagen o cambiar el formato de la imagen para que sea compatible con el modelo.

  3. Ejecutando inferencia

    Este paso implica el uso de la API de TensorFlow Lite para ejecutar el modelo. Implica algunos pasos, como construir el intérprete y asignar tensores, como se describe en las siguientes secciones.

  4. Interpretación de la salida

    Cuando recibe resultados de la inferencia del modelo, debe interpretar los tensores de una manera significativa que sea útil en su aplicación.

    Por ejemplo, un modelo puede devolver solo una lista de probabilidades. Depende de usted asignar las probabilidades a categorías relevantes y presentárselas a su usuario final.

Plataformas compatibles

API de inferencia TensorFlow se proporcionan para la mayoría de plataformas móviles comunes / embebidas como Android , iOS y Linux , en múltiples 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 de uso. TensorFlow Lite está diseñado para inferencias rápidas en dispositivos pequeños, por lo que no debería sorprender que las API intenten evitar copias innecesarias a expensas de la conveniencia. De manera similar, la coherencia con las API de TensorFlow no fue un objetivo explícito y se espera alguna variación entre los idiomas.

En todas las bibliotecas, la API de TensorFlow Lite te permite cargar modelos, alimentar entradas y recuperar salidas de inferencia.

Plataforma Android

En Android, la inferencia de TensorFlow Lite se puede realizar mediante las API de Java o C ++. Las API de Java brindan comodidad y se pueden usar directamente dentro de sus clases de actividad de Android. Las API de C ++ ofrecen más flexibilidad y velocidad, pero pueden requerir la escritura de envoltorios JNI para mover datos entre las capas de Java y C ++.

Véase más abajo para detalles sobre el uso de C ++ y Java , o seguir el inicio rápido de Android para un código de tutorial y ejemplo.

Generador de código de contenedor de Android TensorFlow Lite

Para el modelo Lite TensorFlow mejorada con metadatos , los desarrolladores pueden utilizar el generador de código de contenedor TensorFlow Lite Android para crear código específico envoltorio plataforma. El código de contenedor elimina la necesidad de interactuar directamente con ByteBuffer en Android. En lugar de ello, los desarrolladores pueden interactuar con el modelo TensorFlow Lite con objetos escritos tales como Bitmap y Rect . Para obtener más información, consulte el generador de código de contenedor TensorFlow Lite Android .

Plataforma iOS

En iOS, TensorFlow Lite está disponible con IOS bibliotecas nativas escritas en Swift y Objective-C . También puede utilizar la API de C directamente en los códigos de Objective-C.

Véase más abajo para obtener más información sobre el uso de Swift , Objective-C y la API de C , o seguir la guía de inicio rápido iOS para un código de tutorial y ejemplo.

Plataforma Linux

En las plataformas Linux (incluyendo Frambuesa Pi ), puede ejecutar inferencias utilizando las API TensorFlow Lite disponibles en C ++ y Python , como se muestra en las siguientes secciones.

Ejecutando un modelo

La ejecución de un modelo de TensorFlow Lite implica algunos pasos simples:

  1. Cargue el modelo en la memoria.
  2. Construir un Interpreter basado en un modelo existente.
  3. Establece los valores del tensor de entrada. (Opcionalmente, cambie el tamaño de los tensores de entrada si no desea los tamaños predefinidos).
  4. Invocar inferencia.
  5. Leer los valores del tensor de salida.

Las siguientes secciones describen cómo se pueden realizar estos pasos en cada idioma.

Cargar y ejecutar un modelo en Java

Plataforma: Android

El API Java para ejecutar una inferencia con TensorFlow Lite está diseñado principalmente para su uso con Android, por lo que es disponible como un Android dependencias de bibliotecas: org.tensorflow:tensorflow-lite .

En Java, va a utilizar el Interpreter de clase para cargar un modelo y la unidad modelo de inferencia. En muchos casos, esta puede ser la única API que necesita.

Puede inicializar un Interpreter utilizando un .tflite archivo:

public Interpreter(@NotNull File modelFile);

O con un MappedByteBuffer :

public Interpreter(@NotNull MappedByteBuffer mappedByteBuffer);

En ambos casos, debe proporcionar un modelo válido TensorFlow Lite o la API de tiros IllegalArgumentException . Si utiliza MappedByteBuffer para inicializar un Interpreter , debe permanecer sin cambios durante toda la vida del Interpreter .

La forma preferida de ejecutar inferencias en un modelo es usar firmas: disponible para 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 runSignature método tiene tres argumentos:

  • Entradas: Mapa de las entradas de nombre de la entrada en la firma de un objeto de entrada.

  • Salidas: mapa de asignación de salida del nombre de salida en la firma de datos de salida.

  • Firma Nombre [opcional]: nombre de la firma (se puede dejar en blanco si el modelo tiene una sola firma).

Otra forma de ejecutar una inferencia cuando el modelo no tiene firmas definidas. Simplemente llame Interpreter.run() . Por ejemplo:

try (Interpreter interpreter = new Interpreter(file_of_a_tensorflowlite_model)) {
  interpreter.run(input, output);
}

El run() método toma sólo una entrada y sólo devuelve una salida. Entonces, si su modelo tiene múltiples entradas o múltiples salidas, use en su lugar:

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 los mapas de los índices de los tensores de salida a los datos de salida correspondientes.

En ambos casos, los índices tensoriales deben corresponder a los valores que le dio a la TensorFlow Lite convertidor cuando se creó el modelo. Tenga en cuenta que el orden de los tensores de input debe coincidir con el orden dado a la TensorFlow Lite Converter.

El Interpreter clase también ofrece funciones convenientes para usted para obtener el índice de cualquier modelo de entrada o salida usando un nombre de la operación:

public int getInputIndex(String opName);
public int getOutputIndex(String opName);

Si opName no es una operación válida en el modelo, se produce una IllegalArgumentException .

También ten en cuenta que Interpreter posee recursos. Para evitar la pérdida de memoria, los recursos deben ser liberados después de su uso por:

interpreter.close();

Para un proyecto de ejemplo con Java, consulte la muestra de la clasificación de imágenes de Android .

Tipos de datos admitidos (en Java)

Para usar TensorFlow Lite, los tipos de datos de los tensores de entrada y salida deben ser uno de los siguientes tipos primitivos:

  • float
  • int
  • long
  • byte

String tipos también son compatibles, sino que se codifican de manera diferente que los tipos primitivos. En particular, la forma de un tensor de cuerda dicta el número y la disposición de las cuerdas en el tensor, y cada elemento en sí es una cuerda de longitud variable. En este sentido, el (byte) tamaño del tensor no se puede calcular de la forma y el tipo solo, y por consiguiente las cadenas no se puede proporcionar como una sola, plana ByteBuffer argumento.

Si otros tipos de datos, incluyendo los tipos en caja como Integer y Float , se utilizan, una IllegalArgumentException será lanzada.

Entradas

Cada entrada debe ser una matriz o multi-dimensional array de los tipos primitivos soportados, o una prima ByteBuffer del tamaño apropiado. Si la entrada es una matriz o una matriz multidimensional, el tensor de entrada asociado se redimensionará implícitamente a las dimensiones de la matriz en el momento de la inferencia. Si la entrada es una ByteBuffer, la persona que llama debe primero cambiar el tamaño manualmente el tensor de entrada asociada (a través de Interpreter.resizeInput() ) antes de ejecutar la inferencia.

Al utilizar ByteBuffer , prefieren usar tampones byte directos, ya que esto permite que el Interpreter para evitar copias innecesarias. Si el ByteBuffer es una memoria intermedia de bytes directa, su orden debe ser ByteOrder.nativeOrder() . Una vez que se utiliza para una inferencia de modelo, debe permanecer sin cambios hasta que finalice la inferencia de modelo.

Salidas

Cada salida debe ser una matriz o matriz multidimensional de los tipos primitivos admitidos, o un ByteBuffer del tamaño apropiado. Tenga en cuenta que algunos modelos tienen salidas dinámicas, donde la forma de los tensores de salida puede variar según la entrada. No hay una forma sencilla de manejar esto con la API de inferencia de Java existente, pero las extensiones planificadas lo harán posible.

Cargar y ejecutar un modelo en Swift

Plataforma: iOS

La API Swift está disponible en TensorFlowLiteSwift vaina de Cocoapods.

En primer lugar, es necesario importar TensorFlowLite módulo.

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...
}

Cargar y ejecutar un modelo en Objective-C

Plataforma: iOS

La API de Objective-C está disponible en TensorFlowLiteObjC vaina de Cocoapods.

En primer lugar, es necesario importar TensorFlowLite módulo.

@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... */ }

Usando API C en código Objective-C

Actualmente, la API de Objective-C no admite delegados. Con el fin de delegados de uso con código de Objective-C, es necesario llamar directamente subyacente API C .

#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);

Cargar y ejecutar un modelo en C ++

Plataformas: Android, iOS y Linux

En C ++, el modelo se almacena en FlatBufferModel clase. Encapsula un modelo de TensorFlow Lite y puedes compilarlo de dos formas diferentes, según dónde se almacene 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 tiene el modelo como un FlatBufferModel objeto, puede ejecutarlo con un Interpreter . Un único FlatBufferModel puede ser utilizado simultáneamente por más de un Interpreter .

Las partes importantes de la Interpreter API se muestran en el fragmento de código a continuación. Se debe notar que:

  • Los tensores están representados por números enteros, para evitar comparaciones de cadenas (y cualquier dependencia fija de las bibliotecas de cadenas).
  • No se debe acceder a un intérprete desde subprocesos simultáneos.
  • La asignación de memoria para la entrada y salida tensores debe ser activado llamando AllocateTensors() justo después de cambiar el tamaño de los tensores.

El uso más simple de TensorFlow Lite con C ++ se ve así:

// 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 obtener más código de ejemplo, véase minimal.cc y label_image.cc .

Cargar y ejecutar un modelo en Python

Plataforma: Linux

La API de Python para ejecutar una inferencia se proporciona en el tf.lite módulo. A partir de lo cual, en su mayoría sólo necesita tf.lite.Interpreter para cargar un modelo y ejecutar una inferencia.

El siguiente ejemplo muestra cómo utilizar el intérprete de Python para cargar un .tflite archivo y la inferencia correr con los datos de entrada al azar:

Se recomienda este ejemplo si está convirtiendo desde SavedModel con una SignatureDef definida. Disponible a partir de 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'])

Otro ejemplo si el modelo no tiene 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 a cargar el modelo como un pre-convertida .tflite archivo, puede combinar su código con el API TensorFlow Lite Convertidor Python ( tf.lite.TFLiteConverter ), que le permite convertir su modelo TensorFlow en el formato TensorFlow Lite, y luego ejecutar inferencia:

import numpy as np
import tensorflow as tf

img = tf.placeholder(name="img", dtype=tf.float32, shape=(1, 64, 64, 3))
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
with tf.Session() as sess:
  converter = tf.lite.TFLiteConverter.from_session(sess, [img], [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 más ejemplos de código Python, consulte label_image.py .

Consejo: Programa help(tf.lite.Interpreter) en el terminal de Python para obtener documentación detallada sobre el intérprete.

Operaciones admitidas

TensorFlow Lite admite un subconjunto de operaciones de TensorFlow con algunas limitaciones. Para la lista completa de las operaciones y limitaciones véase la página Operaciones TF Lite .