Delegados da LiteRT

Os delegados permitem a aceleração de hardware dos modelos LiteRT usando aceleradores no dispositivo, como a GPU e o processador de sinal digital (DSP).

Por padrão, o LiteRT usa kernels de CPU otimizados para o conjunto de instruções ARM Neon. No entanto, a CPU é um processador multifuncional que não é necessariamente otimizado para a aritmética pesada normalmente encontrada em modelos de machine learning (por exemplo, a matemática de matrizes envolvida em convolução e camadas densas).

Por outro lado, a maioria dos smartphones modernos tem chips mais adequados para lidar com essas operações pesadas. Usá-los para operações de rede neural oferece grandes benefícios em termos de latência e eficiência energética. Por exemplo, as GPUs podem oferecer um aumento de velocidade de até 5 vezes na latência.

Cada um desses aceleradores tem APIs associadas que permitem computações personalizadas, como OpenCL ou OpenGL ES para GPU móvel. Normalmente, você teria que escrever muito código personalizado para executar uma rede neural por essas interfaces. As coisas ficam ainda mais complicadas quando consideramos que cada acelerador tem seus prós e contras e não pode executar todas as operações em uma rede neural. A API Delegate do TensorFlow Lite resolve esse problema atuando como uma ponte entre o tempo de execução do TFLite e essas APIs de nível inferior.

ambiente de execução com delegados

Como escolher um delegado

O LiteRT oferece suporte a vários delegados, cada um otimizado para determinadas plataformas e tipos específicos de modelos. Normalmente, há vários delegados aplicáveis ao seu caso de uso, dependendo de dois critérios principais: a plataforma (Android ou iOS?) que você segmenta e o tipo de modelo (ponto flutuante ou quantizado?) que você está tentando acelerar.

Delegados por plataforma

Multiplataforma (Android e iOS)

  • Delegado de GPU: pode ser usado no Android e no iOS. Ele é otimizado para executar modelos de ponto flutuante de 32 e 16 bits quando uma GPU está disponível. Ele também é compatível com modelos quantizados de 8 bits e oferece desempenho de GPU equivalente às versões de ponto flutuante. Para detalhes sobre o delegado de GPU, consulte LiteRT na GPU.

iOS

  • Delegado do Core ML para iPhones e iPads mais recentes: para iPhones e iPads mais recentes em que o Neural Engine está disponível, use o delegado do Core ML para acelerar a inferência de modelos de ponto flutuante de 32 ou 16 bits. O Neural Engine está disponível em dispositivos móveis da Apple com A12 SoC ou versões mais recentes. Para uma visão geral do delegado do Core ML e instruções detalhadas, consulte Delegado do Core ML do LiteRT.

Delegados por tipo de modelo

Cada acelerador é projetado com uma determinada largura de bits de dados em mente. Se você fornecer um modelo de ponto flutuante a um delegado que só aceita operações quantizadas de 8 bits, ele vai rejeitar todas as operações, e o modelo será executado totalmente na CPU. Para evitar essas surpresas, a tabela abaixo oferece uma visão geral do suporte a delegados com base no tipo de modelo:

Tipo de modelo GPU CoreML
Ponto flutuante (32 bits) Sim Sim
Quantização float16 pós-treinamento Sim Sim
Quantização de intervalo dinâmico pós-treinamento Sim Não
Quantização de números inteiros pós-treinamento Sim Não
Treinamento com reconhecimento de quantização Sim Não

Como validar a performance

As informações nesta seção servem como uma diretriz aproximada para criar uma lista dos delegados que podem melhorar seu aplicativo. No entanto, é importante observar que cada delegado tem um conjunto predefinido de operações compatíveis e pode ter um desempenho diferente dependendo do modelo e do dispositivo. Portanto, geralmente é recomendável fazer alguns comparativos para avaliar a utilidade de um delegado para suas necessidades. Isso também ajuda a justificar o aumento no tamanho do binário associado à vinculação de um delegado ao tempo de execução do LiteRT.

O LiteRT tem ferramentas abrangentes de avaliação de performance e precisão que podem capacitar os desenvolvedores a usar delegados com confiança no aplicativo. Essas ferramentas são discutidas na próxima seção.

Ferramentas de avaliação

Latência e consumo de memória

A ferramenta de comparativo de mercado do LiteRT pode ser usada com parâmetros adequados para estimar o desempenho do modelo, incluindo latência média de inferência, sobrecarga de inicialização, uso de memória etc. Essa ferramenta oferece suporte a várias flags para descobrir a melhor configuração de delegado para seu modelo. Por exemplo, --gpu_backend=gl pode ser especificado com --use_gpu para medir a execução da GPU com OpenGL. A lista completa de parâmetros de delegação compatíveis está definida na documentação detalhada.

