Délégué d'accélération du GPU pour iOS

L'utilisation de processeurs graphiques (GPU) pour exécuter vos modèles de machine learning (ML) peut améliorer considérablement les performances de votre modèle et l'expérience utilisateur de vos applications compatibles avec le 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 comme des pilotes matériels pour TensorFlow Lite, ce qui vous permet d'exécuter le code de votre modèle sur des processeurs GPU.

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

Utiliser un GPU avec l'API Interpréteur

L'API Interpréteur TensorFlow Lite fournit un ensemble d'API à usage général pour la création d'applications de machine learning. Les instructions suivantes vous expliquent comment ajouter la compatibilité GPU à une application iOS. Dans ce guide, nous partons du principe que vous disposez déjà d'une application iOS capable d'exécuter correctement un modèle de ML avec TensorFlow Lite.

Modifier le fichier Podfile pour inclure la compatibilité avec les GPU

À partir de la version 2.3.0 de TensorFlow Lite, 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 ultérieures, ou l'API C.

Initialiser et utiliser le délégué GPU

Vous pouvez utiliser le délégué GPU avec l'API Interpréteur TensorFlow Lite avec plusieurs langages de programmation. Swift et Objective-C sont recommandés, mais vous pouvez également utiliser C++ et C. L'utilisation de C est obligatoire si vous employez une version de TensorFlow Lite antérieure à la version 2.4. Les exemples de code suivants décrivent comment utiliser le délégué avec chacun de ces langages.

Swift

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

Objective-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 la version 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);
      

Remarques sur l'utilisation de la langue de l'API GPU

  • Les versions de TensorFlow Lite antérieures à la version 2.4.0 ne peuvent utiliser l'API C que pour Objective-C.
  • L'API C++ n'est disponible que lorsque vous utilisez Bazel ou compilez 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().

Compiler et tester en mode publication

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

Pour passer à un build:

  1. Modifiez les paramètres de compilation en sélectionnant Product > Scheme > Edit Scheme (Produit > Schéma > Modifier le schéma), puis Run (Exécuter).
  2. Dans l'onglet Info, définissez Build Configuration (Configuration de compilation) sur Release (Publier) et décochez Debug executable (Déboguer le fichier exécutable). la configuration de
la version
  3. Cliquez sur l'onglet Options, puis définissez Capture d'image GPU sur Désactivé et Validation de l'API Meta sur Désactivé.
    configuration des options
Metal
  4. Veillez à sélectionner "Versions uniquement" sur une architecture 64 bits. Sous Project navigator > tflite_camera_example > PROJECT > your_project_name > Build Settings (Navigateur de projets > tflite_camera_example > PROJET > nom de votre projet > Paramètres de compilation), définissez Build Active Architecture Only > Release (Compiler une architecture active uniquement > Version) sur Yes (Oui). la configuration des options
de publication

Compatibilité avancée avec les GPU

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

Options de délégation pour iOS

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

Swift

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

Objective-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/de sortie à l'aide de l'API C++

Le calcul sur GPU nécessite que les données soient disponibles pour le GPU. Cela signifie souvent que vous devez effectuer une copie de la mémoire. Dans la mesure du possible, évitez que vos données ne dépassent la limite de mémoire CPU/GPU, car cela peut prendre un temps considérable. En règle générale, 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 l'appareil photo), elle peut rester dans la mémoire du GPU sans jamais entrer dans la mémoire du processeur. De même, si la sortie du réseau se présente sous la forme d'une image pouvant faire l'objet d'un rendu, telle qu'une opération de transfert de style d'image, vous pouvez afficher le résultat directement à l'écran.

Pour obtenir des performances optimales, 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 impossibles à éviter.

En supposant que l'entrée d'image se trouve dans la mémoire GPU, vous devez d'abord la convertir en objet MTLBuffer pour Metal. Vous pouvez associer un TfLiteTensor à un MTLBuffer préparé par l'utilisateur à l'aide de la fonction TFLGpuDelegateBindMetalBufferToTensor(). Notez que cette fonction doit être appelée après Interpreter::ModifyGraphWithDelegate(). De plus, le résultat d'inférence est, par défaut, copié de la mémoire du GPU vers la mémoire du processeur. 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 du GPU vers la mémoire du processeur nécessite un appel explicite à Interpreter::EnsureTensorDataIsReadable() pour chaque Tensor de sortie. Cette approche fonctionne également pour les modèles quantifiés, mais vous devez toujours utiliser un tampon de la taille float32 avec des données float32, car ce tampon est lié au tampon interne déquantifié.

Modèles quantifiés

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

Désactiver la prise en charge des modèles quantifiés

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

Swift

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

Objective-C

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

C

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

    TfLiteDelegate* delegate = TFLGpuDelegateCreate(options);
      

Pour en savoir plus sur l'exécution de modèles quantifiés avec l'accélération GPU, consultez la section Délégué de GPU.