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 aInterpreter::ModifyGraphWithDelegate()
, en lugar de llamar aInterpreter::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:
- Edite la configuración de compilación seleccionando Producto > Esquema > Editar esquema... y luego seleccionando Ejecutar .
- En la pestaña Información , cambie Configuración de compilación a Versión y desmarque Depurar ejecutable .
- Haga clic en la pestaña Opciones y cambie GPU Frame Capture a Disabled y Metal API Validation a Disabled .
- 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 Sí .
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 .