Quantização pós-treinamento

A quantização pós-treinamento é uma técnica de conversão que pode reduzir o tamanho do modelo além de melhorar a latência do acelerador de hardware e CPU, com degradação na acurácia do modelo. É possível quantizar um ponto flutuante já treinado TensorFlow ao convertê-lo para o formato LiteRT usando o Conversor de LiteRT (em inglês).

Métodos de otimização

Há várias opções de quantização pós-treinamento para você escolher. Confira tabela resumida das opções e os benefícios que elas oferecem:

Técnica Vantagens Hardware
Intervalo dinâmico quantização 4x menor, 2x a 3x mais rápido CPU
Número inteiro completo quantização 4 vezes menor, 3x ou mais acelerado CPU, Edge TPU Microcontroladores
Quantização Float16 2x menor, GPU aceleração CPU, GPU

A árvore de decisão a seguir pode ajudar a determinar qual quantização pós-treinamento é melhor para seu caso de uso:

opções de otimização pós-treinamento

Sem quantização

Converter para um modelo TFLite sem quantização é um ponto de partida recomendado ponto Isso vai gerar um modelo TFLite flutuante.

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
tflite_quant_model = converter.convert()

Recomendamos que você faça isso como uma etapa inicial para verificar se o arquivo Os operadores do modelo do TF são compatíveis com o TFLite e também podem ser usados como para depurar erros de quantização introduzidos por etapas posteriores do treinamento métodos de quantização. Por exemplo, se um modelo TFLite quantizado produz resultados inesperados, enquanto o modelo flutuante do TFLite é preciso, podemos restringir o problema com erros introduzidos pela versão quantizada dos operadores do TFLite.

Quantização de intervalo dinâmico

A quantização de intervalo dinâmico reduz o uso de memória e acelera a computação sem precisar fornecer um conjunto de dados representativo para calibração. Isso tipo de quantização, quantifica estaticamente apenas os pesos do ponto flutuante para número inteiro no momento da conversão, o que fornece 8 bits de precisão:

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_quant_model = converter.convert()

Para reduzir ainda mais a latência durante a inferência, "intervalo dinâmico" operadores quantificar dinamicamente as ativações com base no intervalo de 8 bits e realizar computacionais com ativações e pesos de 8 bits. Essa otimização fornece latências próximas às inferências de ponto totalmente fixo. No entanto, os resultados ainda são armazenados usando ponto flutuante, de modo que o aumento da velocidade das operações de intervalo dinâmico é menor do que um cálculo completo de ponto fixo.

Quantização de números inteiros completos

Você pode obter melhorias de latência adicionais, reduções no pico de uso de memória e compatibilidade com dispositivos de hardware somente de números inteiros ou aceleradores, certificando-se toda a matemática do modelo é quantizada por números inteiros.

Para a quantização de números inteiros completos, você precisa calibrar ou estimar o intervalo, por exemplo, (min, max) de todos os tensores de ponto flutuante no modelo. Ao contrário da constante como pesos e vieses, tensores variáveis como entrada do modelo, ativações (saídas de camadas intermediárias) e a saída do modelo não podem ser calibrado, a menos que executemos alguns ciclos de inferência. Como resultado, o conversor requer um conjunto de dados representativo para calibrar. Esse conjunto de dados pode ser um pequeno subconjunto (cerca de 100 a 500 amostras) dos dados de treinamento ou validação. Consulte a função representative_dataset() abaixo.

Na versão 2.7 do TensorFlow, é possível especificar o conjunto de dados representativo usando uma signature, como no exemplo a seguir:

def representative_dataset():
  for data in dataset:
    yield {
      "image": data.image,
      "bias": data.bias,
    }

Se houver mais de uma assinatura no modelo do TensorFlow, será possível especifique o conjunto de dados múltiplo especificando as chaves de assinatura:

def representative_dataset():
  # Feed data set for the "encode" signature.
  for data in encode_signature_dataset:
    yield (
      "encode", {
        "image": data.image,
        "bias": data.bias,
      }
    )

  # Feed data set for the "decode" signature.
  for data in decode_signature_dataset:
    yield (
      "decode", {
        "image": data.image,
        "hint": data.hint,
      },
    )

É possível gerar o conjunto de dados representativo fornecendo uma lista de tensores de entrada:

def representative_dataset():
  for data in tf.data.Dataset.from_tensor_slices((images)).batch(1).take(100):
    yield [tf.dtypes.cast(data, tf.float32)]

Desde a versão 2.7 do TensorFlow, recomendamos usar a abordagem com base em assinaturas com a abordagem baseada em lista de tensores de entrada, porque a ordenação ser facilmente alterado.

Para fins de teste, use um conjunto de dados fictício da seguinte maneira:

def representative_dataset():
    for _ in range(100):
      data = np.random.rand(1, 244, 244, 3)
      yield [data.astype(np.float32)]
 

Número inteiro com substituto flutuante (usando entrada/saída de ponto flutuante padrão)

Para quantizar um modelo completamente inteiro, mas use operadores flutuantes quando eles não tiver uma implementação inteira (para garantir que a conversão ocorra sem problemas), use as seguintes etapas:

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset
tflite_quant_model = converter.convert()