Confira um exemplo de execução de um modelo quantizado com GPU via adb:

adb shell /data/local/tmp/benchmark_model \
  --graph=/data/local/tmp/mobilenet_v1_224_quant.tflite \
  --use_gpu=true

Faça o download da versão pré-criada dessa ferramenta para Android, arquitetura ARM de 64 bits aqui (mais detalhes).

Precisão e correção

Normalmente, os delegados realizam cálculos com uma precisão diferente das contrapartes da CPU. Como resultado, há uma troca de precisão (geralmente pequena) associada ao uso de um delegado para aceleração de hardware. Isso nem sempre é verdade. Por exemplo, como a GPU usa precisão de ponto flutuante para executar modelos quantizados, pode haver uma pequena melhoria na precisão (por exemplo, <1% de melhoria no Top-5 na classificação de imagens do ILSVRC).

O LiteRT tem dois tipos de ferramentas para medir a precisão do comportamento de um delegado para um determinado modelo: baseada em tarefas e independente de tarefas. Todas as ferramentas descritas nesta seção são compatíveis com os parâmetros avançados de delegação usados pela ferramenta de comparativo de mercado da seção anterior. As subseções abaixo se concentram na avaliação de delegados (o delegado tem o mesmo desempenho que a CPU?) em vez da avaliação do modelo (o modelo é adequado para a tarefa?).

Avaliação baseada em tarefas

O LiteRT tem ferramentas para avaliar a correção em duas tarefas baseadas em imagens:

Binários pré-criados dessas ferramentas (Android, arquitetura ARM de 64 bits) e documentação podem ser encontrados aqui:

O exemplo abaixo demonstra a avaliação de classificação de imagens com GPU em um Pixel 4:

adb shell /data/local/tmp/run_eval \
  --model_file=/data/local/tmp/mobilenet_quant_v1_224.tflite \
  --ground_truth_images_path=/data/local/tmp/ilsvrc_images \
  --ground_truth_labels=/data/local/tmp/ilsvrc_validation_labels.txt \
  --model_output_labels=/data/local/tmp/model_output_labels.txt \
  --output_file_path=/data/local/tmp/accuracy_output.txt \
  --num_images=0 # Run on all images. \
  --use_gpu=true

A saída esperada é uma lista de métricas Top-K de 1 a 10:

Top-1 Accuracy: 0.733333
Top-2 Accuracy: 0.826667
Top-3 Accuracy: 0.856667
Top-4 Accuracy: 0.87
Top-5 Accuracy: 0.89
Top-6 Accuracy: 0.903333
Top-7 Accuracy: 0.906667
Top-8 Accuracy: 0.913333
Top-9 Accuracy: 0.92
Top-10 Accuracy: 0.923333

Avaliação independente de tarefas

Para tarefas em que não há uma ferramenta de avaliação no dispositivo estabelecida ou se você estiver testando modelos personalizados, o LiteRT tem a ferramenta Diferença de inferência. (Android, arquitetura binária ARM de 64 bits aqui)

O Inference Diff compara a execução do LiteRT (em termos de latência e desvio do valor de saída) em duas configurações:

  • Inferência de CPU com uma só linha de execução
  • Inferência definida pelo usuário: definida por estes parâmetros

Para isso, a ferramenta gera dados gaussianos aleatórios e os transmite por dois interpreters do TFLite: um executando kernels de CPU de linha única e o outro parametrizado pelos argumentos do usuário.

Ele mede a latência dos dois, bem como a diferença absoluta entre os tensores de saída de cada intérprete, elemento por elemento.

Para um modelo com um único tensor de saída, a saída pode ser assim:

Num evaluation runs: 50
Reference run latency: avg=84364.2(us), std_dev=12525(us)
Test run latency: avg=7281.64(us), std_dev=2089(us)
OutputDiff[0]: avg_error=1.96277e-05, std_dev=6.95767e-06

Isso significa que, para o tensor de saída no índice 0, os elementos da saída da CPU são diferentes da saída do delegado em uma média de 1.96e-05.

Interpretar esses números exige um conhecimento mais profundo do modelo e do que cada tensor de saída significa. Se for uma regressão simples que determina algum tipo de pontuação ou incorporação, a diferença será baixa. Caso contrário, haverá um erro com o delegado. No entanto, saídas como a "classe de detecção" dos modelos SSD são um pouco mais difíceis de interpretar. Por exemplo, ela pode mostrar uma diferença usando essa ferramenta, mas isso não significa que há algo realmente errado com o delegado. Considere duas classes (falsas): "TV (ID: 10)" e "Monitor (ID: 20)". Se um delegado estiver um pouco fora da verdade absoluta e mostrar um monitor em vez de uma TV, a diferença de saída para esse tensor poderá ser algo tão alto quanto 20-10 = 10.