O uso de unidades de processamento gráfico (GPUs) para executar modelos de machine learning (ML) pode melhorar muito o desempenho do modelo e a experiência do usuário nos aplicativos habilitados para ML. Em dispositivos iOS, é possível ativar o uso da execução acelerada pela GPU dos modelos usando um delegado. Os delegados atuam como drivers de hardware para o TensorFlow Lite, permitindo que você execute o código do modelo em processadores GPU.
Nesta página, descrevemos como ativar a aceleração de GPU para modelos do TensorFlow Lite em apps iOS. Para saber mais sobre como usar o delegado de GPU para o TensorFlow Lite, incluindo práticas recomendadas e técnicas avançadas, consulte a página Delegados de GPU.
Usar a GPU com a API Interpreter
A API Interpreter do TensorFlow Lite oferece um conjunto de APIs de uso geral para criar aplicativos de machine learning. As instruções a seguir orientam você sobre como adicionar suporte a GPUs a um app iOS. Neste guia, presume-se que você já tenha um app iOS que possa executar um modelo de ML com o TensorFlow Lite.
Modifique o Podfile para incluir suporte a GPUs
A partir da versão 2.3.0 do TensorFlow Lite, o delegado da GPU é excluído
do pod para reduzir o tamanho binário. É possível incluí-los especificando um
subspec para o pod TensorFlowLiteSwift
:
pod 'TensorFlowLiteSwift/Metal', '~> 0.0.1-nightly',
OU
pod 'TensorFlowLiteSwift', '~> 0.0.1-nightly', :subspecs => ['Metal']
Também é possível usar TensorFlowLiteObjC
ou TensorFlowLiteC
se você quiser usar o Objective-C, disponível para as versões 2.4.0 e mais recentes, ou a API C.
Inicializar e usar o delegado da GPU
É possível usar o delegado da GPU com a API Interpreter do TensorFlow Lite com várias linguagens de programação. Swift e Objective-C são recomendados, mas você também pode usar C++ e C. É necessário usar C se você estiver usando uma versão do TensorFlow Lite anterior à 2.4. Os exemplos de código a seguir descrevem como usar o delegado com cada uma dessas linguagens.
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 (antes da 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);
Observações sobre o uso da linguagem da API GPU
- As versões do TensorFlow Lite anteriores à 2.4.0 só podem usar a API C para Objective-C.
- A API C++ só está disponível quando você usa o Bazel ou cria o TensorFlow Lite por conta própria. A API C++ não pode ser usada com CocoaPods.
- Ao usar o TensorFlow Lite com o delegado da GPU com C++, consiga o delegado da GPU
usando a função
TFLGpuDelegateCreate()
e, em seguida, transmita-o paraInterpreter::ModifyGraphWithDelegate()
, em vez de chamarInterpreter::AllocateTensors()
.
Criar e testar com o modo de lançamento
Mude para um build de lançamento com as configurações apropriadas do acelerador da API Metal para melhorar a performance e para os testes finais. Esta seção explica como ativar um build de lançamento e definir as configurações de aceleração do Metal.
Para mudar para um build de lançamento:
- Para editar as configurações de build, selecione Produto > Esquema > Editar esquema... e depois Executar.
- Na guia Info, mude Build Configuration para Release e desmarque Debug executável.
- Clique na guia Options e mude GPU Frame Capture para Disabled
e Metal API Validation para Disabled.
- Selecione builds somente para lançamento na arquitetura de 64 bits. Em Project Navigator > tflite_camera_example > PROJECT > your_project_name > Build Settings, defina Build Active Architecture Only > Release como Yes.
Suporte avançado a GPUs
Esta seção aborda usos avançados do delegado de GPU para iOS, incluindo opções de delegação, buffers de entrada e saída e o uso de modelos quantizados.
Opções de delegação para iOS
O construtor do delegado da GPU aceita um struct
de opções na API Swift, na API Objective-C e na API C.
Transmitir nullptr
(API C) ou nada (API Objective-C e Swift) ao
inicializador define as opções padrão, explicadas no exemplo de uso básico
acima.
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);
Buffers de entrada/saída usando a API C++
A computação na GPU exige que os dados estejam disponíveis para ela. Esse requisito geralmente significa que você precisa executar uma cópia da memória. Evite que os dados ultrapassem o limite de memória da CPU/GPU, se possível, porque isso pode demorar muito. Normalmente, esse cruzamento é inevitável, mas, em alguns casos especiais, um ou outro pode ser omitido.
Se a entrada da rede for uma imagem já carregada na memória da GPU (por exemplo, uma textura da GPU que contém o feed da câmera), ela poderá permanecer na memória da GPU sem entrar na memória da CPU. Da mesma forma, se a saída da rede estiver na forma de uma imagem renderizável, como uma operação de transferência de estilo de imagem, será possível exibir o resultado diretamente na tela.
Para ter o melhor desempenho, o TensorFlow Lite possibilita que os usuários leiam e gravem diretamente no buffer de hardware do TensorFlow e ignorem cópias de memória evitáveis.
Supondo que a entrada da imagem esteja na memória da GPU, você precisa primeiro convertê-la em um
objeto MTLBuffer
para Metal. É possível associar um TfLiteTensor
a um
MTLBuffer
preparado pelo usuário com a função
TFLGpuDelegateBindMetalBufferToTensor()
. Observe que essa função precisa ser chamada após
Interpreter::ModifyGraphWithDelegate()
. Além disso, a saída de inferência é,
por padrão, copiada da memória da GPU para a memória da CPU. É possível desativar esse comportamento
chamando Interpreter::SetAllowBufferHandleOutput(true)
durante a
inicialização.
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;
Depois que o comportamento padrão é desativado, a cópia da saída de inferência da memória da GPU
para a memória da CPU requer uma chamada explícita de
Interpreter::EnsureTensorDataIsReadable()
para cada tensor de saída. Essa abordagem também funciona para modelos quantizados, mas você ainda precisa usar um buffer de tamanho float32 com dados float32, porque o buffer está vinculado ao buffer desquantizado interno.
Modelos quantizados
As bibliotecas delegadas de GPU do iOS oferecem suporte a modelos quantizados por padrão. Não é necessário fazer nenhuma mudança no código para usar modelos quantizados com o delegado da GPU. A seção a seguir explica como desativar o suporte quantizado para fins de teste ou experimentais.
Desativar suporte a modelos quantizados
O código a seguir mostra como desativar o suporte para modelos quantizados.
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 saber mais sobre como executar modelos quantizados com aceleração de GPU, consulte a visão geral do delegado de GPU.