Se usó la API de Cloud Translation para traducir esta página.
Switch to English

Inferencia de TensorFlow Lite

El término inferencia se refiere al proceso de ejecutar un modelo de TensorFlow Lite en el dispositivo para hacer predicciones basadas en datos de entrada. Para realizar una inferencia con un modelo de TensorFlow Lite, debes ejecutarlo 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.

En esta página, se describe cómo acceder al intérprete de TensorFlow Lite y realizar una inferencia con C ++, Java y Python, además de vínculos a otros recursos para cada plataforma compatible .

Conceptos importantes

La inferencia de TensorFlow Lite normalmente sigue los siguientes pasos:

  1. Cargando un modelo

    Debe cargar el modelo .tflite 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 presentarlas a su usuario final.

Plataformas compatibles

Las API de inferencia de TensorFlow se proporcionan para las plataformas móviles / integradas más comunes, 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 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 comodidad. De manera similar, la coherencia con las API de TensorFlow no era 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.

Operaciones compatibles

TensorFlow Lite admite un subconjunto de operaciones de TensorFlow con algunas limitaciones. Para obtener una lista completa de operaciones y limitaciones, consulte la página de Operaciones de TF Lite .

Androide

En Android, la inferencia de TensorFlow Lite se puede realizar utilizando 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 ++.

Consulte a continuación los detalles sobre el uso de C ++ y Java, o siga la guía de inicio rápido de Android para obtener un tutorial y un código de ejemplo.

Generador de código de envoltura de Android TensorFlow Lite

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

iOS

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

Consulte a continuación para obtener detalles sobre el uso de Swift, Objective-C y C API, o siga el inicio rápido de iOS para obtener un tutorial y un código de ejemplo.

Linux

En plataformas Linux (incluida Raspberry Pi ), puede ejecutar inferencias mediante las API de 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. Cree un Interpreter basado en un modelo existente.
  3. Establecer valores de 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

La API de Java para ejecutar una inferencia con TensorFlow Lite está diseñada principalmente para su uso con Android, por lo que está disponible como una dependencia de biblioteca de Android: org.tensorflow:tensorflow-lite .

En Java, usará la clase Interpreter para cargar un modelo e impulsar la inferencia del modelo. En muchos casos, esta puede ser la única API que necesita.

Puede inicializar un Interpreter usando un archivo .tflite :

public Interpreter(@NotNull File modelFile);

O con un MappedByteBuffer :

public Interpreter(@NotNull MappedByteBuffer mappedByteBuffer);

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

Para luego ejecutar una inferencia con el modelo, simplemente llame 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 devuelve solo 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 las inputs corresponde a un tensor de entrada y map_of_indices_to_outputs asigna índices de tensores de salida a los datos de salida correspondientes.

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

La clase Interpreter también proporciona funciones convenientes para que usted obtenga el índice de cualquier entrada o salida del modelo usando 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, lanza una IllegalArgumentException .

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

interpreter.close();

Para ver un proyecto de ejemplo con Java, consulte el ejemplo de 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

También se admiten los tipos de String , pero se codifican de forma diferente a 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 tamaño (byte) del Tensor no se puede calcular solo a partir de la forma y el tipo y, en consecuencia, las cadenas no se pueden proporcionar como un único argumento ByteBuffer plano.

Si se utilizan otros tipos de datos, incluidos los tipos en caja como Integer y Float , se IllegalArgumentException una IllegalArgumentException .

Entradas

Cada entrada debe ser una matriz o matriz multidimensional de los tipos primitivos admitidos, o un ByteBuffer sin 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 un ByteBuffer, la persona que llama primero debe cambiar manualmente el tamaño del tensor de entrada asociado (a través de Interpreter.resizeInput() ) antes de ejecutar la inferencia.

Cuando utilice ByteBuffer , prefiera utilizar búferes de bytes directos, ya que esto permite al Interpreter evitar copias innecesarias. Si ByteBuffer es un búfer de bytes directo, 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 de Swift está disponible en TensorFlowLiteSwift Pod de Cocoapods.

Primero, debe 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...
}

Cargar y ejecutar un modelo en Objective-C

Plataforma: iOS

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

Primero, debe importar el 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...

// Copy the input data to the input `TFLTensor`.
[interpreter copyData:inputData toInputTensorAtIndex:0 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:&amp;error];
if (error != nil) { /* Error handling... */ }

Usando C API en código Objective-C

Actualmente, la API de Objective-C no admite delegados. Para utilizar delegados con código Objective-C, debe llamar directamente a la API C subyacente.

#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 la clase FlatBufferModel . 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 objeto FlatBufferModel , puede ejecutarlo con un Interpreter . Un solo FlatBufferModel puede ser utilizado simultáneamente por más de un Interpreter .

Las partes importantes de la API de Interpreter 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 los tensores de entrada y salida debe activarse llamando a 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 módulo tf.lite . A partir del cual, la mayoría de las tf.lite.Interpreter solo necesita tf.lite.Interpreter para cargar un modelo y ejecutar una inferencia.

