Delegado de aceleración de GPU para iOS

Usar unidades de procesamiento gráfico (GPU) para ejecutar tus modelos de aprendizaje automático (AA) puede mejorar drásticamente el rendimiento del modelo y la experiencia del usuario de tus aplicaciones habilitadas para AA. En los dispositivos iOS, puedes habilitar el uso de Ejecución acelerada por GPU de tus modelos mediante un delegate. Los delegados actúan como controladores de hardware para LiteRT, 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 para modelos LiteRT en apps para iOS. Para obtener más información sobre el uso del delegado de GPU para LiteRT, incluidas las prácticas recomendadas y las técnicas avanzadas, consulta la documentación de GPU página de delegados.

Usa GPU con la API de Interpreter

El intérprete de LiteRT API ofrece un conjunto de datos para compilar aplicaciones de aprendizaje automático. Lo siguiente Las instrucciones te guiarán para agregar compatibilidad con GPU a una app para iOS. Esta guía supone que ya tienes una app para iOS que puede ejecutar correctamente un modelo de AA con LiteRT.

Modifica el Podfile para incluir la compatibilidad con GPU

A partir de la versión LiteRT 2.3.0, se excluye el delegado de GPU del Pod para reducir el tamaño del objeto binario. Puedes incluirlos especificando un 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 quieres 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 GPU

Puedes usar el delegado de la GPU con el Interpretador de LiteRT API con cierta programación idiomas. Te recomendamos Swift y Objective-C, pero también puedes usar C++ y C) Debes usar C si usas una versión anterior de LiteRT. que la 2.4. En los siguientes ejemplos de código, se describe cómo usar el delegado con cada de estos lenguajes.

Swift

import TensorFlowLite

// Load model ...

// Initialize LiteRT 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 (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 de la API de GPU

  • Las versiones LiteRT 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 por tu cuenta No se puede usar la API de C++ con CocoaPods.
  • Cuando usas LiteRT con el delegado de la GPU con C++, obtén la GPU. delegar a través de la función TFLGpuDelegateCreate() y, luego, pasarlo al Interpreter::ModifyGraphWithDelegate(), en lugar de llamar Interpreter::AllocateTensors()

Cómo compilar y probar 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 para obtener un mejor rendimiento y para las pruebas finales. En esta sección, se explica cómo habilita una compilación de lanzamiento y establece la configuración de la aceleración de Metal.

Para cambiar a una compilación de lanzamiento, haz lo siguiente:

  1. Para editar la configuración de compilación, selecciona Producto > Esquema > Editar esquema... y, luego, selecciona Ejecutar.
  2. En la pestaña Info, cambia Build Configuration a Release. Desmarca Depurar ejecutable. Configurando
lanzar
  3. Haz clic en la pestaña Options y cambia GPU Frame Capture a Disabled. y Metal API Validation como Disabled.
    configuración de metal
Opciones
  4. Asegúrate de seleccionar compilaciones solo de lanzamiento en una arquitectura de 64 bits. Por debajo Navegador de proyectos > tflite_camera_example > PROYECTO > tu_nombre_de_tu_proyecto > Build Settings establece Build Active Architecture Only > Suelta en . configurando el lanzamiento
Opciones

Compatibilidad avanzada con GPU

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

Delegar opciones para iOS

El constructor para el delegado de la GPU acepta un struct de opciones en Swift API, Objective-C API, y C API. Pasar nullptr (API de C) o nada (API de Objective-C y Swift) a la el inicializador establece las opciones predeterminadas (que se explican en la sección Uso básico) ejemplo 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. Esta requisito suele implicar que debes realizar una copia en la memoria. Debes evitar tener si tus datos superan el límite de memoria de CPU/GPU si es posible, ya que esto puede ocupar durante una cantidad de tiempo significativa. Por lo general, ese cruce es inevitable, pero en algunos casos especiales, uno u otro se puede omitir.

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

Para lograr el mejor rendimiento, LiteRT permite que los usuarios puede leer y escribir directamente en el búfer de hardware de TensorFlow, así como omitir copias de memoria evitables.

Si suponemos que la entrada de imagen está en la memoria GPU, primero debes convertirla en un Objeto MTLBuffer para Metal. Puedes asociar un TfLiteTensor a un MTLBuffer preparado por el usuario con TFLGpuDelegateBindMetalBufferToTensor() . Ten en cuenta que esta función debe llamarse después Interpreter::ModifyGraphWithDelegate() Además, el resultado de la inferencia es de forma predeterminada, se copia de la memoria de la GPU a la memoria de la CPU. Puedes desactivar este comportamiento llamando a Interpreter::SetAllowBufferHandleOutput(true) durante de inicio.

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, se copia el resultado de la inferencia de la GPU entre la memoria y la CPU requiere una llamada explícita Interpreter::EnsureTensorDataIsReadable() para cada tensor de salida. Esta también funciona para modelos cuantizados, pero aún debes usar búfer de tamaño flotante float32 con datos de float32, ya que el búfer está vinculado al búfer interno descuantizado.

Modelos cuantificados

Las bibliotecas delegadas de GPU para iOS admiten modelos cuantizados de forma predeterminada. No debes deberá realizar cambios en el código para usar modelos cuantizados con el delegado de la GPU. El En la siguiente sección, se explica cómo inhabilitar la asistencia cuantizada para pruebas o con fines experimentales.

Inhabilitar 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 Descripción general del delegado de GPU.