Inspection des erreurs de quantification avec le débogueur de quantification

Voir sur TensorFlow.org Exécuter dans Google Colab Voir la source sur GitHub Télécharger le cahier Voir le modèle TF Hub

Bien que la quantification en nombre entier améliore la taille et la latence du modèle, le modèle quantifié ne fonctionnera pas toujours comme prévu. On s'attend généralement à ce que la qualité du modèle (par exemple, précision, mAP, WER) soit légèrement inférieure à celle du modèle flottant d'origine. Cependant, il existe des cas où la qualité du modèle peut être inférieure à vos attentes ou générer des résultats complètement erronés.

Lorsque ce problème se produit, il est difficile et douloureux de repérer la cause première de l'erreur de quantification, et il est encore plus difficile de corriger l'erreur de quantification. Pour faciliter ce processus d'inspection du modèle, débogueur de quantification peut être utilisé pour identifier les couches problématiques, et la quantification sélective peut laisser les couches problématiques en float de sorte que la précision du modèle peut être récupéré au coût de bénéfice réduit de quantification.

Débogueur de quantification

Le débogueur de quantification permet d'effectuer une analyse de métrique de qualité de quantification dans le modèle existant. Le débogueur de quantification peut automatiser les processus d'exécution du modèle avec un ensemble de données de débogage et collecter des métriques de qualité de quantification pour chaque tenseur.

Conditions préalables

Si vous disposez déjà d'un pipeline pour quantifier un modèle, vous disposez de tous les éléments nécessaires pour exécuter le débogueur de quantification !

  • Modèle à quantifier
  • Ensemble de données représentatif

En plus du modèle et des données, vous devrez utiliser un cadre de traitement de données (par exemple, des pandas, des feuilles de calcul Google) pour analyser les résultats exportés.

Installer

Cette section prépare les bibliothèques, le modèle MobileNet v3 et l'ensemble de données de test de 100 images.

# 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

Chaudronniers et aides

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%

Nous pouvons voir que le modèle d'origine a une précision top-5 beaucoup plus élevée pour notre petit ensemble de données, tandis que le modèle quantifié a une perte de précision significative.

Étape 1. Préparation du débogueur

Pour utiliser facilement le débogueur de quantification est de fournir tf.lite.TFLiteConverter que vous avez utilisé pour quantifier le modèle.

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

Étape 2. Exécuter le débogueur et obtenir les résultats

Lorsque vous appelez QuantizationDebugger.run() , le débogueur enregistre les différences entre les tenseurs de flotteur et tenseurs pour le même quantifiées emplacement op, et de les traiter avec des mesures données.

debugger.run()

Les mesures traitées sont accessibles avec QuantizationDebugger.layer_statistics , ou peuvent être jetés dans un fichier texte au format CSV avec 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

Pour chaque ligne de la décharge, le nom et l' index op vient en premier, suivi de paramètres de quantification et de métrique d'erreur (y compris les mesures d'erreur définies par l' utilisateur , le cas échéant). Le fichier CSV résultant peut être utilisé pour sélectionner des couches problématiques avec de grandes métriques d'erreur de quantification.

Avec des pandas ou d'autres bibliothèques de traitement de données, nous pouvons inspecter des métriques d'erreur détaillées par couche.

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

Étape 3. Analyse des données

