Inspecionando Erros de Quantização com Depurador de Quantização

Ver no TensorFlow.org Executar no Google Colab Ver fonte no GitHub Baixar caderno Veja o modelo TF Hub

Embora a quantização de número inteiro completo forneça tamanho e latência de modelo aprimorados, o modelo quantizado nem sempre funcionará conforme o esperado. Normalmente, espera-se que a qualidade do modelo (por exemplo, precisão, mAP, WER) seja um pouco menor do que o modelo flutuante original. No entanto, existem casos em que a qualidade do modelo pode ficar abaixo de sua expectativa ou gerar resultados completamente errados.

Quando esse problema acontece, é complicado e doloroso identificar a causa raiz do erro de quantização e é ainda mais difícil consertar o erro de quantização. Para auxiliar este processo de controlo modelo, depurador de quantificao pode ser utilizado para identificar camadas problemáticos, e quantização selectiva pode deixar essas camadas problemáticos na bóia de modo a que a precisão do modelo pode ser recuperado no custo do benefício reduzido de quantização.

Depurador de quantização

O depurador de quantização torna possível fazer análises métricas de qualidade de quantização no modelo existente. O depurador de quantização pode automatizar processos para executar o modelo com um conjunto de dados de depuração e coletar métricas de qualidade de quantização para cada tensor.

Pré-requisitos

Se você já tem um pipeline para quantizar um modelo, você tem todas as peças necessárias para executar o depurador de quantização!

  • Modelo para quantizar
  • Conjunto de dados representativo

Além do modelo e dos dados, você precisará usar uma estrutura de processamento de dados (por exemplo, pandas, planilhas do Google) para analisar os resultados exportados.

Configurar

Esta seção prepara bibliotecas, modelo MobileNet v3 e conjunto de dados de teste de 100 imagens.

# Quantization debugger is available from TensorFlow 2.7.0
pip uninstall -y tensorflow
pip install tf-nightly
pip install tensorflow_datasets --upgrade  # imagenet_v2 needs latest checksum
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import tensorflow as tf
import tensorflow_datasets as tfds
import tensorflow_hub as hub

Boilerplates e ajudantes

2021-10-30 11:57:45.262002: W tensorflow/python/util/util.cc:368] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
INFO:tensorflow:Assets written to: /tmp/tmp_3ry7zon/assets
INFO:tensorflow:Assets written to: /tmp/tmp_3ry7zon/assets
2021-10-30 11:57:52.134354: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:363] Ignored output_format.
2021-10-30 11:57:52.134407: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:366] Ignored drop_control_dependency.
fully_quantize: 0, inference_type: 6, input_inference_type: 0, output_inference_type: 0
test_ds = ds.map(lambda data: (data['image'], data['label'] + 1)).batch(16)
loss, acc = model.evaluate(test_ds)
print(f'Top-5 accuracy (float): {acc * 100:.2f}%')
7/7 [==============================] - 6s 33ms/step - loss: 88.6092 - sparse_top_k_categorical_accuracy: 11.7143
Top-5 accuracy (float): 1171.43%
eval_tflite(quantized_model, ds)
Top-5 accuracy (quantized): 51.00%

Podemos ver que o modelo original tem uma precisão dos cinco primeiros muito mais alta para nosso pequeno conjunto de dados, enquanto o modelo quantizado tem uma perda significativa de precisão.

Etapa 1. Preparação do depurador

Maneira mais fácil de usar o depurador de quantização é fornecer tf.lite.TFLiteConverter que você tem usado para quantificar o modelo.

converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset(ds)

# my_debug_dataset should have the same format as my_representative_dataset
debugger = tf.lite.experimental.QuantizationDebugger(
    converter=converter, debug_dataset=representative_dataset(ds))
INFO:tensorflow:Assets written to: /tmp/tmpoa_5gejn/assets
INFO:tensorflow:Assets written to: /tmp/tmpoa_5gejn/assets
2021-10-30 11:58:34.006052: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:363] Ignored output_format.
2021-10-30 11:58:34.006103: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:366] Ignored drop_control_dependency.
fully_quantize: 0, inference_type: 6, input_inference_type: 0, output_inference_type: 0

