Delegado de aceleración de GPU para iOS

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

En esta página, se describe cómo habilitar la aceleración de GPU en modelos de TensorFlow Lite en apps para iOS. Si quieres 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, consulta la página Delegados de GPU.

Usa la GPU con la API de intérprete

La API de Interpreter de TensorFlow Lite proporciona un conjunto de API de uso general para compilar aplicaciones de aprendizaje automático. Las siguientes instrucciones sirven de guía para agregar compatibilidad con GPU a una app para iOS. En esta guía, se da por sentado que ya tienes una app para iOS que puede ejecutar correctamente un modelo de AA con TensorFlow Lite.

Modifica el Podfile para incluir compatibilidad con GPU

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

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

O

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

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

Inicializa y usa el delegado de la GPU

Puedes usar el delegado de la GPU con la API de Interpreter de TensorFlow Lite con varios lenguajes de programación. Se recomiendan Swift y Objective-C, pero también puedes usar C++ y C. Es obligatorio usar C si usas una versión de TensorFlow Lite anterior a la 2.4. En los siguientes ejemplos de código, se describe cómo usar el delegado con cada uno de estos lenguajes.

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 (anterior a 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 sobre el uso del lenguaje de la 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 usas Bazel o cuando compilas TensorFlow Lite por tu cuenta. 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().

Compila y prueba con el modo de lanzamiento

Cambia a una compilación de lanzamiento con la configuración adecuada del acelerador de la API de Metal para obtener un mejor rendimiento y realizar pruebas finales. En esta sección, se explica cómo habilitar una compilación de lanzamiento y establecer la configuración de la aceleración de Metal.

Sigue estos pasos para cambiar a una compilación de lanzamiento:

  1. Para editar la configuración de la compilación, selecciona Product > Scheme > Edit Scheme... y, luego, Run.
  2. En la pestaña Información, cambia Configuración de compilación a Versión y desmarca Depurar ejecutable. configurar el lanzamiento
  3. Haz clic en la pestaña Options y cambia GPU Frame Capture a Disabled y Metal API Validation a Disabled.
    configurar opciones de metal
  4. Asegúrate de seleccionar compilaciones de solo versión en arquitectura de 64 bits. En Project navigator > tflite_camera_example > PROJECT > your_project_name > Build Settings, configura Build Active Architecture Only > Release en Yes. configurar las opciones de lanzamiento

Compatibilidad avanzada con GPU

En esta sección, se abordan 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 cuantizados.

Opciones de delegación para iOS

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

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

Búferes de entrada y salida con la API de C++

El procesamiento en la GPU requiere que los datos estén disponibles para la GPU. Este requisito a menudo implica que debes realizar una copia de memoria. Si es posible, debes evitar que tus datos crucen el límite de memoria de CPU/GPU, ya que esto puede tardar bastante tiempo. Por lo general, ese cruce es inevitable. Sin embargo, en algunos casos especiales, se puede omitir uno o el otro.

Si la entrada de la red es una imagen ya cargada en la memoria de la GPU (por ejemplo, una textura de la GPU que contiene el feed de la cámara), esta puede permanecer en la memoria de la GPU sin ingresar nunca a la memoria de la CPU. Del mismo modo, si el resultado de la red es una imagen que se puede renderizar, como una operación de transferencia de estilo de imagen, puedes mostrar el resultado directamente en la pantalla.

Para lograr un rendimiento óptimo, TensorFlow Lite permite que los usuarios lean y escriban directamente en el búfer de hardware de TensorFlow, y eviten las copias de memoria evitables.

Si suponemos que la entrada de la imagen está en la memoria de GPU, primero debes convertirla en un objeto MTLBuffer para Metal. Puedes asociar un TfLiteTensor a una MTLBuffer preparada por el usuario con la función TFLGpuDelegateBindMetalBufferToTensor(). Ten en cuenta que se debe llamar a esta función después de Interpreter::ModifyGraphWithDelegate(). Además, el resultado de la inferencia se copia, de forma predeterminada, de la memoria de la GPU a la memoria de la CPU. Puedes 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, para copiar el resultado de la inferencia de la memoria de la GPU a la de la CPU, se requiere una llamada explícita a Interpreter::EnsureTensorDataIsReadable() para cada tensor de salida. Este enfoque también funciona para modelos cuantizados, pero aún debes usar un búfer de tamaño float32 con datos float32, ya que el búfer está vinculado al búfer interno descuantizado.

Modelos cuantificados

Las bibliotecas delegadas de GPU de iOS admiten modelos cuantificados de forma predeterminada. No es necesario que realices ningún cambio de código para usar modelos cuantizados con el delegado de GPU. En la siguiente sección, se explica cómo inhabilitar la compatibilidad cuantizada con fines experimentales o de prueba.

Inhabilita la compatibilidad con modelos cuantizados

En el siguiente código, se muestra cómo inhabilitar la compatibilidad con modelos cuantizados.

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

Para obtener más información sobre cómo ejecutar modelos cuantizados con aceleración de GPU, consulta la descripción general del delegado de GPU.