O Dia da Comunidade de ML é dia 9 de novembro! Junte-nos para atualização de TensorFlow, JAX, e mais Saiba mais

Inferência do TensorFlow Lite

O termo refere-se a inferência o processo de execução de um modelo TensorFlow Lite no dispositivo, a fim de fazer previsões com base em dados de entrada. Para executar uma inferência com um modelo TensorFlow Lite, você deve executá-lo através de um intérprete. O interpretador TensorFlow Lite foi projetado para ser simples e rápido. O interpretador 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.

Esta página descreve como o acesso ao intérprete TensorFlow Lite e executar uma inferência usando C ++, Java e Python, além de links para outros recursos para cada plataforma suportada .

Conceitos importantes

A inferência do TensorFlow Lite normalmente segue as seguintes etapas:

  1. Carregando um modelo

    Você deve carregar o .tflite modelo na memória, que contém gráfico de execução do modelo.

  2. Transformando dados

    Os dados de entrada brutos para o modelo geralmente não correspondem ao formato de dados de entrada esperado pelo modelo. Por exemplo, você pode precisar redimensionar uma imagem ou alterar o formato da imagem para ser compatível com o modelo.

  3. Inferência de execução

    Esta etapa envolve o uso da API TensorFlow Lite para executar o modelo. Envolve algumas etapas, como construir o interpretador e alocar tensores, conforme descrito nas seções a seguir.

  4. Produção de interpretação

    Ao receber os resultados da inferência do modelo, você deve interpretar os tensores de uma forma significativa que seja útil em sua aplicação.

    Por exemplo, um modelo pode retornar apenas uma lista de probabilidades. Cabe a você mapear as probabilidades para categorias relevantes e apresentá-las ao seu usuário final.

Plataformas suportadas

APIs de inferência TensorFlow são fornecidos para a maioria dos / as plataformas móveis comuns embutidos, tais como Android , iOS e Linux , em várias linguagens de programação.

Na maioria dos casos, o design da API reflete uma preferência por desempenho em relação à facilidade de uso. O TensorFlow Lite foi projetado para inferência rápida em dispositivos pequenos, então não deve ser surpresa que as APIs tentam evitar cópias desnecessárias em detrimento da conveniência. Da mesma forma, a consistência com as APIs do TensorFlow não era uma meta explícita e algumas variações entre as linguagens são esperadas.

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 fornecem conveniência e podem ser usadas diretamente em suas classes de atividades Android. As APIs C ++ oferecem mais flexibilidade e velocidade, mas podem exigir a gravação de wrappers JNI para mover dados entre as camadas Java e C ++.

Veja abaixo para detalhes sobre como usar C ++ e Java , ou seguir o quickstart Android para um código de tutorial e exemplo.

Gerador de código de wrapper TensorFlow Lite para Android

Para o modelo TensorFlow Lite reforçada com metadados , os desenvolvedores podem usar o gerador de código invólucro TensorFlow Lite Android para criar plataforma de código de embalagem específica. O código de embalagem elimina a necessidade de interagir diretamente com ByteBuffer no Android. Em vez disso, os desenvolvedores podem interagir com o modelo TensorFlow Lite com objetos digitados como Bitmap e Rect . Para mais informações, por favor consulte o gerador de código invólucro TensorFlow Lite Android .

plataforma iOS

No iOS, TensorFlow Lite está disponível com iOS bibliotecas nativas escritos em Swift e Objective-C . Você também pode usar C API diretamente em códigos Objective-C.

Veja abaixo mais detalhes sobre o uso de Swift , Objective-C eo C API , ou seguir o quickstart iOS para um código de tutorial e exemplo.

Plataforma Linux

Em plataformas Linux (incluindo Raspberry Pi ), você pode executar inferências usando APIs TensorFlow Lite disponíveis em C ++ e Python , como mostrado nas seções seguintes.

Executando um modelo

Executar um modelo TensorFlow Lite envolve algumas etapas simples:

  1. Carregue o modelo na memória.
  2. Construir um Interpreter com base em um modelo existente.
  3. Defina os valores do tensor de entrada. (Opcionalmente, redimensione os tensores de entrada se os tamanhos predefinidos não forem desejados.)
  4. Invoque inferência.
  5. Leia os valores do tensor de saída.

As seções a seguir descrevem como essas etapas podem ser realizadas em cada idioma.

Carregue e execute um modelo em Java

Plataforma: Android

A API Java para a execução de uma inferência com TensorFlow Lite foi projetado principalmente para uso com Android, por isso é disponível como uma dependência biblioteca Android: org.tensorflow:tensorflow-lite .

Em Java, você vai usar o Interpreter classe para carregar uma inferência modelo e unidade modelo. Em muitos casos, essa pode ser a única API de que você precisa.

Você pode inicializar um Interpreter usando um .tflite arquivo:

public Interpreter(@NotNull File modelFile);

Ou com um MappedByteBuffer :

public Interpreter(@NotNull MappedByteBuffer mappedByteBuffer);

Em ambos os casos, você deve fornecer um modelo TensorFlow Lite válida ou a API lança IllegalArgumentException . Se você usar MappedByteBuffer para inicializar um Interpreter , deve permanecer inalterada durante todo o tempo de vida do Interpreter .

A maneira preferida 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 runSignature método leva três argumentos:

  • Entradas: Roteiro para as entradas de nome de entrada na assinatura de um objeto de entrada.

  • Saídas: mapa para o mapeamento de saída do nome de saída na assinatura de dados de saída.

  • Assinatura Nome [opcional]: Nome Assinatura (pode ser deixado vazio se o modelo tem assinatura individual).

