Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
tensorflow
GitHub Repository: tensorflow/docs-l10n
Path: blob/master/site/es-419/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.

Inspección de errores de cuantización con el depurador de cuantización

Aunque la cuantización entera completa ofrece una mejora del tamaño del modelo y de la latencia, el modelo cuantizado no siempre funcionará como se espera. Normalmente se espera que la calidad del modelo (por ejemplo, precisión, mAP, WER) sea ligeramente inferior a la del modelo flotante original. Sin embargo, hay casos en los que la calidad del modelo puede quedar por debajo de sus expectativas o generar resultados completamente erróneos.

Cuando se produce este problema, es complicado y doloroso detectar la causa raíz del error de cuantización, y es aún más difícil solucionar el error de cuantización. Para ayudar a este proceso de inspección del modelo, se puede usar el depurador de cuantización para identificar las capas problemáticas, y la cuantización selectiva puede dejar esas capas problemáticas en flotación para que se pueda recuperar la precisión del modelo a costa de reducir los beneficios de la cuantización.

Nota: Esta API es experimental, y es posible que se produzcan cambios de última hora en la misma en el curso de las mejoras.

Depurador de cuantización

El depurador de cuantización permite realizar análisis de métricas de calidad de la cuantización en el modelo existente. El depurador de cuantización puede automatizar los procesos para ejecutar el modelo con un conjunto de datos de depuración y recopilar métricas de calidad de la cuantización para cada uno de los tensores.

Nota: El depurador de cuantización y la cuantización selectiva sólo funcionan actualmente para la cuantización entera completa con activaciones int8.

Requisitos previos

Si ya dispone de una canalización para cuantizar un modelo, ¡ya tiene todas las piezas necesarias para ejecutar el depurador de cuantización!

  • Modelo a cuantizar

  • Conjunto de datos representativo

Además del modelo y los datos, tendrá que usar un marco de procesamiento de datos (por ejemplo, pandas, Google Sheets) para analizar los resultados exportados.

Preparación

En esta sección se preparan las bibliotecas, el modelo MobileNet v3 y el conjunto de datos de prueba de 100 imágenes.

# 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 el modelo original tiene una precisión muy superior al top-5 para nuestro pequeño conjunto de datos, mientras que el modelo cuantizado tiene una pérdida de precisión significativa.

Paso 1. Preparación del depurador

La forma más fácil de usar el depurador de cuantización es proporcionar tf.lite.TFLiteConverter que ha estado usando para cuantizar el 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))

Paso 2. Ejecutar el depurador y obtener los resultados

Cuando llame a QuantizationDebugger.run(), el depurador registrará las diferencias entre los tensores flotantes y los tensores cuantizados para la misma ubicación op, y los procesará con las métricas dadas.

debugger.run()

Se puede acceder a las métricas procesadas con QuantizationDebugger.layer_statistics, o se pueden vaciar a un archivo de texto en formato CSV con 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 fila del vaciado, el nombre y el índice de la op van primero, seguidos de los parámetros de cuantización y las métricas de error (incluyendo métricas de error definidas por el usuario, si las hay). El archivo CSV resultante puede usarse para elegir capas problemáticas con grandes métricas de error de cuantización.

Con pandas u otras bibliotecas de procesamiento de datos, podemos inspeccionar métricas de error detalladas por capa.

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

Paso 3. Análisis de los datos

Hay varias formas de analizar el resultado. Primero, añadamos algunas métricas útiles derivadas de las salidas del depurador (scale significa el factor de escala de cuantización para cada tensor).

  • Rango (256 / scale)

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

El RMSE / scale es cercano a 1 / sqrt(12) (~ 0.289) cuando la distribución cuantizada es similar a la distribución flotante original, lo que indica un buen modelo cuantizado. Cuanto mayor sea el valor, es más probable que la capa no esté bien cuantizada.

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

Hay muchas capas con rangos amplios, y algunas capas que tienen valores RMSE/scale altos. Entremos en las capas con métricas de error altas.

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

Con estas capas, puede probar la cuantización selectiva para ver si la no cuantización de esas capas mejora la calidad del modelo.

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