Etapa 2. Executando o depurador e obtendo os resultados

Quando você chama QuantizationDebugger.run() , o depurador irá registrar diferenças entre tensores flutuar e tensores quantizados para o mesmo local op, e processá-los com determinadas métricas.

debugger.run()

As métricas processados podem ser acessados com QuantizationDebugger.layer_statistics , ou pode ser despejado em um arquivo de texto no formato CSV com QuantizationDebugger.layer_statistics_dump() .

RESULTS_FILE = '/tmp/debugger_results.csv'
with open(RESULTS_FILE, 'w') as f:
  debugger.layer_statistics_dump(f)
head /tmp/debugger_results.csv

Para cada linha no despejo, o nome op e índice vem em primeiro lugar, seguido por parâmetros de quantização e métricas de erros (incluindo métricas de erro definido pelo utilizador , se for o caso). O arquivo CSV resultante pode ser usado para selecionar camadas problemáticas com grandes métricas de erro de quantização.

Com o pandas ou outras bibliotecas de processamento de dados, podemos inspecionar métricas de erro detalhadas por camada.

layer_stats = pd.read_csv(RESULTS_FILE)
layer_stats.head()

Etapa 3. Análise de dados

Existem várias maneiras de analisar o resultado. Primeiro, vamos adicionar algumas métricas úteis derivadas das saídas do depurador. ( scale significa que o factor de escala de quantificao para cada tensor.)

  • Range ( 256 / scale )
  • RMSE / escala ( sqrt(mean_squared_error) / scale )

O RMSE / scale é próximo de 1 / sqrt(12) (~ 0,289) quando a distribuição quantizado é semelhante à distribuição flutuador original, indicando um bom modelo quantizado. Quanto maior for o valor, é mais provável que a camada não seja bem quantizada.

layer_stats['range'] = 255.0 * layer_stats['scale']
layer_stats['rmse/scale'] = layer_stats.apply(
    lambda row: np.sqrt(row['mean_squared_error']) / row['scale'], axis=1)
layer_stats[['op_name', 'range', 'rmse/scale']].head()
plt.figure(figsize=(15, 5))
ax1 = plt.subplot(121)
ax1.bar(np.arange(len(layer_stats)), layer_stats['range'])
ax1.set_ylabel('range')
ax2 = plt.subplot(122)
ax2.bar(np.arange(len(layer_stats)), layer_stats['rmse/scale'])
ax2.set_ylabel('rmse/scale')
plt.show()

png

Há muitas camadas com intervalos de largura, e algumas camadas que têm alta RMSE/scale de valores. Vamos obter as camadas com altas métricas de erro.

layer_stats[layer_stats['rmse/scale'] > 0.7][[
    'op_name', 'range', 'rmse/scale', 'tensor_name'
]]

Com essas camadas, você pode tentar a quantização seletiva para ver se a quantização dessas camadas melhora a qualidade do modelo.

suspected_layers = list(
    layer_stats[layer_stats['rmse/scale'] > 0.7]['tensor_name'])

Além disso, pular a quantização para as primeiras camadas também ajuda a melhorar a qualidade do modelo quantizado.

suspected_layers.extend(list(layer_stats[:5]['tensor_name']))

Quantização Seletiva

A quantização seletiva ignora a quantização para alguns nós, de modo que o cálculo pode acontecer no domínio de ponto flutuante original. Quando as camadas corretas são ignoradas, podemos esperar alguma recuperação da qualidade do modelo ao custo de latência e tamanho do modelo aumentados.

No entanto, se você estiver planejando executar modelos quantizados em aceleradores somente inteiros (por exemplo, Hexagon DSP, EdgeTPU), a quantização seletiva causaria a fragmentação do modelo e resultaria em latência de inferência mais lenta causada principalmente pelo custo de transferência de dados entre a CPU e esses aceleradores . Para evitar isso, você pode considerar a execução de quantização formação consciente para manter todas as camadas em inteiro, preservando a precisão do modelo.

