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 modelos TensorFlow Lite en aplicaciones de iOS. Para obtener más información sobre el uso del delegado de GPU para TensorFlow Lite, incluidas las mejores prácticas y técnicas avanzadas, consulte la página de delegados de GPU .

Utilice GPU con API de intérprete

La API de intérprete de TensorFlow Lite proporciona un conjunto de API de propósito general para crear aplicaciones de aprendizaje automático. Las siguientes instrucciones lo guiarán para agregar compatibilidad con GPU a una aplicación de iOS. Esta guía supone que ya tienes una aplicación de iOS que puede ejecutar correctamente un modelo de aprendizaje automático con TensorFlow Lite.

Modifique el Podfile para incluir soporte para 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. Puedes incluirlos especificando una subespección para el pod 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 superiores, o la API de C.

Inicializar y usar delegado de GPU

Puede utilizar 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 puedes usar C++ y C. Es necesario usar C si estás usando una versión de TensorFlow Lite anterior a la 2.4. Los siguientes ejemplos de código describen cómo utilizar 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 C para Objective-C.
  • La API de C++ solo está disponible cuando usas bazel o compilas TensorFlow Lite tú mismo. La API de C++ no se puede utilizar 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() .

Construya y pruebe con el modo de lanzamiento

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

Para cambiar a una versió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 Lanzar y desmarque Depurar ejecutable .configurando la liberación
  3. Haga clic en la pestaña Opciones y cambie Captura de fotogramas de GPU a Desactivado y Validación de API de metal a Desactivado .
    configurar opciones de metal
  4. Asegúrese de seleccionar compilaciones de solo lanzamiento en arquitectura de 64 bits. En Navegador de proyectos > tflite_camera_example > PROYECTO > nombre_de_su_proyecto > Configuración de compilación, establezca Construir solo arquitectura activa > Lanzar en . configurar opciones de lanzamiento

Soporte avanzado de GPU

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

Opciones de delegado para iOS

El constructor del 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úfers de entrada/salida usando API de C++

La computación 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. Si es posible, debe evitar que sus datos crucen el límite de memoria de la CPU/GPU, ya que esto puede llevar una cantidad significativa de tiempo. Por lo general, dicho cruce es inevitable, pero en algunos casos especiales, uno u otro puede omitirse.

Si la entrada de la red es una imagen ya 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 siquiera ingresar 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 el resultado directamente en la pantalla.

Para lograr el mejor rendimiento, TensorFlow Lite permite a los usuarios leer y escribir directamente en el búfer de hardware de TensorFlow y evitar 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 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 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 es necesario realizar ningún cambio en el código para utilizar modelos cuantificados con el delegado de GPU. La siguiente sección explica cómo desactivar el soporte cuantificado para fines experimentales o de prueba.

Deshabilitar la compatibilidad con modelos cuantificados

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 la ejecución de modelos cuantificados con aceleración de GPU, consulte Descripción general del delegado de GPU .