Delegado de aceleración de GPU para iOS

El uso de unidades de procesamiento de gráficos (GPU) para ejecutar sus modelos de aprendizaje automático (ML) puede mejorar drásticamente el rendimiento de su modelo y la experiencia del usuario de sus aplicaciones habilitadas para ML. En dispositivos iOS, puede habilitar el uso de la ejecución acelerada por GPU de sus modelos mediante un delegado . Los delegados actúan como controladores de hardware para TensorFlow Lite, lo que le permite ejecutar el código de su modelo en procesadores GPU.

Esta página describe cómo habilitar la aceleración de GPU para los modelos TensorFlow Lite en las aplicaciones de iOS. Para obtener más información sobre el uso del delegado de GPU para TensorFlow Lite, incluidas las prácticas recomendadas y las técnicas avanzadas, consulte la página de delegados de GPU .

Usar GPU con API de intérprete

La API de intérprete de TensorFlow Lite proporciona un conjunto de API de uso general para crear aplicaciones de aprendizaje automático. Las siguientes instrucciones lo guían para agregar compatibilidad con GPU a una aplicación de iOS. Esta guía asume que ya tiene una aplicación de iOS que puede ejecutar con éxito un modelo ML con TensorFlow Lite.

Modifique el Podfile para incluir compatibilidad con GPU

A partir de la versión TensorFlow Lite 2.3.0, el delegado de GPU se excluye del pod para reducir el tamaño binario. Puede incluirlos especificando una subespecificación para el pod de TensorFlowLiteSwift :

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

O

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

También puede usar TensorFlowLiteObjC o TensorFlowLiteC si desea usar Objective-C, que está disponible para las versiones 2.4.0 y posteriores, o la API de C.

Inicializar y usar delegado de GPU

Puedes usar el delegado de GPU con la API de intérprete de TensorFlow Lite con varios lenguajes de programación. Se recomiendan Swift y Objective-C, pero también puede usar C++ y C. Es necesario usar C si usa una versión de TensorFlow Lite anterior a la 2.4. Los siguientes ejemplos de código describen cómo usar el delegado con cada uno de estos idiomas.

Rápido

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

C objetivo

// 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 (antes de 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);
      

Notas de uso del lenguaje API de GPU

  • Las versiones de TensorFlow Lite anteriores a la 2.4.0 solo pueden usar la API de C para Objective-C.
  • La API de C++ solo está disponible cuando usa bazel o crea TensorFlow Lite usted mismo. La API de C++ no se puede usar con CocoaPods.
  • Cuando uses TensorFlow Lite con el delegado de GPU con C++, obtén el delegado de GPU a través de la función TFLGpuDelegateCreate() y luego pásalo a Interpreter::ModifyGraphWithDelegate() , en lugar de llamar a Interpreter::AllocateTensors() .

Cree y pruebe con el modo de lanzamiento

Cambie a una compilación de lanzamiento con la configuración adecuada del acelerador de la API de Metal para obtener un mejor rendimiento y para las pruebas finales. Esta sección explica cómo habilitar una compilación de lanzamiento y configurar la configuración para la aceleración de Metal.

Para cambiar a una compilación de lanzamiento:

  1. Edite la configuración de compilación seleccionando Producto > Esquema > Editar esquema... y luego seleccionando Ejecutar .
  2. En la pestaña Información , cambie Configuración de compilación a Versión y desmarque Depurar ejecutable .configuración de liberación
  3. Haga clic en la pestaña Opciones y cambie GPU Frame Capture a Disabled y Metal API Validation a Disabled .
    configuración de opciones de metal
  4. Asegúrese de seleccionar compilaciones de solo versión en arquitectura de 64 bits. En Project Navigator > tflite_camera_example > PROJECT > your_project_name > Build Settings , establezca Build Active Architecture Only > Release en . configuración de opciones de lanzamiento

Compatibilidad con GPU avanzada

Esta sección cubre los usos avanzados del delegado de GPU para iOS, incluidas las opciones de delegado, los búferes de entrada y salida y el uso de modelos cuantificados.

Opciones de delegado para iOS

El constructor para el delegado de GPU acepta una struct de opciones en Swift API , Objective-C API y C API . Pasar nullptr (C API) o nada (Objective-C y Swift API) al inicializador establece las opciones predeterminadas (que se explican en el ejemplo de uso básico anterior).

Rápido

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

C objetivo

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

Búferes de entrada/salida usando la API de C++

El cálculo en la GPU requiere que los datos estén disponibles para la GPU. Este requisito a menudo significa que debe realizar una copia de memoria. Debe evitar que sus datos crucen el límite de memoria de CPU/GPU si es posible, ya que esto puede llevar una cantidad de tiempo significativa. Por lo general, dicho cruce es inevitable, pero en algunos casos especiales, se puede omitir uno u otro.

Si la entrada de la red es una imagen que ya está cargada en la memoria de la GPU (por ejemplo, una textura de GPU que contiene la transmisión de la cámara), puede permanecer en la memoria de la GPU sin ingresar nunca a la memoria de la CPU. De manera similar, si la salida de la red tiene la forma de una imagen renderizable, como una operación de transferencia de estilo de imagen , puede mostrar directamente el resultado en la pantalla.

Para lograr el mejor rendimiento, TensorFlow Lite hace posible que los usuarios lean y escriban directamente en el búfer de hardware de TensorFlow y eviten las copias de memoria evitables.

Suponiendo que la entrada de la imagen está en la memoria de la GPU, primero debe convertirla en un objeto MTLBuffer para Metal. Puede asociar un TfLiteTensor a un MTLBuffer preparado por el usuario con la función TFLGpuDelegateBindMetalBufferToTensor() . Tenga en cuenta que esta función debe llamarse después de Interpreter::ModifyGraphWithDelegate() . Además, la salida de la inferencia se copia, de forma predeterminada, de la memoria de la GPU a la memoria de la CPU. Puede desactivar este comportamiento llamando a Interpreter::SetAllowBufferHandleOutput(true) durante la inicialización.

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;
      

Una vez que se desactiva el comportamiento predeterminado, copiar la salida de inferencia de la memoria de la GPU a la memoria de la CPU requiere una llamada explícita a Interpreter::EnsureTensorDataIsReadable() para cada tensor de salida. Este enfoque también funciona para modelos cuantificados, pero aún necesita usar un búfer de tamaño float32 con datos de float32 , porque el búfer está vinculado al búfer interno descuantificado.

modelos cuantificados

Las bibliotecas delegadas de GPU de iOS admiten modelos cuantificados de forma predeterminada . No necesita realizar ningún cambio de código para usar modelos cuantificados con el delegado de GPU. La siguiente sección explica cómo deshabilitar el soporte cuantificado para fines de prueba o experimentales.

Deshabilitar el soporte de modelo cuantificado

El siguiente código muestra cómo deshabilitar la compatibilidad con modelos cuantificados.

Rápido

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

C objetivo

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

C

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

    TfLiteDelegate* delegate = TFLGpuDelegateCreate(options);
      

Para obtener más información sobre cómo ejecutar modelos cuantificados con aceleración de GPU, consulte Información general sobre delegados de GPU .