Opção do depurador quantização aceita denylisted_nodes e denylisted_ops opções para pular quantização para camadas específicas, ou todas as instâncias do ops específicos. Usando suspected_layers nós preparados a partir da etapa anterior, podemos usar depurador quantização para obter um modelo quantizado seletivamente.

debug_options = tf.lite.experimental.QuantizationDebugOptions(
    denylisted_nodes=suspected_layers)
debugger = tf.lite.experimental.QuantizationDebugger(
    converter=converter,
    debug_dataset=representative_dataset(ds),
    debug_options=debug_options)
INFO:tensorflow:Assets written to: /tmp/tmpqqc57uli/assets
INFO:tensorflow:Assets written to: /tmp/tmpqqc57uli/assets
2021-10-30 11:59:13.603355: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:363] Ignored output_format.
2021-10-30 11:59:13.603400: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:366] Ignored drop_control_dependency.
fully_quantize: 0, inference_type: 6, input_inference_type: 0, output_inference_type: 0
selective_quantized_model = debugger.get_nondebug_quantized_model()
eval_tflite(selective_quantized_model, ds)
fully_quantize: 0, inference_type: 6, input_inference_type: 0, output_inference_type: 0
Top-5 accuracy (quantized): 64.00%

A precisão ainda é menor em comparação com o modelo flutuante original, mas temos uma melhoria notável de todo o modelo quantizado, pulando a quantização para ~ 10 camadas de 111 camadas.

Você também pode tentar não quantizar todas as ops na mesma classe. Por exemplo, para pular quantização para todos os ops médios, você pode passar MEAN para denylisted_ops .

debug_options = tf.lite.experimental.QuantizationDebugOptions(
    denylisted_ops=['MEAN'])
debugger = tf.lite.experimental.QuantizationDebugger(
    converter=converter,
    debug_dataset=representative_dataset(ds),
    debug_options=debug_options)
INFO:tensorflow:Assets written to: /tmp/tmpxltlornb/assets
INFO:tensorflow:Assets written to: /tmp/tmpxltlornb/assets
2021-10-30 11:59:44.677473: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:363] Ignored output_format.
2021-10-30 11:59:44.677519: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:366] Ignored drop_control_dependency.
fully_quantize: 0, inference_type: 6, input_inference_type: 0, output_inference_type: 0
selective_quantized_model = debugger.get_nondebug_quantized_model()
eval_tflite(selective_quantized_model, ds)
fully_quantize: 0, inference_type: 6, input_inference_type: 0, output_inference_type: 0
Top-5 accuracy (quantized): 54.00%

Com essas técnicas, podemos melhorar a precisão do modelo quantizado MobileNet V3. A seguir, exploraremos técnicas avançadas para melhorar ainda mais a precisão do modelo.

Usos avançados

Com os recursos a seguir, você pode personalizar ainda mais seu pipeline de depuração.

Métricas personalizadas

Por padrão, o depurador de quantização emite cinco métricas para cada diferença float-quant: tamanho do tensor, desvio padrão, erro médio, erro absoluto máximo e erro quadrático médio. Você pode adicionar mais métricas personalizadas, passando-as para opções. Para cada métrica, o resultado deve ser um único valor flutuante e a métrica resultante será uma média das métricas de todos os exemplos.

  • layer_debug_metrics : calcular métrica baseada em diff para cada saídas op de bóia e saídas op quantizados.
  • layer_direct_compare_metrics : em vez de recebendo apenas diff, este irá calcular métrica baseada em float em bruto e tensores quantizados, e seus parâmetros de quantização (escala, ponto zero)
  • model_debug_metrics : usado somente quando float_model_(path|content) é passado para o depurador. Além das métricas de nível operacional, a saída da camada final é comparada à saída de referência do modelo flutuante original.
debug_options = tf.lite.experimental.QuantizationDebugOptions(
    layer_debug_metrics={
        'mean_abs_error': (lambda diff: np.mean(np.abs(diff)))
    },
    layer_direct_compare_metrics={
        'correlation':
            lambda f, q, s, zp: (np.corrcoef(f.flatten(),
                                             (q.flatten() - zp) / s)[0, 1])
    },
    model_debug_metrics={
        'argmax_accuracy': (lambda f, q: np.mean(np.argmax(f) == np.argmax(q)))
    })

