Délégué à l'accélération GPU pour iOS

L'utilisation d'unités de traitement graphique (GPU) pour exécuter vos modèles d'apprentissage automatique (ML) peut améliorer considérablement les performances de votre modèle et l'expérience utilisateur de vos applications compatibles ML. Sur les appareils iOS, vous pouvez activer l'exécution de vos modèles accélérée par GPU à l'aide d'un délégué . Les délégués agissent en tant que pilotes matériels pour TensorFlow Lite, vous permettant d'exécuter le code de votre modèle sur des processeurs GPU.

Cette page explique comment activer l'accélération GPU pour les modèles TensorFlow Lite dans les applications iOS. Pour plus d'informations sur l'utilisation du délégué GPU pour TensorFlow Lite, y compris les bonnes pratiques et les techniques avancées, consultez la page des délégués GPU .

Utiliser le GPU avec l'API Interpreter

L' API TensorFlow Lite Interpreter fournit un ensemble d'API à usage général pour créer des applications d'apprentissage automatique. Les instructions suivantes vous guident dans l'ajout de la prise en charge GPU à une application iOS. Ce guide suppose que vous disposez déjà d'une application iOS capable d'exécuter avec succès un modèle ML avec TensorFlow Lite.

Modifier le Podfile pour inclure la prise en charge du GPU

À partir de la version TensorFlow Lite 2.3.0, le délégué GPU est exclu du pod pour réduire la taille binaire. Vous pouvez les inclure en spécifiant une sous-spécification pour le pod TensorFlowLiteSwift :

pod 'TensorFlowLiteSwift/Metal', '~> 0.0.1-nightly',

OU

pod 'TensorFlowLiteSwift', '~> 0.0.1-nightly', :subspecs => ['Metal']

Vous pouvez également utiliser TensorFlowLiteObjC ou TensorFlowLiteC si vous souhaitez utiliser Objective-C, disponible pour les versions 2.4.0 et supérieures, ou l'API C.

Initialiser et utiliser le délégué GPU

Vous pouvez utiliser le délégué GPU avec l' API TensorFlow Lite Interpreter avec un certain nombre de langages de programmation. Swift et Objective-C sont recommandés, mais vous pouvez également utiliser C++ et C. L'utilisation de C est requise si vous utilisez une version de TensorFlow Lite antérieure à 2.4. Les exemples de code suivants expliquent comment utiliser le délégué avec chacune de ces langues.

Rapide

import TensorFlowLite

// Load model ...

// Initialize TensorFlow Lite interpreter with the GPU delegate.
let delegate = MetalDelegate()
if let interpreter = try Interpreter(modelPath: modelPath,
                                      delegates: [delegate]) {
  // Run inference ...
}
      

Objectif c

// Import module when using CocoaPods with module support
@import TFLTensorFlowLite;

// Or import following headers manually
#import "tensorflow/lite/objc/apis/TFLMetalDelegate.h"
#import "tensorflow/lite/objc/apis/TFLTensorFlowLite.h"

// Initialize GPU delegate
TFLMetalDelegate* metalDelegate = [[TFLMetalDelegate alloc] init];

// Initialize interpreter with model path and GPU delegate
TFLInterpreterOptions* options = [[TFLInterpreterOptions alloc] init];
NSError* error = nil;
TFLInterpreter* interpreter = [[TFLInterpreter alloc]
                                initWithModelPath:modelPath
                                          options:options
                                        delegates:@[ metalDelegate ]
                                            error:&error];
if (error != nil) { /* Error handling... */ }

if (![interpreter allocateTensorsWithError:&error]) { /* Error handling... */ }
if (error != nil) { /* Error handling... */ }

// Run inference ...
      

C++

// Set up interpreter.
auto model = FlatBufferModel::BuildFromFile(model_path);
if (!model) return false;
tflite::ops::builtin::BuiltinOpResolver op_resolver;
std::unique_ptr<Interpreter> interpreter;
InterpreterBuilder(*model, op_resolver)(&interpreter);

// Prepare GPU delegate.
auto* delegate = TFLGpuDelegateCreate(/*default options=*/nullptr);
if (interpreter->ModifyGraphWithDelegate(delegate) != kTfLiteOk) return false;

// Run inference.
WriteToInputTensor(interpreter->typed_input_tensor<float>(0));
if (interpreter->Invoke() != kTfLiteOk) return false;
ReadFromOutputTensor(interpreter->typed_output_tensor<float>(0));

// Clean up.
TFLGpuDelegateDelete(delegate);
      

C (avant 2.4.0)