Il existe différentes manières d'analyser le résultat. Tout d'abord, ajoutons quelques métriques utiles dérivées des sorties du débogueur. ( scale signifie que le facteur d'échelle de quantification pour chaque tenseur).

  • Gamme ( 256 / scale )
  • RMSE / échelle ( sqrt(mean_squared_error) / scale )

La RMSE / scale est proche de 1 / sqrt(12) (~ 0,289) lorsque la distribution est similaire à quantifiée la distribution de flotteur d' origine, ce qui indique un bon modèle quantifiée. Plus la valeur est grande, il est plus probable que la couche ne soit pas bien quantifiée.

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

Il y a beaucoup de couches avec de larges gammes, et certaines couches qui ont une grande RMSE/scale des valeurs. Obtenons les couches avec des métriques d'erreur élevées.

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

Avec ces couches, vous pouvez essayer une quantification sélective pour voir si la non-quantification de ces couches améliore la qualité du modèle.

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

En plus de cela, sauter la quantification pour les premières couches contribue également à améliorer la qualité du modèle quantifié.

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

Quantification sélective

La quantification sélective ignore la quantification pour certains nœuds, de sorte que le calcul peut s'effectuer dans le domaine à virgule flottante d'origine. Lorsque les couches correctes sont ignorées, nous pouvons nous attendre à une récupération de la qualité du modèle au prix d'une latence et d'une taille de modèle accrues.

Cependant, si vous prévoyez d'exécuter des modèles quantifiés sur des accélérateurs à nombre entier uniquement (par exemple, Hexagon DSP, EdgeTPU), la quantification sélective entraînerait une fragmentation du modèle et entraînerait une latence d'inférence plus lente principalement causée par le coût de transfert de données entre le processeur et ces accélérateurs. . Pour éviter cela, vous pouvez envisager d' exécuter une formation consciente quantification pour maintenir toutes les couches en entier tout en préservant la précision du modèle.

Option de débogueur Quantification accepte denylisted_nodes et denylisted_ops des options pour sauter la quantification pour les couches spécifiques, ou toutes les instances d'ops spécifiques. En utilisant suspected_layers nous avons préparé à l'étape précédente, nous pouvons utiliser le débogueur de quantification pour obtenir un modèle quantifié de manière sélective.

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%

La précision est toujours inférieure à celle du modèle flottant d'origine, mais nous avons une amélioration notable par rapport à l'ensemble du modèle quantifié en sautant la quantification pour environ 10 couches sur 111 couches.

Vous pouvez également essayer de ne pas quantifier toutes les opérations de la même classe. Par exemple, pour ignorer la quantification pour toutes les opérations moyennes, vous pouvez passer MEAN à 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%

Grâce à ces techniques, nous sommes en mesure d'améliorer la précision du modèle quantifié MobileNet V3. Ensuite, nous explorerons des techniques avancées pour améliorer encore plus la précision du modèle.

Utilisations avancées

Avec les fonctionnalités suivantes, vous pouvez personnaliser davantage votre pipeline de débogage.

Métriques personnalisées

Par défaut, le débogueur de quantification émet cinq métriques pour chaque différence float-quant : taille du tenseur, écart type, erreur moyenne, erreur absolue maximale et erreur quadratique moyenne. Vous pouvez ajouter plus de métriques personnalisées en les transmettant aux options. Pour chaque métrique, le résultat doit être une valeur flottante unique et la métrique résultante sera une moyenne des métriques de tous les exemples.

  • layer_debug_metrics : calculer métrique basée sur diff pour chaque sorties op de flotteur et sorties op quantifiées.
  • layer_direct_compare_metrics : plutôt que d' obtenir diff seulement, cette calculera métrique basée sur flotté brut et tenseurs quantifiées, et ses paramètres de quantification (échelle, point zéro)
  • model_debug_metrics : utilisé uniquement lorsque float_model_(path|content) est passé au débogueur. En plus des métriques de niveau opérationnel, la sortie de la couche finale est comparée à la sortie de référence du modèle flottant d'origine.
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()

Le résultat de model_debug_metrics peut être vu séparément debugger.model_statistics .

debugger.model_statistics
{'argmax_accuracy': 0.36}

Utilisation de l'API (interne) mlir_quantize pour accéder à des fonctionnalités approfondies

from tensorflow.lite.python import convert

Mode de vérification du modèle entier

Le comportement par défaut pour la génération du modèle de débogage est la vérification par couche. Dans ce mode, l'entrée pour la paire d'opérations flottantes et de quantification provient de la même source (opération quantifiée précédente). Un autre mode est la vérification du modèle entier, où les modèles flottant et de quantification sont séparés. Ce mode serait utile pour observer comment l'erreur se propage vers le bas du modèle. Pour activer, enable_whole_model_verify=True à convert.mlir_quantize tout en générant manuellement le modèle de débogage.

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

Quantification sélective à partir d'un modèle déjà calibré

Vous pouvez appeler directement convert.mlir_quantize pour obtenir le modèle sélectif à partir du modèle quantifiée déjà calibré. Cela serait particulièrement utile lorsque vous souhaitez calibrer le modèle une fois et expérimenter différentes combinaisons de listes de refus.

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%