Outra forma de executar uma inferência quando o modelo não possui assinaturas definidas. Basta ligar para Interpreter.run() . Por exemplo:

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

A run() método tem apenas uma entrada e retorno apenas uma saída. Portanto, se o seu modelo tiver várias entradas ou saídas, use:

interpreter.runForMultipleInputsOutputs(inputs, map_of_indices_to_outputs);

Neste 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 de tensores devem corresponder aos valores que você deu ao TensorFlow Lite Converter quando criou o modelo. Esteja ciente de que a ordem dos tensores na input deve coincidir com a ordem dada ao TensorFlow Lite Converter.

O Interpreter classe também fornece funções convenientes para você obter o índice de qualquer entrada ou saída do modelo usando um nome da operação:

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

Se opName não é uma operação válida no modelo, ele lança uma IllegalArgumentException .

Também tem cuidado que Interpreter possui recursos. Para evitar vazamento de memória, os recursos devem ser liberados após o uso por:

interpreter.close();

Para um projeto de exemplo com Java, consulte a amostra de classificação de imagens Android .

Tipos de dados suportados (em Java)

Para usar o TensorFlow Lite, os tipos de dados dos tensores de entrada e saída devem ser um dos seguintes tipos primitivos:

  • float
  • int
  • long
  • byte

String tipos são suportados também, mas eles são codificados de forma diferente do que os tipos primitivos. Em particular, a forma de um tensor de corda dita o número e a disposição das cordas no tensor, com cada elemento sendo uma corda de comprimento variável. Neste sentido, o (byte) tamanho do tensor não pode ser calculado a partir da forma e tipo sozinho, e, consequentemente, cordas não pode ser fornecida como uma única, plana ByteBuffer argumento.

Se outros tipos de dados, incluindo tipos de caixas, como Integer e Float , são utilizados, um IllegalArgumentException será lançada.

Entradas

Cada entrada deve ser uma matriz ou matriz multi-dimensional dos tipos primitivos suportados, ou uma matéria- ByteBuffer do tamanho apropriado. Se a entrada for uma matriz ou matriz multidimensional, o tensor de entrada associado será redimensionado implicitamente para as dimensões da matriz no momento da inferência. Se a entrada é um ByteBuffer, o chamador deve primeiro redimensionar manualmente o tensor de entrada associados (via Interpreter.resizeInput() ) antes de executar a inferência.

Quando se utiliza ByteBuffer , preferem usar tampões byte directas, como este permite que o Interpreter para evitar cópias desnecessárias. Se o ByteBuffer é um tampão de byte directo, a sua ordem deve ser ByteOrder.nativeOrder() . Depois de ser usado para uma inferência de modelo, ele deve permanecer inalterado até que a inferência de modelo seja concluída.

Saídas

Cada saída deve ser uma matriz ou uma matriz multidimensional dos tipos primitivos suportados ou um ByteBuffer do tamanho apropriado. Observe que alguns modelos têm saídas dinâmicas, onde 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 existente, mas as extensões planejadas tornarão isso possível.

Carregue e execute um modelo em Swift

Plataforma: iOS

A API Swift está disponível em TensorFlowLiteSwift Pod de CocoaPods.

Primeiro, você precisa 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...
}

Carregue e execute um modelo em Objective-C

Plataforma: iOS

O API Objective-C é disponível em TensorFlowLiteObjC vagem de CocoaPods.

Primeiro, você precisa 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 C API em código Objective-C

Atualmente a API Objective-C não oferece suporte a delegados. Para utilizar delegados com código de Objective-C, você precisa chamar diretamente subjacente C API .

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

Carregue e execute um modelo em C ++

Plataformas: Android, iOS e Linux

Em C ++, o modelo é armazenado em FlatBufferModel classe. Ele encapsula um modelo do TensorFlow Lite e você pode construí-lo de várias 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 FlatBufferModel objeto, você pode executá-lo com um Interpreter . Um único FlatBufferModel pode ser utilizado simultaneamente por mais do que um Interpreter .

As partes importantes do Interpreter API são mostrados no fragmento de código abaixo. Deve-se notar que:

  • Tensores são representados por inteiros, a fim de evitar comparações de strings (e qualquer dependência fixa em bibliotecas de strings).
  • Um intérprete não deve ser acessado de threads simultâneos.
  • Alocação de memória para tensores entrada e saída devem ser desencadeada por chamando AllocateTensors() logo após o redimensionamento tensores.

O uso mais simples do TensorFlow Lite com C ++ é assim:

// 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 mais exemplo de código, consulte minimal.cc e label_image.cc .

Carregue e execute um modelo em Python

Plataforma: Linux

A API Python para a execução de uma inferência é fornecido no tf.lite módulo. A partir do qual, você principalmente necessitam apenas tf.lite.Interpreter para carregar um modelo e executar uma inferência.

O exemplo a seguir mostra como usar o interpretador Python para carregar um .tflite arquivo e inferência executado com dados de entrada aleatória:

Este exemplo é recomendado se você estiver convertendo de SavedModel com um SignatureDef definido. 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 tem SignatureDefs definidos.

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 uma alternativa para carregar o modelo como um pré-convertida .tflite arquivo, você pode combinar o seu código com a API TensorFlow Lite Converter Python ( tf.lite.TFLiteConverter ), o que lhe permite converter seu modelo TensorFlow para o formato TensorFlow Lite e depois executar inferência:

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 mais código de exemplo Python, consulte label_image.py .

Dica: Programa help(tf.lite.Interpreter) no terminal Python para obter uma documentação detalhada sobre o intérprete.

Operações com suporte

O TensorFlow Lite é compatível com um subconjunto de operações do TensorFlow com algumas limitações. Para lista completa de operações e limitações ver página Ops TF Lite .