#include "tensorflow/lite/c/c_api.h"
#include "tensorflow/lite/delegates/gpu/metal_delegate.h"

// Initialize model
TfLiteModel* model = TfLiteModelCreateFromFile(model_path);

// Initialize interpreter with GPU delegate
TfLiteInterpreterOptions* options = TfLiteInterpreterOptionsCreate();
TfLiteDelegate* delegate = TFLGPUDelegateCreate(nil);  // default config
TfLiteInterpreterOptionsAddDelegate(options, metal_delegate);
TfLiteInterpreter* interpreter = TfLiteInterpreterCreate(model, options);
TfLiteInterpreterOptionsDelete(options);

TfLiteInterpreterAllocateTensors(interpreter);

NSMutableData *input_data = [NSMutableData dataWithLength:input_size * sizeof(float)];
NSMutableData *output_data = [NSMutableData dataWithLength:output_size * sizeof(float)];
TfLiteTensor* input = TfLiteInterpreterGetInputTensor(interpreter, 0);
const TfLiteTensor* output = TfLiteInterpreterGetOutputTensor(interpreter, 0);

// Run inference
TfLiteTensorCopyFromBuffer(input, inputData.bytes, inputData.length);
TfLiteInterpreterInvoke(interpreter);
TfLiteTensorCopyToBuffer(output, outputData.mutableBytes, outputData.length);

// Clean up
TfLiteInterpreterDelete(interpreter);
TFLGpuDelegateDelete(metal_delegate);
TfLiteModelDelete(model);
      

Notes d'utilisation du langage de l'API GPU

  • Les versions de TensorFlow Lite antérieures à 2.4.0 ne peuvent utiliser que l'API C pour Objective-C.
  • L'API C++ n'est disponible que lorsque vous utilisez Bazel ou créez TensorFlow Lite par vous-même. L'API C++ ne peut pas être utilisée avec CocoaPods.
  • Lorsque vous utilisez TensorFlow Lite avec le délégué GPU avec C++, obtenez le délégué GPU via la fonction TFLGpuDelegateCreate() , puis transmettez-le à Interpreter::ModifyGraphWithDelegate() , au lieu d'appeler Interpreter::AllocateTensors() .

Construire et tester avec le mode release

Passez à une version avec les paramètres d'accélérateur de l'API Metal appropriés pour obtenir de meilleures performances et pour les tests finaux. Cette section explique comment activer une version de version et configurer les paramètres pour l'accélération Metal.

Pour passer à une version release :

  1. Modifiez les paramètres de construction en sélectionnant Produit > Schéma > Modifier le schéma... puis en sélectionnant Exécuter .
  2. Dans l'onglet Info , remplacez Build Configuration par Release et décochez Debug executable .configuration de la version
  3. Cliquez sur l'onglet Options et modifiez GPU Frame Capture sur Disabled et Metal API Validation sur Disabled .
    configuration des options métalliques
  4. Assurez-vous de sélectionner les versions en version uniquement sur une architecture 64 bits. Sous Navigateur de projet > tflite_camera_example > PROJECT > your_project_name > Build Settings, définissez Build Active Architecture Only > Release sur Yes . configuration des options de version

Prise en charge avancée des GPU

Cette section couvre les utilisations avancées du délégué GPU pour iOS, notamment les options de délégué, les tampons d'entrée et de sortie et l'utilisation de modèles quantifiés.

Options de délégué pour iOS

Le constructeur du délégué GPU accepte une struct d'options dans l' API Swift , l'API Objective-C et l'API C. Passer nullptr (API C) ou rien (API Objective-C et Swift) à l'initialiseur définit les options par défaut (qui sont expliquées dans l'exemple d'utilisation de base ci-dessus).

Rapide

// THIS:
var options = MetalDelegate.Options()
options.isPrecisionLossAllowed = false
options.waitType = .passive
options.isQuantizationEnabled = true
let delegate = MetalDelegate(options: options)

// IS THE SAME AS THIS:
let delegate = MetalDelegate()
      

Objectif c

// THIS:
TFLMetalDelegateOptions* options = [[TFLMetalDelegateOptions alloc] init];
options.precisionLossAllowed = false;
options.waitType = TFLMetalDelegateThreadWaitTypePassive;
options.quantizationEnabled = true;

TFLMetalDelegate* delegate = [[TFLMetalDelegate alloc] initWithOptions:options];

// IS THE SAME AS THIS:
TFLMetalDelegate* delegate = [[TFLMetalDelegate alloc] init];
      

C

