Delegado de aceleração de GPU para iOS

Como usar unidades de processamento gráfico (GPUs) para executar modelos de machine learning (ML) pode melhorar drasticamente o desempenho do seu modelo e a experiência do usuário dos seus aplicativos com ML. Em dispositivos iOS, você pode ativar o uso de a execução acelerada por GPU dos modelos usando uma delegar. Os delegados atuam como drivers de hardware para LiteRT, que permite que você execute o código do seu modelo em processadores GPU.

Esta página descreve como ativar a aceleração de GPU para modelos LiteRT em Apps iOS. Para mais informações sobre como usar o delegado da GPU para o LiteRT, incluindo práticas recomendadas e técnicas avançadas, consulte a GPU delegados.

Usar GPU com a API Interpreter

O intérprete do LiteRT API oferece um conjunto de instruções APIs específicas para criar aplicativos de machine learning. O seguinte ajudam você a adicionar suporte a GPUs em um app iOS. Este guia pressupõe que você já tem um app iOS capaz de executar um modelo de ML com a LiteRT.

Modificar o Podfile para incluir suporte a GPU

A partir da versão LiteRT 2.3.0, o delegado da GPU será excluído do pod para reduzir o tamanho do binário. É possível incluí-las especificando uma 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 o Objective-C, disponível para as versões 2.4.0 e superiores, ou a API C.

Inicializar e usar o delegado da GPU

Você pode usar o delegado da GPU com o Intérprete de LiteRT com diversas APIs de programação idiomas. 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 LiteRT anterior do que 2.4. Os exemplos de código a seguir descrevem como usar o delegado com cada dessas linguagens.

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 da versão 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 de linguagem da API GPU

  • As versões LiteRT anteriores à 2.4.0 só podem usar a API C para usando Objective-C.
  • A API C++ só está disponível quando você usa o Bazel ou cria o TensorFlow Use o Lite por conta própria. A API C++ não pode ser usada com o CocoaPods.
  • Ao usar o LiteRT com o delegado de GPU com C++, você recebe a GPU delegado usando a função TFLGpuDelegateCreate() e a transmita para Interpreter::ModifyGraphWithDelegate(), em vez de chamar Interpreter::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 o desempenho e para os testes finais. Esta seção explica como Ativar um build de lançamento e definir a configuração para aceleração de metal.

Para mudar para um build de lançamento:

  1. Edite as configurações do build selecionando Product > Esquema > Editar esquema... e selecionando Executar.
  2. Na guia Info, altere Build Configuration para Release e desmarque a opção Depurar executável. configurando
liberam
  3. Clique na guia Opções e altere a Captura de frames da GPU para Desativado. e Validação da API Metal como Disabled.
    configuração de metal
opções
  4. Selecione builds somente para lançamento em arquiteturas de 64 bits. Abaixo Navegador do projeto > tflite_camera_example > PROJETO > nome_do_projeto > "Build Settings" e defina Build Active Architecture Only > Liberar para Sim. Configurando a versão
opções

Suporte avançado a GPUs

Esta seção aborda os usos avançados do delegado da GPU para iOS, incluindo: opções de delegação, buffers de entrada e saída e uso de modelos quantizados.

Delegar opções para iOS

O construtor do delegado da GPU aceita um struct de opções no elemento Swift API, Objective-C API, e C API. Transmitir nullptr (API C) ou nada (Objective-C e Swift API) para a inicializador define as opções padrão (que são explicadas no 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. Isso o que geralmente significa que você precisa fazer uma cópia na memória. Você deve evitar ter porque seus dados cruzam o limite de memória da CPU/GPU, se possível, um período significativo. Normalmente, essa travessia é 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 de GPU que contém o feed da câmera) que pode permanecer na memória da GPU sem nunca entrar na memória da CPU. Da mesma forma, se a saída da rede estiver em a forma de uma imagem renderizável, como um estilo de imagem transferência você poderá exibir o resultado diretamente na tela.

Para alcançar o melhor desempenho, a LiteRT possibilita que os usuários ler e gravar diretamente no buffer de hardware do TensorFlow e ignorar evitáveis de memória.

Supondo que a entrada de imagem esteja na memória da GPU, primeiro é preciso convertê-la em um Objeto MTLBuffer para Metal. Você pode associar um TfLiteTensor a um MTLBuffer preparou o usuário com TFLGpuDelegateBindMetalBufferToTensor() função. Esta função precisa ser chamada após Interpreter::ModifyGraphWithDelegate() Além disso, a saída de inferência copiado da memória da GPU para a memória da CPU. É possível desativar esse comportamento chamando Interpreter::SetAllowBufferHandleOutput(true) durante inicialização do sistema.

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;
      

Quando o comportamento padrão estiver desativado, copiar a saída de inferência da GPU de memória para memória de CPU exige uma chamada explícita para Interpreter::EnsureTensorDataIsReadable() para cada tensor de saída. Isso abordagem também funciona para modelos quantizados, mas você ainda precisa usar uma buffer de tamanho float32 com dados de float32, porque o buffer está vinculado à ou um buffer desquantizado interno.

Modelos quantizados

As bibliotecas de delegação de GPU do iOS oferecem suporte a modelos quantizados por padrão. Você não fazer alterações no código para usar modelos quantizados com o delegado da GPU. O seção a seguir explica como desativar o suporte quantizado para testes ou para fins experimentais.

Desativar o suporte a modelos quantizados

O código a seguir mostra como desativar o suporte a 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 mais informações sobre como executar modelos quantizados com aceleração de GPU, consulte Visão geral do delegado da GPU.