Somente número inteiro

A criação de modelos somente de números inteiros é um caso de uso comum da LiteRT para Microcontroladores e Coral TPUs de borda.

Além disso, para garantir compatibilidade com dispositivos que só usam números inteiros (como dispositivos de microcontroladores) e aceleradores (como o Coral Edge TPU), é possível aplicar quantização de número inteiro total para todas as operações, incluindo entrada e saída, usando as seguintes etapas:

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
converter.inference_input_type = tf.int8  # or tf.uint8
converter.inference_output_type = tf.int8  # or tf.uint8
tflite_quant_model = converter.convert()

Quantização Float16

É possível reduzir o tamanho de um modelo de ponto flutuante quantificando os pesos para float16, o padrão IEEE para números de ponto flutuante de 16 bits. Para ativar o float16 quantização de pesos, siga estas etapas:

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_types = [tf.float16]
tflite_quant_model = converter.convert()

As vantagens da quantização float16 são as seguintes:

  • Reduz o tamanho do modelo em até metade (já que todos os pesos se tornam metade dos tamanho original).
  • Ela causa perda mínima de acurácia.
  • Ele é compatível com alguns delegados (por exemplo, o delegado da GPU) que podem operar diretamente nos dados float16, resultando em uma execução mais rápida do que o float32 computacionais.

As desvantagens da quantização float16 são as seguintes:

  • Isso não reduz a latência tanto quanto uma quantização para o cálculo de ponto fixo.
  • Por padrão, um modelo quantizado de float16 "desquantiza" os valores dos pesos para float32 quando executado na CPU. O delegado da GPU não executa essa desquantização, já que pode operar em dados float16.)

Somente número inteiro: ativações de 16 bits com pesos de 8 bits (experimental)

Este é um esquema de quantização experimental. Ela é semelhante à opção "número inteiro" mas as ativações são quantizadas com base no intervalo de 16 bits, pesos são quantizados em números inteiros de 8 bits e o viés é quantizado em inteiro de 64 bits. Isso também é chamada de quantização 16x8.

A principal vantagem dessa quantização é que ela pode melhorar a acurácia significativamente, mas só aumenta ligeiramente o tamanho dele.

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.representative_dataset = representative_dataset
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_ops = [tf.lite.OpsSet.EXPERIMENTAL_TFLITE_BUILTINS_ACTIVATIONS_INT16_WEIGHTS_INT8]
tflite_quant_model = converter.convert()

Se a quantização 16x8 não tiver suporte para alguns operadores no modelo, o modelo ainda pode ser quantizado, mas os operadores incompatíveis são mantidos em flutuação. A a opção a seguir deve ser adicionada a target_spec para permitir isso.

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.representative_dataset = representative_dataset
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_ops = [tf.lite.OpsSet.EXPERIMENTAL_TFLITE_BUILTINS_ACTIVATIONS_INT16_WEIGHTS_INT8,
tf.lite.OpsSet.TFLITE_BUILTINS]
tflite_quant_model = converter.convert()

Exemplos de casos de uso em que as melhorias de precisão oferecidas por este esquema de quantização incluem:

  • super-resolução,
  • processamento de sinais de áudio, como cancelamento de ruído e beamforming;
  • remoção de ruído da imagem,
  • Reconstrução HDR a partir de uma única imagem.

A desvantagem dessa quantização é:

  • Atualmente, a inferência é visivelmente mais lenta que o número inteiro de 8 bits devido ao falta de implementação otimizada do kernel.
  • No momento, ele é incompatível com o TFLite acelerado por hardware existente. delegados.
.

Confira um tutorial para este modo de quantização aqui.

Precisão do modelo

Como os pesos são quantizados após o treinamento, pode haver uma perda de acurácia, especialmente para redes menores. Modelos totalmente quantizados pré-treinados fornecidas para redes específicas no Kaggle modelos , É importante verificar a acurácia do modelo quantizado para verificar se qualquer degradação na acurácia está dentro dos limites aceitáveis. Existem ferramentas para avalie o modelo LiteRT precisão.

Como alternativa, se a queda na precisão for muito alta, considere usar a quantização cientes treinamento , No entanto, isso exige modificações durante o treinamento do modelo para adicionar de nós de quantização, enquanto as técnicas de quantização pós-treinamento neste use um modelo pré-treinado que já existe.

Representação para tensores quantizados

A quantização de 8 bits se aproxima dos valores de ponto flutuante usando o seguinte fórmula.

\[real\_value = (int8\_value - zero\_point) \times scale\]

A representação tem duas partes principais:

  • Pesos por eixo (também conhecido como por canal) ou pesos por tensor representados por int8 dois valores de complemento no intervalo [-127, 127] com ponto zero igual a 0.

  • Ativações/entradas por tensor representadas pelos valores de complemento de dois int8 em no intervalo [-128, 127], com um ponto zero no intervalo [-128, 127].

Para uma visão detalhada do nosso esquema de quantização, consulte a página spec. Fornecedores de hardware que querem se conectar ao TensorFlow A interface delegada do Lite é incentivada a implementar o esquema de quantização descritas aqui.