// THIS:
const TFLGpuDelegateOptions options = {
  .allow_precision_loss = false,
  .wait_type = TFLGpuDelegateWaitType::TFLGpuDelegateWaitTypePassive,
  .enable_quantization = true,
};

TfLiteDelegate* delegate = TFLGpuDelegateCreate(options);

// IS THE SAME AS THIS:
TfLiteDelegate* delegate = TFLGpuDelegateCreate(nullptr);
      

Tampons d'entrée/sortie utilisant l'API C++

Le calcul sur le GPU nécessite que les données soient disponibles sur le GPU. Cette exigence signifie souvent que vous devez effectuer une copie mémoire. Si possible, vous devez éviter que vos données franchissent la limite de la mémoire CPU/GPU, car cela peut prendre beaucoup de temps. Habituellement, un tel croisement est inévitable, mais dans certains cas particuliers, l'un ou l'autre peut être omis.

Si l'entrée du réseau est une image déjà chargée dans la mémoire du GPU (par exemple, une texture GPU contenant le flux de la caméra), elle peut rester dans la mémoire du GPU sans jamais entrer dans la mémoire du CPU. De même, si la sortie du réseau se présente sous la forme d'une image rendue, telle qu'une opération de transfert de style d'image , vous pouvez afficher directement le résultat à l'écran.

Pour obtenir les meilleures performances, TensorFlow Lite permet aux utilisateurs de lire et d'écrire directement dans le tampon matériel TensorFlow et de contourner les copies de mémoire évitables.

En supposant que l'image d'entrée se trouve dans la mémoire GPU, vous devez d'abord la convertir en un objet MTLBuffer pour Metal. Vous pouvez associer un TfLiteTensor à un MTLBuffer préparé par l'utilisateur avec la fonction TFLGpuDelegateBindMetalBufferToTensor() . Notez que cette fonction doit être appelée après Interpreter::ModifyGraphWithDelegate() . De plus, la sortie d’inférence est, par défaut, copiée de la mémoire GPU vers la mémoire CPU. Vous pouvez désactiver ce comportement en appelant Interpreter::SetAllowBufferHandleOutput(true) lors de l'initialisation.

C++

#include "tensorflow/lite/delegates/gpu/metal_delegate.h"
#include "tensorflow/lite/delegates/gpu/metal_delegate_internal.h"

// ...

// Prepare GPU delegate.
auto* delegate = TFLGpuDelegateCreate(nullptr);

if (interpreter->ModifyGraphWithDelegate(delegate) != kTfLiteOk) return false;

interpreter->SetAllowBufferHandleOutput(true);  // disable default gpu->cpu copy
if (!TFLGpuDelegateBindMetalBufferToTensor(
        delegate, interpreter->inputs()[0], user_provided_input_buffer)) {
  return false;
}
if (!TFLGpuDelegateBindMetalBufferToTensor(
        delegate, interpreter->outputs()[0], user_provided_output_buffer)) {
  return false;
}

// Run inference.
if (interpreter->Invoke() != kTfLiteOk) return false;
      

Une fois le comportement par défaut désactivé, la copie de la sortie d'inférence de la mémoire GPU vers la mémoire CPU nécessite un appel explicite à Interpreter::EnsureTensorDataIsReadable() pour chaque tenseur de sortie. Cette approche fonctionne également pour les modèles quantifiés, mais vous devez toujours utiliser un tampon de taille float32 avec des données float32 , car le tampon est lié au tampon déquantifié interne.

Modèles quantifiés

Les bibliothèques déléguées GPU iOS prennent en charge les modèles quantifiés par défaut . Vous n'avez pas besoin d'apporter de modifications au code pour utiliser des modèles quantifiés avec le délégué GPU. La section suivante explique comment désactiver la prise en charge quantifiée à des fins de test ou d'expérimentation.

Désactiver la prise en charge du modèle quantifié

Le code suivant montre comment désactiver la prise en charge des modèles quantifiés.

Rapide

    var options = MetalDelegate.Options()
    options.isQuantizationEnabled = false
    let delegate = MetalDelegate(options: options)
      

Objectif c

    TFLMetalDelegateOptions* options = [[TFLMetalDelegateOptions alloc] init];
    options.quantizationEnabled = false;
      

C

    TFLGpuDelegateOptions options = TFLGpuDelegateOptionsDefault();
    options.enable_quantization = false;

    TfLiteDelegate* delegate = TFLGpuDelegateCreate(options);
      

Pour plus d’informations sur l’exécution de modèles quantifiés avec l’accélération GPU, consultez Présentation des délégués GPU .