Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
tensorflow
GitHub Repository: tensorflow/docs-l10n
Path: blob/master/site/pt-br/lite/performance/quantization_debugger.ipynb
25118 views
Kernel: Python 3
#@title Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License.

Inspeção de erros de quantização com o depurador

Embora a quantização de números inteiros forneça melhorias de tamanho do modelo e latência, o modelo quantizado nem sempre funcionará como esperado. Geralmente, o esperado é que a qualidade do modelo (por exemplo, exatidão, mAP, WER) seja um pouco inferior ao modelo float original. No entanto, há casos em que a qualidade do modelo pode ficar abaixo das suas expectativas ou gerar resultados totalmente errados.

Quando ocorrer esse problema, é complicado e trabalhoso localizar a causa raiz do erro de quantização, mas corrigi-lo é ainda mais difícil. Para ajudar no processo de inspeção do modelo, o depurador de quantização pode ser usado para identificar as camadas problemáticas, e a quantização seletiva pode deixar essas camadas problemáticas em float para que a exatidão do modelo seja recuperada à custa de um benefício reduzido da quantização.

Observação: essa API é experimental, e pode haver mudanças que causem quebras na API no decorrer das melhorias.

Depurador de quantização

O depurador de quantização possibilita a análise de métricas de qualidade da quantização no modelo existente. Ele pode automatizar os processos para a execução do modelo com um dataset de depuração e coletar métricas de qualidade de quantização para cada tensor.

Observação: no momento, o depurador de quantização e a quantização seletiva só funcionam para a quantização de números inteiros com ativações int8.

Pré-requisitos

Se você já tem um pipeline para quantizar um modelo, tem todos os elementos necessários para executar o depurador de quantização!

  • Modelo que será quantizado

  • Dataset representativo

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

Configuração

Esta seção prepara as bibliotecas, o modelo MobileNet v3 e o dataset 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
#@title Boilerplates and helpers MODEL_URI = 'https://tfhub.dev/google/imagenet/mobilenet_v3_small_100_224/classification/5' def process_image(data): data['image'] = tf.image.resize(data['image'], (224, 224)) / 255.0 return data # Representative dataset def representative_dataset(dataset): def _data_gen(): for data in dataset.batch(1): yield [data['image']] return _data_gen def eval_tflite(tflite_model, dataset): """Evaluates tensorflow lite classification model with the given dataset.""" interpreter = tf.lite.Interpreter(model_content=tflite_model) interpreter.allocate_tensors() input_idx = interpreter.get_input_details()[0]['index'] output_idx = interpreter.get_output_details()[0]['index'] results = [] for data in representative_dataset(dataset)(): interpreter.set_tensor(input_idx, data[0]) interpreter.invoke() results.append(interpreter.get_tensor(output_idx).flatten()) results = np.array(results) gt_labels = np.array(list(dataset.map(lambda data: data['label'] + 1))) accuracy = ( np.sum(np.argsort(results, axis=1)[:, -5:] == gt_labels.reshape(-1, 1)) / gt_labels.size) print(f'Top-5 accuracy (quantized): {accuracy * 100:.2f}%') model = tf.keras.Sequential([ tf.keras.layers.Input(shape=(224, 224, 3), batch_size=1), hub.KerasLayer(MODEL_URI) ]) model.compile( loss='sparse_categorical_crossentropy', metrics='sparse_top_k_categorical_accuracy') model.build([1, 224, 224, 3]) # Prepare dataset with 100 examples ds = tfds.load('imagenet_v2', split='test[:1%]') ds = ds.map(process_image) converter = tf.lite.TFLiteConverter.from_keras_model(model) converter.representative_dataset = representative_dataset(ds) converter.optimizations = [tf.lite.Optimize.DEFAULT] quantized_model = converter.convert()
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}%')
eval_tflite(quantized_model, ds)

Podemos ver que o modelo original tem uma exatidão top-5 muito mais alta do que nosso pequeno dataset, enquanto o modelo quantizado tem uma perda significativa na exatidão.

Etapa 1. Prepare o depurador

A maneira mais fácil de usar o depurador de quantização é fornecer o tf.lite.TFLiteConverter usado para quantizar 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))

Etapa 2. Execute o depurador e obtenha os resultados

Ao chamar QuantizationDebugger.run(), o depurador registrará as diferenças entre os tensores float e quantizados para o mesmo local da op e os processará com as determinadas métricas.

debugger.run()

As métricas processadas podem ser acessadas com QuantizationDebugger.layer_statistics ou despejadas em um arquivo de texto em 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 dump, o nome da op e o índice vêm primeiro, seguidos pelos parâmetros de quantização e métricas de erros (incluindo métricas de erros definidas pelo usuário, se houver alguma). O arquivo CSV resultante pode ser usado para escolher as camadas problemáticas com grandes métricas de erros de quantização.

