Ajuda a proteger a Grande Barreira de Corais com TensorFlow em Kaggle Junte Desafio

Quantização pós-treinamento

A quantização pós-treinamento é uma técnica de conversão que pode reduzir o tamanho do modelo e, ao mesmo tempo, melhorar a latência da CPU e do acelerador de hardware, com pouca degradação na precisão do modelo. Você pode quantificar um modelo TensorFlow flutuador já treinado quando você convertê-lo para o formato TensorFlow Lite usando o TensorFlow Lite Converter .

Métodos de Otimização

Existem várias opções de quantização pós-treinamento para escolher. Aqui está uma tabela de resumo das opções e dos benefícios que elas oferecem:

Técnica Benefícios Hardware
Quantização de faixa dinâmica 4x menor, aumento 2x-3x CPU
Quantização inteira completa 4x menor, 3x + aceleração CPU, Edge TPU, microcontroladores
Quantização Float16 2x menor, aceleração de GPU CPU, GPU

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

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

Quantização de faixa dinâmica

A forma mais simples de quantização pós-treinamento quantiza estaticamente apenas os pesos do ponto flutuante ao inteiro, que tem 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()

Na inferência, os pesos são convertidos de 8 bits de precisão para ponto flutuante e calculados usando kernels de ponto flutuante. Essa conversão é feita uma vez e armazenada em cache para reduzir a latência.

Para melhorar ainda mais a latência, os operadores de "faixa dinâmica" quantizam dinamicamente as ativações com base em sua faixa de 8 bits e realizam cálculos com pesos e ativações de 8 bits. Essa otimização fornece latências próximas à inferência de ponto fixo totalmente. No entanto, as saídas ainda são armazenadas usando ponto flutuante, de forma que a aceleração com operações de faixa dinâmica seja menor do que um cálculo de ponto fixo completo.

Quantização inteira completa

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

Para a quantização inteira completa, você precisa calibrar ou estimar a faixa, ou seja, (min, max) de todos os tensores de ponto flutuante no modelo. Ao contrário dos tensores constantes, como pesos e vieses, 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 requer um conjunto de dados representativo para calibrá-los. Este conjunto de dados pode ser um pequeno subconjunto (cerca de ~ 100-500 amostras) dos dados de treinamento ou validação. Referem-se a representative_dataset() função abaixo.

De TensorFlow 2,7 versão, você pode especificar o conjunto de dados representativo através de uma assinatura como o 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, você pode especificar 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,
      },
    )

Você pode 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 do TensorFlow 2.7, recomendamos o uso da abordagem baseada em assinatura em vez da abordagem baseada em lista de tensor de entrada porque a ordem do tensor de entrada pode ser facilmente invertida.

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)]
 

Inteiro com float fallback (usando a entrada / saída padrão do float)

Para quantizar totalmente o número inteiro de um modelo, 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), 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()

Apenas inteiro

Criando inteiro apenas modelos é um caso de uso comum para TensorFlow Lite para microcontroladores e Coral Borda TPUs .

Além disso, para garantir a compatibilidade com dispositivos apenas de número inteiro (como microcontroladores de 8 bits) e aceleradores (como o Coral Edge TPU), você pode aplicar a quantização inteira de número inteiro para todas as operações, incluindo a entrada e a 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

Você pode 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 habilitar a quantização float16 de pesos, 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 pela metade (já que todos os pesos tornam-se a metade de seu tamanho original).
  • Isso causa perda mínima de precisão.
  • Ele suporta alguns delegados (por exemplo, o delegado 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:

  • Não reduz a latência tanto quanto uma quantização para matemática de ponto fixo.
  • Por padrão, um modelo quantizado float16 irá "desquantizar" os valores dos pesos para float32 quando executado na CPU. (Observe que o delegado da GPU não realizará essa desquantização, uma vez que pode operar em dados float16.)

Apenas inteiro: ativações de 16 bits com pesos de 8 bits (experimental)

Este é um esquema de quantização experimental. É semelhante ao esquema "somente inteiro", mas as ativações são quantizadas com base em seu intervalo de 16 bits, os pesos são quantizados em inteiros de 8 bits e a polarização é quantizada em inteiros de 64 bits. Isso é conhecido como quantização 16x8 adicional.

A principal vantagem dessa quantização é que ela pode melhorar a precisão significativamente, mas apenas aumentar ligeiramente 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 suportada por alguns operadores no modelo, então o modelo ainda pode ser quantizado, mas os operadores não suportados mantidos em flutuação. A seguinte opção deve 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 precisão fornecidas por este esquema de quantização incluem: * super-resolução, * processamento de sinal de áudio, como cancelamento de ruído e formação de feixe, * redução de ruído de imagem, * reconstrução HDR de uma única imagem.

A desvantagem dessa quantização é:

  • Atualmente a inferência é visivelmente mais lenta do que o inteiro completo de 8 bits devido à falta de implementação otimizada do kernel.
  • Atualmente, é incompatível com os delegados TFLite acelerados por hardware existentes.

Um tutorial para este modo de quantização pode ser encontrada aqui .

Precisão do modelo

Como os pesos são quantizados após o treinamento, pode haver uma perda de precisão, especialmente para redes menores. Pré-treinados modelos totalmente quantificados são fornecidos para redes específicas no repositório de modelos TensorFlow Lite . É importante verificar a precisão do modelo quantizado para verificar se qualquer degradação na precisão está dentro dos limites aceitáveis. Existem ferramentas para avaliar a precisão do modelo TensorFlow Lite .

Alternativamente, se a queda de precisão é muito alta, considerar o uso de formação ciente 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 existente.

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 (também conhecidos por canal) ou por tensor representados por int8 valores de complemento de dois no intervalo [-127, 127] com ponto zero igual a 0.

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

Para uma visão detalhada de nosso esquema de quantização, consulte a nossa especificação de quantização . Os fornecedores de hardware que desejam se conectar à interface delegada do TensorFlow Lite são incentivados a implementar o esquema de quantização aqui descrito.