Aide à protéger la Grande barrière de corail avec tensorflow sur Kaggle Rejoignez Défi

Inférence TensorFlow Lite

L'inférence terme fait référence au processus d'exécution d' un modèle tensorflow Lite sur l'appareil afin de faire des prédictions basées sur des données d'entrée. Pour effectuer une inférence avec un modèle tensorflow Lite, vous devez l' exécuter par un interprète. L'interpréteur TensorFlow Lite est conçu pour être simple et rapide. L'interpréteur utilise un ordre de graphe statique et un allocateur de mémoire personnalisé (moins dynamique) pour garantir une charge, une initialisation et une latence d'exécution minimales.

Cette page décrit comment accéder à l'interpréteur tensorflow Lite et effectuer une inférence en C ++, Java et Python, ainsi que des liens vers d' autres ressources pour chaque plate - forme supportée .

Notions importantes

L'inférence TensorFlow Lite suit généralement les étapes suivantes :

  1. Chargement d'un modèle

    Vous devez charger le .tflite modèle en mémoire, qui contient le graphe d'exécution du modèle.

  2. Transformer les données

    Les données d'entrée brutes du modèle ne correspondent généralement pas au format de données d'entrée attendu par le modèle. Par exemple, vous devrez peut-être redimensionner une image ou modifier le format de l'image pour qu'elle soit compatible avec le modèle.

  3. Exécution de l'inférence

    Cette étape implique l'utilisation de l'API TensorFlow Lite pour exécuter le modèle. Cela implique quelques étapes telles que la construction de l'interpréteur et l'allocation des tenseurs, comme décrit dans les sections suivantes.

  4. Interprétation de la sortie

    Lorsque vous recevez les résultats de l'inférence du modèle, vous devez interpréter les tenseurs d'une manière significative qui est utile dans votre application.

    Par exemple, un modèle peut renvoyer uniquement une liste de probabilités. C'est à vous de mapper les probabilités aux catégories pertinentes et de les présenter à votre utilisateur final.

Plateformes prises en charge

API d'inférence tensorflow sont fournies pour la plupart des plates - formes mobiles / intégrées communes telles que Android , iOS et Linux , dans plusieurs langages de programmation.

Dans la plupart des cas, la conception de l'API reflète une préférence pour les performances plutôt que pour la facilité d'utilisation. TensorFlow Lite est conçu pour une inférence rapide sur les petits appareils, il n'est donc pas surprenant que les API essaient d'éviter les copies inutiles au détriment de la commodité. De même, la cohérence avec les API TensorFlow n'était pas un objectif explicite et une certaine variation entre les langues est à prévoir.

Dans toutes les bibliothèques, l'API TensorFlow Lite vous permet de charger des modèles, d'alimenter des entrées et de récupérer des sorties d'inférence.

Plateforme Android

Sur Android, l'inférence TensorFlow Lite peut être effectuée à l'aide des API Java ou C++. Les API Java sont pratiques et peuvent être utilisées directement dans vos classes d'activité Android. Les API C++ offrent plus de flexibilité et de vitesse, mais peuvent nécessiter l'écriture de wrappers JNI pour déplacer les données entre les couches Java et C++.

Voir ci - dessous pour plus de détails sur l' utilisation de C ++ et Java , ou suivez le QuickStart Android un code tutoriel et un exemple.

Générateur de code wrapper Android TensorFlow Lite

Pour tensorflow modèle Lite améliorée avec les métadonnées , les développeurs peuvent utiliser le générateur de code wrapper Android tensorflow Lite pour créer un code d'emballage spécifique de la plate - forme. Le code d'emballage supprime la nécessité d'interagir directement avec ByteBuffer sur Android. Au lieu de cela, les développeurs peuvent interagir avec le modèle Lite tensorflow avec des objets typés tels que Bitmap et Rect . Pour plus d' informations, s'il vous plaît se référer au générateur de code wrapper Android tensorflow Lite .

Plateforme iOS

Sur iOS, tensorflow Lite est disponible avec les bibliothèques iOS natives écrites dans Swift et Objective-C . Vous pouvez également utiliser l' API C directement dans les codes Objective-C.

Voir ci - dessous pour plus de détails sur l' utilisation de Swift , Objective-C et l' API C , ou suivez le QuickStart iOS un code tutoriel et un exemple.

Plateforme Linux

Sur les plateformes Linux (y compris Raspberry Pi ), vous pouvez exécuter des inférences en utilisant les API tensorflow Lite disponibles en C ++ et Python , comme indiqué dans les sections suivantes.