Com o pandas ou outra biblioteca de processamento de dados, podemos inspecionar as métricas de erros detalhadas por camada.

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

Etapa 3. Analise os dados

Há várias maneiras de analisar os resultados. Primeiro, vamos adicionar algumas métricas úteis derivadas das saídas do depurador. (scale significa o fator de escala de quantização para cada tensor.)

  • Range, ou intervalo, (256 / scale)

  • RMSE / scale, ou raiz do erro quadrático médio / escala, (sqrt(mean_squared_error) / scale)

O RMSE / scale é próximo a 1 / sqrt(12) (cerca de 0,289) quando a distribuição quantizada é semelhante à distribuição float original, indicando um bom modelo quantizado. Quanto maior for o valor, mais provável será que a camada não tenha sido 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()

Há várias camadas com grandes intervalos, e algumas camadas com valores altos de RMSE/scale. Vamos obter as camadas com métricas de erros altas.

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 qualidade do modelo melhora ao deixar de quantizá-las.

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

Além disso, pular a quantização nas 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 pula alguns nós, para que o cálculo possa ser feito no domínio de ponto flutuante original. Quando as camadas corretas são puladas, podemos esperar a recuperação de um pouco da qualidade do modelo à custa de maior latência e tamanho do modelo.

No entanto, se você estiver planejando executar modelos quantizados em aceleradores somente números inteiros (por exemplo, DSP Hexagon ou EdgeTPU), a quantização seletiva causaria a fragmentação do modelo e resultaria em uma latência de inferência mais lenta, causada principalmente pelo custo da transferência de dados entre a CPU e esses aceleradores. Para evitar isso, considere realizar o treinamento consciente de quantização para manter todas as camadas em números inteiros, preservando a exatidão do modelo.

O depurador de quantização aceita as opções denylisted_nodes e denylisted_ops para pular a quantização em camadas específicas ou todas as instâncias de determinadas ops. Usando as suspected_layers que preparamos na etapa anterior, podemos utilizar o depurador de quantização para obter um modelo quantizado de forma seletiva.

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)
selective_quantized_model = debugger.get_nondebug_quantized_model() eval_tflite(selective_quantized_model, ds)

A exatidão é ainda mais baixa em comparação com o modelo float original, mas o modelo totalmente quantizado tem melhorias notáveis ao pular a quantização para cerca de 10 das 111 camadas.

Você também pode tentar não quantizar todas as operações na mesma classe. Por exemplo, para pular a quantização em todas as operações de média, você pode passar MEAN a 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)
selective_quantized_model = debugger.get_nondebug_quantized_model() eval_tflite(selective_quantized_model, ds)

Com essas técnicas, podemos melhorar a exatidão do modelo MobileNet V3. Em seguida, vamos explorar técnicas avançadas para melhorar ainda mais a exatidão.

Usos avançados

Com os seguintes recursos, 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 ao passá-las às opções. Para cada métrica, o resultado deverá ser um único valor float, e a métrica resultante será uma média das métricas de todos os exemplos.

  • layer_debug_metrics: calcula uma métrica baseada na diferença de cada saída de operação em relação às saídas de operações float e quantizadas.

  • layer_direct_compare_metrics: em vez de só obter a diferença, calcula uma métrica baseada nos tensores float e quantizados brutos e seus parâmetros de quantização (escala, ponto zero).

  • model_debug_metrics: só é usada quando float_model_(path|content) é passado ao depurador. Além das métricas no nível da operação, a saída da camada final é comparada à saída de referência do modelo float 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)
debugger.run()
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 de model_debug_metrics pode ser visto separadamente de debugger.model_statistics.

debugger.model_statistics

Usando a API mlir_quantize (interna) para acessar recursos detalhados

Observação: alguns recursos na seção a seguir, TFLiteConverter._experimental_calibrate_only e converter.mlir_quantize, são APIs internas experimentais e estão sujeitas a mudanças incompatíveis com versões anteriores.

from tensorflow.lite.python import convert

Modo de verificação do modelo inteiro

O comportamento padrão para a geração do modelo de depuração é a verificação por camada. Nesse modo, a entrada para o par de operações float e quantizadas é da mesma fonte (operação quantizada anteriormente). Outro modo é a verificação do modelo inteiro, em que os modelos float e quantizados são separados. Esse modo seria útil para observar como o erro é propagado pelo modelo. Para ativar, defina 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()
# 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))

Quantização seletiva de um modelo já calibrado

Você pode chamar convert.mlir_quantize diretamente para obter o modelo quantizado seletivamente do modelo já calibrado. Isso é especialmente útil quando você quer calibrar o modelo uma vez e experimentar com várias combinações de denylist.

selective_quantized_model = convert.mlir_quantize( calibrated_model, denylisted_nodes=suspected_layers) eval_tflite(selective_quantized_model, ds)