Además, omitir la cuantización de las primeras capas también ayuda a mejorar la calidad del modelo cuantizado.

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

Cuantización selectiva

La cuantización selectiva omite la cuantización de algunos nodos, de modo que el cálculo pueda realizarse en el dominio original de punto flotante. Cuando se omiten capas correctas, podemos esperar cierta recuperación de la calidad del modelo a costa de un aumento de la latencia y del tamaño del modelo.

Sin embargo, si planea ejecutar modelos cuantizados en aceleradores de sólo enteros (por ejemplo, Hexagon DSP, EdgeTPU), la cuantización selectiva causaría la fragmentación del modelo y resultaría en una latencia de inferencia más lenta causada principalmente por el coste de transferencia de datos entre la CPU y esos aceleradores. Para evitarlo, puede considerar la posibilidad de ejecutar entrenamiento consciente de la cuantización para mantener todas las capas en enteros y preservar al mismo tiempo la precisión del modelo.

La opción del depurador de cuantización acepta las opciones denylisted_nodes y denylisted_ops para omitir la cuantización de capas específicas, o todas las instancias de ops específicas. Usando suspected_layers que preparamos en el paso anterior, podemos usar el depurador de cuantización para obtener un modelo cuantizado selectivamente.

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)

La precisión sigue siendo inferior en comparación con el modelo flotante original, pero tenemos una mejora notable del modelo cuantizado completo al omitir la cuantización de ~10 capas de 111 capas.

También puede intentar no cuantizar todas las ops de la misma clase. Por ejemplo, para omitir la cuantización de todas las ops medias, puede pasar 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)

Con estas técnicas, conseguimos mejorar la precisión del modelo cuantizado de MobileNet V3. A continuación exploraremos técnicas avanzadas para mejorar aún más la precisión del modelo.

Usos avanzados

Con las siguientes funciones, puede personalizar aún más su canal de depuración.

Métricas personalizadas

De forma predeterminada, el depurador de cuantización emite cinco métricas para cada diferencia flotante-cuantizado: tamaño del tensor, desviación estándar, error medio, error absoluto máximo y error medio al cuadrado. Puede añadir más métricas personalizadas pasándolas a opciones. Para cada métrica, el resultado debe ser un único valor flotante y la métrica resultante será un promedio de las métricas de todos los ejemplos.

  • layer_debug_metrics: calcule la métrica basada en la diferencia para cada salida op de las salidas op flotantes y cuantificadas.

  • layer_direct_compare_metrics: en lugar de obtener sólo la diferencia, esto calculará la métrica basándose en los tensores en bruto flotantes y cuantizados, y en sus parámetros de cuantización (escala, punto cero)

  • model_debug_metrics: sólo se usa cuando float_model_(path|content) se pasa al depurador. Además de las métricas de nivel op, la salida final de la capa se compara con la salida de referencia del 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()

El resultado de model_debug_metrics puede verse por separado de debugger.model_statistics.

debugger.model_statistics

Usar la API (interna) mlir_quantize para acceder a funciones en profundidad

Nota: Algunas funciones de la sección siguiente, TFLiteConverter._experimental_calibrate_only y converter.mlir_quantize son APIs internas experimentales y están sujetas a cambios que no permiten la retrocompatibilidad.

from tensorflow.lite.python import convert

Modo de verificación de todo el modelo

El comportamiento predeterminado para la generación del modelo de depuración es la verificación por capas. En este modo, la entrada para el par de ops flotantes y cuantizadas procede de la misma fuente (op cuantizada anterior). Otro modo es la verificación de todo el modelo, en la que los modelos flotante y cuantizado están separados. Este modo sería útil para observar cómo se propaga el error por el modelo. Para activarlo, enable_whole_model_verify=True a convert.mlir_quantize al generar el modelo de depuración 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))

Cuantización selectiva a partir de un modelo ya calibrado

Puede llamar directamente a convert.mlir_quantize para obtener el modelo cuantizado selectivo a partir del modelo ya calibrado. Esto sería especialmente útil cuando desee calibrar el modelo una vez y experimentar con varias combinaciones de denylist.

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