Exécuter un modèle

L'exécution d'un modèle TensorFlow Lite implique quelques étapes simples :

  1. Chargez le modèle en mémoire.
  2. Construire un Interpreter basé sur un modèle existant.
  3. Définissez les valeurs du tenseur d'entrée. (Facultativement redimensionner les tenseurs d'entrée si les tailles prédéfinies ne sont pas souhaitées.)
  4. Invoquer l'inférence.
  5. Lire les valeurs du tenseur de sortie.

Les sections suivantes décrivent comment ces étapes peuvent être effectuées dans chaque langue.

Charger et exécuter un modèle en Java

Plateforme : Android

L'API Java pour exécuter une inférence avec tensorflow Lite est principalement conçu pour être utilisé avec Android, il est disponible en tant que dépendance bibliothèque Android: org.tensorflow:tensorflow-lite .

En Java, vous utiliserez l' Interpreter de classe pour charger une inférence de modèle de modèle et d' entraînement. Dans de nombreux cas, cela peut être la seule API dont vous avez besoin.

Vous pouvez initialiser un Interpreter à l' aide d' un .tflite fichier:

public Interpreter(@NotNull File modelFile);

Ou avec un MappedByteBuffer :

public Interpreter(@NotNull MappedByteBuffer mappedByteBuffer);

Dans les deux cas, vous devez fournir un modèle valide tensorflow Lite ou l'API jette IllegalArgumentException . Si vous utilisez MappedByteBuffer pour initialiser un Interpreter , il doit rester inchangé pendant toute la vie de l' Interpreter .

La meilleure façon d'exécuter l'inférence sur un modèle consiste à utiliser des signatures - Disponible pour les modèles convertis à 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");
}

La runSignature méthode prend trois arguments:

  • Entrées: la carte pour les entrées de nom d'entrée dans la signature d'un objet d'entrée.

  • Sorties: la carte pour la cartographie de sortie du nom de sortie dans la signature de données de sortie.

  • Signature Nom [facultatif]: Signature Nom (peut - être laissé vide si le modèle a la signature unique).

Une autre façon d'exécuter une inférence lorsque le modèle n'a pas de signatures définies. Il suffit d' appeler Interpreter.run() . Par exemple:

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

Le run() méthode prend une seule entrée et retourne une seule sortie. Donc, si votre modèle a plusieurs entrées ou plusieurs sorties, utilisez plutôt :

interpreter.runForMultipleInputsOutputs(inputs, map_of_indices_to_outputs);

Dans ce cas, chaque entrée dans les inputs correspond à un tenseur d'entrée et map_of_indices_to_outputs cartes des indices de tenseurs de sortie aux données de sortie correspondantes.

Dans les deux cas, les indices du tenseur doivent correspondre aux valeurs que vous avez données au convertisseur Lite tensorflow lorsque vous avez créé le modèle. Sachez que l'ordre de tenseurs en input doit correspondre à l'ordre donné au tensorflow Lite Converter.

L' Interpreter classe offre des fonctions pratiques pour vous aussi d'obtenir l'indice d'une entrée de modèle ou de sortie en utilisant un nom d'opération:

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

Si opName n'est pas une opération valide dans le modèle, il jette un IllegalArgumentException .

Méfiez - vous également que l' Interpreter possède des ressources. Pour éviter les fuites mémoire, les ressources doivent être libérées après utilisation par :

interpreter.close();

Pour un exemple de projet avec Java, voir l' exemple de classification d'image Android .

Types de données pris en charge (en Java)

Pour utiliser TensorFlow Lite, les types de données des tenseurs d'entrée et de sortie doivent être l'un des types primitifs suivants :

  • float
  • int
  • long
  • byte

String types sont également pris en charge, mais ils sont codés différemment que les types primitifs. En particulier, la forme d'une chaîne Tensor dicte le nombre et la disposition des chaînes dans le Tensor, chaque élément étant lui-même une chaîne de longueur variable. En ce sens, la taille (en octets) du tenseur ne peut pas être calculé à partir de la forme et le type seul, et par conséquent , les chaînes ne peut pas être fourni en un seul plat ByteBuffer argument.

Si d' autres types de données, y compris les types comme boxed Integer et Float , sont utilisés, un IllegalArgumentException sera jeté.

Contributions