debugger = tf.lite.experimental.QuantizationDebugger(
    converter=converter,
    debug_dataset=representative_dataset(ds),
    debug_options=debug_options)
INFO:tensorflow:Assets written to: /tmp/tmpm7cb9qcd/assets
INFO:tensorflow:Assets written to: /tmp/tmpm7cb9qcd/assets
2021-10-30 12:00:18.502193: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:363] Ignored output_format.
2021-10-30 12:00:18.502238: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:366] Ignored drop_control_dependency.
INFO:tensorflow:Assets written to: /tmp/tmpzkg3ny_8/assets
INFO:tensorflow:Assets written to: /tmp/tmpzkg3ny_8/assets
2021-10-30 12:00:28.401195: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:363] Ignored output_format.
2021-10-30 12:00:28.401241: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:366] Ignored drop_control_dependency.
fully_quantize: 0, inference_type: 6, input_inference_type: 0, output_inference_type: 0
debugger.run()
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/numpy/lib/function_base.py:2691: RuntimeWarning: invalid value encountered in true_divide
  c /= stddev[:, None]
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/numpy/lib/function_base.py:2692: RuntimeWarning: invalid value encountered in true_divide
  c /= stddev[None, :]
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/lite/tools/optimize/debugging/python/debugger.py:382: RuntimeWarning: Mean of empty slice
  metrics[metric_name] = np.nanmean(metrics[metric_name])
CUSTOM_RESULTS_FILE = '/tmp/debugger_results.csv'
with open(CUSTOM_RESULTS_FILE, 'w') as f:
  debugger.layer_statistics_dump(f)

custom_layer_stats = pd.read_csv(CUSTOM_RESULTS_FILE)
custom_layer_stats[['op_name', 'mean_abs_error', 'correlation']].tail()

O resultado da model_debug_metrics pode ser visto separadamente da debugger.model_statistics .

debugger.model_statistics
{'argmax_accuracy': 0.36}

Usando a API mlir_quantize (interna) para acessar recursos detalhados

from tensorflow.lite.python import convert

Modo de verificação de modelo inteiro

O comportamento padrão para a geração do modelo de depuração é a verificação por camada. Neste modo, a entrada para float e par de op de quantização é da mesma fonte (op quantizado anterior). Outro modo é a verificação do modelo inteiro, onde os modelos de flutuação e quantização são separados. Este modo seria útil para observar como o erro está sendo propagado pelo modelo. Para ativar, enable_whole_model_verify=True para convert.mlir_quantize ao gerar o modelo de depuração manualmente.

converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.representative_dataset = representative_dataset(ds)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter._experimental_calibrate_only = True
calibrated_model = converter.convert()
INFO:tensorflow:Assets written to: /tmp/tmp2oa0sp06/assets
INFO:tensorflow:Assets written to: /tmp/tmp2oa0sp06/assets
2021-10-30 12:01:33.233118: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:363] Ignored output_format.
2021-10-30 12:01:33.233171: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:366] Ignored drop_control_dependency.
# Note that enable_numeric_verify and enable_whole_model_verify are set.
quantized_model = convert.mlir_quantize(
    calibrated_model,
    enable_numeric_verify=True,
    enable_whole_model_verify=True)
debugger = tf.lite.experimental.QuantizationDebugger(
    quant_debug_model_content=quantized_model,
    debug_dataset=representative_dataset(ds))
fully_quantize: 0, inference_type: 6, input_inference_type: 0, output_inference_type: 0

Quantização seletiva de um modelo já calibrado

Você pode chamar diretamente convert.mlir_quantize para obter o modelo quantized seletiva do modelo já calibrado. Isso seria particularmente útil quando você deseja calibrar o modelo uma vez e experimentar várias combinações de denylist.

selective_quantized_model = convert.mlir_quantize(
    calibrated_model, denylisted_nodes=suspected_layers)
eval_tflite(selective_quantized_model, ds)
fully_quantize: 0, inference_type: 6, input_inference_type: 0, output_inference_type: 0
Top-5 accuracy (quantized): 64.00%