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 quantificar um modelo flutuante do TensorFlow já treinado ao convertê-lo para o formato do TensorFlow Lite usando o TensorFlow Lite Converter.
Métodos de otimização
Há várias opções de quantização pós-treinamento para você escolher. Confira uma tabela de resumo das opções e os benefícios que elas oferecem:
Técnica | Vantagens | Hardware |
---|---|---|
Quantização de intervalo dinâmico | 4x menor, 2x a 3x mais rápido | CPU |
Quantização de números inteiros completos | 4 vezes menor, 3x ou mais acelerado | CPU, Edge TPU e microcontroladores |
Quantização Float16 | Aceleração de GPU 2x menor | 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 TFLite sem quantização é um ponto de partida recomendado. 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 os operadores do modelo original do TF são compatíveis com o TFLite e também podem ser usados como valor de referência para depurar erros de quantização introduzidos por métodos de quantização pós-treinamento subsequentes. Por exemplo, se um modelo do TFLite quantizado produzir resultados inesperados, enquanto o modelo flutuante do TFLite for preciso, poderemos restringir o problema aos 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 oferece uma computação mais rápida sem a necessidade de fornecer um conjunto de dados representativo para calibração. Esse tipo de quantização quantifica estaticamente apenas os pesos do ponto flutuante para o 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 de "intervalo dinâmico" quantificam as ativações dinamicamente com base no intervalo de 8 bits e realizam cálculos 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, as saídas ainda são armazenadas usando ponto flutuante. Portanto, 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
É possível conseguir outras melhorias de latência, reduções no pico de uso de memória e compatibilidade com dispositivos de hardware ou aceleradores com uso exclusivo de números inteiros, garantindo que toda a matemática do modelo seja quantizada por números inteiros.
Para a quantização de número inteiro total, é necessário calibrar ou estimar o intervalo,
por exemplo, (min, max) de todos os tensores de ponto flutuante no modelo. Ao contrário dos tensores constantes, como pesos e vieses, os tensores variáveis, como a entrada do modelo, as ativações (saídas de camadas intermediárias) e a 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 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 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 fornecido, especifique as chaves de assinatura para especificar o conjunto de dados:
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 o uso da abordagem baseada em assinaturas em vez da abordagem baseada em lista de tensores de entrada, porque a ordem do tensor de entrada pode ser facilmente alterada.
Para fins de teste, você pode usar 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 quantificar um modelo totalmente por números inteiros, mas usar operadores flutuantes quando eles não tiverem 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
A criação de modelos somente com números inteiros é um caso de uso comum do TensorFlow Lite para microcontroladores e das TPUs Coral Edge.
Além disso, para garantir a compatibilidade com dispositivos que só usam números inteiros (como microcontroladores de 8 bits) e aceleradores (como o Coral Edge TPU), é possível aplicar a quantização de números inteiros completos para todas as operações, incluindo a entrada e a 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 quantificando os pesos para float16, o padrão IEEE para números de ponto flutuante de 16 bits. Para ativar a quantização de pesos float16, 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.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 perda mínima de acurácia.
- Ele é compatível com alguns delegados (por exemplo, o delegado da GPU) que podem operar diretamente em dados float16, resultando em uma execução mais rápida do que os cálculos de float32.
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 float16 "desquantiza" os valores de pesos para float32 quando executado na CPU. Observe que o delegado da GPU não realizará 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. É semelhante ao esquema "somente número inteiro", mas as ativações são quantizadas com base no intervalo de 16 bits, os pesos são quantizados em números inteiros de 8 bits e o viés é quantizado em números inteiros de 64 bits. Isso é chamado também de quantização 16x8.
A principal vantagem dessa quantização é que ela pode melhorar significativamente a precisão, mas aumentar 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 16x8 não for compatível com alguns operadores no modelo, ele ainda poderá ser quantizado, mas os operadores incompatíveis são mantidos em flutuação. A opção a seguir precisa 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 fornecidas por esse esquema de quantização incluem:
- super-resolution,
- 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 do que o número inteiro completo de 8 bits devido à falta de implementação otimizada do kernel.
- No momento, ele é incompatível com os delegados do TFLite com aceleração de hardware existente.
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 em redes menores. Modelos totalmente quantizados pré-treinados são fornecidos para redes específicas em Modelos Kaggle. É importante verificar a acurácia do modelo quantizado para confirmar se qualquer degradação na acurácia está dentro dos limites aceitáveis. Existem ferramentas para avaliar a precisão do modelo do TensorFlow Lite.
Como alternativa, se a queda na precisão for muito alta, use o treinamento com reconhecimento de quantização. No entanto, fazer isso requer 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 atual.
Representação para tensores quantizados
A quantização de 8 bits se aproxima dos valores de ponto flutuante usando a fórmula a seguir.
\[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 por tensor representados pelos valores de complemento de int8 dois no intervalo [-127, 127] com ponto zero igual a 0.
Ativações/entradas por tensor representadas pelos 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 nossa especificação de quantização. Os fornecedores de hardware que quiserem se conectar à interface delegada do TensorFlow Lite são incentivados a implementar o esquema de quantização descrito lá.