A quantização pós-treinamento é uma técnica de conversão que pode reduzir o tamanho do modelo e melhorar a latência do acelerador de CPU e hardware, com pouca degradação na acurácia do modelo. É possível quantizar um modelo do TensorFlow de ponto flutuante já treinado ao convertê-lo para o formato LiteRT usando o conversor LiteRT.
Métodos de otimização
Há várias opções de quantização pós-treinamento. Confira uma tabela de resumo das opções e dos benefícios que elas oferecem:
| Técnica | Vantagens | Hardware |
|---|---|---|
| Quantização de intervalo dinâmico | 4 vezes menor, 2 a 3 vezes mais rápido | CPU |
| Quantização de número inteiro | 4x menor, aceleração de 3x ou mais | CPU, Edge TPU e microcontroladores |
| Quantização float16 | 2 vezes menor, aceleração de GPU | CPU, GPU |
A árvore de decisão a seguir pode ajudar a determinar qual método de quantização pós-treinamento é melhor para seu caso de uso:

Sem quantização
A conversão para um modelo do TFLite sem quantização é um ponto de partida recomendado. Isso vai gerar um modelo TFLite de ponto 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 os operadores do modelo TF original são compatíveis com o TFLite e também podem ser usados como uma base para depurar erros de quantização introduzidos por métodos de quantização pós-treinamento subsequentes. Por exemplo, se um modelo TFLite quantizado produzir resultados inesperados, enquanto o modelo TFLite de ponto flutuante for preciso, podemos restringir o problema a erros introduzidos pela versão quantizada dos operadores 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 que você precise fornecer um conjunto de dados representativo para calibragem. Esse tipo de quantização quantiza estaticamente apenas os pesos de 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, os operadores "dynamic-range" quantizam dinamicamente as ativações com base no intervalo delas para 8 bits e realizam cálculos com pesos e ativações de 8 bits. Essa otimização oferece latências próximas às inferências de ponto fixo. No entanto, as saídas ainda são armazenadas usando ponto flutuante. Portanto, o aumento na velocidade das operações de intervalo dinâmico é menor do que um cálculo completo de ponto fixo.
Quantização de números inteiros completa
Para melhorar ainda mais a latência, reduzir o uso máximo de memória e garantir a compatibilidade com dispositivos ou aceleradores de hardware somente com números inteiros, verifique se todos os cálculos do modelo são quantizados com números inteiros.
Para quantização de números inteiros, é necessário calibrar ou estimar o intervalo, ou seja, (mínimo, máximo) de todos os tensores de ponto flutuante no modelo. Ao contrário dos tensores constantes, como pesos e vieses, os tensores variáveis, como entrada do modelo, ativações (saídas de camadas intermediárias) e saída do modelo, não podem ser calibrados, a menos que executemos alguns ciclos de inferência. Como resultado, o conversor exige um conjunto de dados representativo para calibrá-los. 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.
A partir da versão 2.7 do TensorFlow, é possível especificar o conjunto de dados representativo usando uma assinatura, 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, especifique o conjunto de dados múltiplo usando 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,
},
)
Para gerar o conjunto de dados representativo, forneça 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 baseada em assinatura em vez da abordagem baseada em lista de tensores de entrada, porque a ordenação de tensores de entrada pode ser facilmente invertida.
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 retorno de ponto flutuante (usando entrada/saída de ponto flutuante padrão)
Para quantizar totalmente um modelo com números inteiros, mas usar operadores de ponto flutuante quando eles não têm uma implementação de número inteiro (para garantir que a conversão ocorra sem problemas), siga estas 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
Criar modelos somente de números inteiros é um caso de uso comum para LiteRT para microcontroladores e TPUs de borda da Coral.
Além disso, para garantir a compatibilidade com dispositivos somente inteiros (como microcontroladores de 8 bits) e aceleradores (como a Coral Edge TPU), é possível aplicar a quantização de números inteiros para todas as operações, incluindo entrada e saída, seguindo estas 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 quantizando os pesos para float16, o padrão IEEE para números de ponto flutuante de 16 bits. Para ativar a quantização de ponto flutuante de 16 bits 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:
- Ele reduz o tamanho do modelo em até metade, já que todos os pesos se tornam metade do tamanho original.
- Ela causa uma perda mínima na acurácia.
- Ele é compatível com alguns delegados (por exemplo, o delegado de GPU), que podem operar diretamente em dados float16, resultando em uma execução mais rápida do que cálculos float32.
As desvantagens da quantização float16 são as seguintes:
- Ela não reduz a latência tanto quanto uma quantização para matemática de ponto fixo.
- Por padrão, um modelo quantizado float16 vai "desquantizar" os valores de peso para float32 quando executado na CPU. (Observe que o delegado da GPU não vai realizar essa desquantização, já que pode operar com dados float16.)
Somente números inteiros: ativações de 16 bits com pesos de 8 bits (experimental)
Esse é um esquema de quantização experimental. É semelhante ao esquema "somente números inteiros", mas as ativações são quantizadas com base no intervalo delas para 16 bits, os pesos são quantizados em números inteiros de 8 bits e o bias é quantizado em números inteiros de 64 bits. Isso é chamado de quantização 16x8.
A principal vantagem dessa quantização é que ela pode melhorar a acurácia significativamente, mas aumenta apenas um pouco o tamanho do modelo.
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 de 16x8 não for compatível com alguns operadores no modelo, ele ainda poderá ser quantizado, mas os operadores sem suporte serão mantidos em ponto flutuante. A opção a seguir precisa ser adicionada ao 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 acurácia fornecidas por esse esquema de quantização incluem:
- super-resolução,
- processamento de sinal de áudio, como cancelamento de ruído e beamforming;
- redução de ruído de imagem,
- Reconstrução de HDR de uma única imagem.
A desvantagem dessa quantização é:
- No momento, a inferência é visivelmente mais lenta do que o número inteiro de 8 bits devido à falta de uma implementação de kernel otimizada.
- No momento, ele é incompatível com os delegados do TFLite acelerados por hardware.
Confira um tutorial sobre esse 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, principalmente para redes menores. Modelos totalmente quantizados pré-treinados são fornecidos para redes específicas no Kaggle Models. É importante verificar a acurácia do modelo quantizado para verificar se qualquer degradação na acurácia está dentro dos limites aceitáveis. Há ferramentas para avaliar a acurácia do modelo LiteRT.
Como alternativa, se a queda de acurácia for muito alta, considere usar o treinamento com reconhecimento de quantização. No entanto, isso exige modificações durante o treinamento do modelo para adicionar nós de quantização falsos, enquanto as técnicas de quantização pós-treinamento nesta página usam um modelo pré-treinado.
Representação para tensores quantizados
A quantização de 8 bits aproxima os valores de ponto flutuante usando a seguinte fórmula.
\[real\_value = (int8\_value - zero\_point) \times scale\]
A representação tem duas partes principais:
Pesos por eixo (ou por canal) ou por tensor representados por valores de complemento de dois int8 no intervalo [-127, 127] com ponto zero igual a 0.
Ativações/entradas por tensor representadas por valores de complemento de dois int8 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 especificação de quantização. Recomendamos que os fornecedores de hardware que querem se conectar à interface de delegação do TensorFlow Lite implementem o esquema de quantização descrito lá.