El siguiente ejemplo muestra cómo usar el intérprete de Python para cargar un archivo .tflite y ejecutar inferencias con datos de entrada aleatorios:

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 archivo .tflite , puede combinar su código con la API de Python de TensorFlow Lite Converter ( tf.lite.TFLiteConverter ), lo que le permite convertir su modelo de TensorFlow al formato de 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 obtener más código de muestra de Python, consulte label_image.py .

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

Escribe un operador personalizado

Todos los operadores de TensorFlow Lite (tanto personalizados como integrados) se definen mediante una interfaz simple de C puro que consta de cuatro funciones:

typedef struct {
  void* (*init)(TfLiteContext* context, const char* buffer, size_t length);
  void (*free)(TfLiteContext* context, void* buffer);
  TfLiteStatus (*prepare)(TfLiteContext* context, TfLiteNode* node);
  TfLiteStatus (*invoke)(TfLiteContext* context, TfLiteNode* node);
} TfLiteRegistration;

Consulte context.h para obtener detalles sobre TfLiteContext y TfLiteNode . El primero proporciona funciones de notificación de errores y acceso a objetos globales, incluidos todos los tensores. Este último permite que las implementaciones accedan a sus entradas y salidas.

Cuando el intérprete carga un modelo, llama a init() una vez para cada nodo del gráfico. Un init() dado se llamará más de una vez si la operación se usa varias veces en el gráfico. Para operaciones personalizadas, se proporcionará un búfer de configuración, que contiene un búfer flexible que asigna los nombres de los parámetros a sus valores. El búfer está vacío para operaciones integradas porque el intérprete ya ha analizado los parámetros de operación. Las implementaciones de kernel que requieren estado deben inicializarlo aquí y transferir la propiedad al llamador. Para cada llamada a init() , habrá una llamada correspondiente a free() , lo que permitirá que las implementaciones eliminen el búfer que podrían haber asignado en init() .

Siempre que se cambie el tamaño de los tensores de entrada, el intérprete pasará por el gráfico notificando las implementaciones del cambio. Esto les da la oportunidad de cambiar el tamaño de su búfer interno, verificar la validez de las formas y tipos de entrada y recalcular las formas de salida. Todo esto se hace a través de prepare() , y las implementaciones pueden acceder a su estado usando node->user_data .

Finalmente, cada vez que se ejecuta la inferencia, el intérprete recorre el gráfico llamando a invoke() , y aquí también el estado está disponible como node->user_data .

Las operaciones personalizadas se pueden implementar exactamente de la misma manera que las operaciones integradas, definiendo esas cuatro funciones y una función de registro global que generalmente se ve así:

namespace tflite {
namespace ops {
namespace custom {
  TfLiteRegistration* Register_MY_CUSTOM_OP() {
    static TfLiteRegistration r = {my_custom_op::Init,
                                   my_custom_op::Free,
                                   my_custom_op::Prepare,
                                   my_custom_op::Eval};
    return &r;
  }
}  // namespace custom
}  // namespace ops
}  // namespace tflite

Tenga en cuenta que el registro no es automático y se debe realizar una llamada explícita a Register_MY_CUSTOM_OP algún lugar. Si bien el BuiltinOpResolver estándar (disponible en el destino :builtin_ops ) se encarga del registro de los builtins, las operaciones personalizadas deberán recopilarse en bibliotecas personalizadas separadas.

Personaliza la biblioteca del kernel

Detrás de escena, el intérprete cargará una biblioteca de kernels que se asignará para ejecutar cada uno de los operadores en el modelo. Si bien la biblioteca predeterminada solo contiene núcleos integrados, es posible reemplazarla con una biblioteca personalizada.

El intérprete utiliza un OpResolver para traducir códigos y nombres de operadores en código real:

class OpResolver {
  virtual TfLiteRegistration* FindOp(tflite::BuiltinOperator op) const = 0;
  virtual TfLiteRegistration* FindOp(const char* op) const = 0;
  virtual void AddOp(tflite::BuiltinOperator op, TfLiteRegistration* registration) = 0;
  virtual void AddOp(const char* op, TfLiteRegistration* registration) = 0;
};

El uso regular requiere que use BuiltinOpResolver y escriba:

tflite::ops::builtin::BuiltinOpResolver resolver;

Opcionalmente, puede registrar operaciones personalizadas (antes de pasar el solucionador al InterpreterBuilder ):

resolver.AddOp("MY_CUSTOM_OP", Register_MY_CUSTOM_OP());

Si se considera que el conjunto de operaciones integradas es demasiado grande, se podría generar un nuevo OpResolver código en función de un subconjunto determinado de operaciones, posiblemente solo las contenidas en un modelo determinado. Este es el equivalente al registro selectivo de TensorFlow (y hay una versión simple disponible en el directorio de tools ).