Chaque entrée doit être un tableau de réseau ou multi-dimensionnelle des types primitifs pris en charge, ou une première ByteBuffer de la taille appropriée. Si l'entrée est un tableau ou un tableau multidimensionnel, le tenseur d'entrée associé sera implicitement redimensionné aux dimensions du tableau au moment de l'inférence. Si l'entrée est un ByteBuffer, l'appelant doit d'abord redimensionner manuellement le tenseur d'entrée associé (via Interpreter.resizeInput() ) avant d' inférence en cours d' exécution.

Lorsque vous utilisez ByteBuffer , préfèrent utiliser des tampons d'octets directs, car cela permet à l' Interpreter d'éviter des copies inutiles. Si le ByteBuffer est un tampon d'octets directe, l'ordre doit être ByteOrder.nativeOrder() . Après avoir été utilisé pour une inférence de modèle, il doit rester inchangé jusqu'à ce que l'inférence de modèle soit terminée.

Les sorties

Chaque sortie doit être un tableau ou un tableau multidimensionnel des types primitifs pris en charge, ou un ByteBuffer de la taille appropriée. Notez que certains modèles ont des sorties dynamiques, où la forme des tenseurs de sortie peut varier en fonction de l'entrée. Il n'y a pas de moyen simple de gérer cela avec l'API d'inférence Java existante, mais les extensions prévues rendront cela possible.

Charger et exécuter un modèle dans Swift

Plateforme : iOS

L' API Swift est disponible en TensorFlowLiteSwift Pod de Cocoapods.

Tout d' abord, vous devez importer TensorFlowLite module.

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

Charger et exécuter un modèle en Objective-C

Plateforme : iOS

L' API Objective-C est disponible en TensorFlowLiteObjC Pod de Cocoapods.

Tout d' abord, vous devez importer TensorFlowLite module.

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

Utilisation de l'API C dans le code Objective-C

Actuellement, l'API Objective-C ne prend pas en charge les délégués. Pour les délégués d'utilisation avec le code Objective-C, vous devez appeler directement sous - jacente 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);

Charger et exécuter un modèle en C++

Plateformes : Android, iOS et Linux

En C ++, le modèle est stocké dans FlatBufferModel classe. Il encapsule un modèle TensorFlow Lite et vous pouvez le créer de différentes manières, selon l'endroit où le modèle est stocké :

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

Maintenant que vous avez le modèle comme FlatBufferModel objet, vous pouvez l' exécuter avec un Interpreter . Un seul FlatBufferModel peut être utilisé simultanément par plus d'un Interpreter .

Les parties importantes de l' Interpreter API sont indiquées dans le code snippet ci - dessous. Il faut noter que :

  • Les tenseurs sont représentés par des entiers, afin d'éviter les comparaisons de chaînes (et toute dépendance fixe sur les bibliothèques de chaînes).
  • Un interpréteur ne doit pas être accessible à partir de threads concurrents.
  • L' allocation de mémoire pour tenseurs d'entrée et de sortie doit être déclenchée en appelant AllocateTensors() juste après le redimensionnement tenseurs.

L'utilisation la plus simple de TensorFlow Lite avec C++ ressemble à ceci :

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

Pour plus d' exemple de code, voir minimal.cc et label_image.cc .

Charger et exécuter un modèle en Python

Plateforme : Linux

L'API Python pour l' exécution d' une inférence est fournie dans le tf.lite module. À partir de laquelle, vous avez besoin la plupart du temps que tf.lite.Interpreter pour charger un modèle et exécuter une inférence.

L'exemple suivant montre comment utiliser l'interpréteur Python pour charger un .tflite fichier et l' inférence exécuté avec des données d'entrée au hasard:

Cet exemple est recommandé si vous effectuez une conversion à partir de SavedModel avec un SignatureDef défini. Disponible à 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'])

Un autre exemple si le modèle n'a pas défini de SignatureDefs.

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)

Comme alternative au chargement du modèle en tant que pré-converti .tflite fichier, vous pouvez combiner votre code avec l' API Converter Lite tensorflow Python ( tf.lite.TFLiteConverter ), vous permettant de convertir votre modèle tensorflow dans le tensorflow le format Lite et exécuter l'inférence :

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

Pour plus d' exemple de code Python, voir label_image.py .

Astuce: Exécuter help(tf.lite.Interpreter) dans le terminal Python pour obtenir une documentation détaillée sur l'interprète.

Opérations prises en charge

TensorFlow Lite prend en charge un sous-ensemble d'opérations TensorFlow avec certaines limitations. Pour la liste complète des opérations et des limitations voir TF Lite page Ops .