Sprawdzanie błędów kwantyzacji za pomocą debugera kwantyzacji

Zobacz na TensorFlow.org Uruchom w Google Colab Wyświetl źródło na GitHub Pobierz notatnik Zobacz model piasty TF

Chociaż kwantyzacja oparta na pełnej liczbie całkowitej zapewnia lepszy rozmiar i opóźnienie modelu, model kwantowany nie zawsze będzie działał zgodnie z oczekiwaniami. Zwykle oczekuje się, że jakość modelu (np. dokładność, mAP, WER) będzie nieco niższa niż w oryginalnym modelu pływającym. Istnieją jednak przypadki, w których jakość modelu może spaść poniżej Twoich oczekiwań lub wygenerować całkowicie błędne wyniki.

Kiedy pojawia się ten problem, trudno jest wykryć pierwotną przyczynę błędu kwantyzacji, a naprawienie błędu kwantyzacji jest jeszcze trudniejsze. Aby ułatwić ten proces kontrolny modelu, debugera kwantyzacji mogą być stosowane do identyfikacji problematyczne warstw i wybiórcze kwantyzacji może pozostawić te warstwy w problematycznych pływaka, tak że dokładność model może być odzyskany kosztem zmniejszenia korzyści z kwantyzacji.

Debuger kwantyzacji

Debuger kwantyzacji umożliwia wykonanie analizy metryki jakości kwantyzacji w istniejącym modelu. Debuger kwantyzacji może zautomatyzować procesy uruchamiania modelu za pomocą zestawu danych debugowania i zbierać metryki jakości kwantyzacji dla każdego tensora.

Warunki wstępne

Jeśli masz już potok do kwantyzacji modelu, masz wszystkie niezbędne elementy do uruchomienia debugera kwantyzacji!

  • Model do kwantyzacji
  • Reprezentatywny zbiór danych

Oprócz modelu i danych, będziesz musiał użyć struktury przetwarzania danych (np. pandy, Arkusze Google) do analizy wyeksportowanych wyników.

Ustawiać

Ta sekcja przygotowuje biblioteki, model MobileNet v3 i testowy zestaw danych zawierający 100 obrazów.

# 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

Kotły i pomocniki

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%

Widzimy, że oryginalny model ma znacznie wyższą dokładność top-5 dla naszego małego zestawu danych, podczas gdy model skwantowany ma znaczną utratę dokładności.

Krok 1. Przygotowanie debugera

Najprostszym sposobem korzystania z debugera kwantyzacji jest zapewnienie tf.lite.TFLiteConverter które zostały z wykorzystaniem do kwantowania modelu.

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

Krok 2. Uruchomienie debugera i uzyskanie wyników

Podczas rozmowy telefonicznej QuantizationDebugger.run() , debugger będzie różnic między tensorów pływaka i skwantowanych tensorów w tym samym miejscu op dziennika oraz przetwarzać je z podanymi danymi.

debugger.run()

Przetworzone dane mogą być dostępne z QuantizationDebugger.layer_statistics , lub może być zrzucana do pliku tekstowego w formacie CSV z 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

Dla każdego wiersza w wysypisku nazwa OP, a wskaźnik jest na pierwszym miejscu, a następnie za pomocą parametrów kwantyzacji metryk błędów (w tym metryk błędów zdefiniowanych przez użytkownika , o ile w ogóle). Wynikowy plik CSV może być użyty do wybrania problematycznych warstw z dużymi metrykami błędów kwantyzacji.

Dzięki pandom lub innym bibliotekom przetwarzania danych możemy sprawdzać szczegółowe metryki błędów w poszczególnych warstwach.

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

Krok 3. Analiza danych

Wyniki można analizować na różne sposoby. Najpierw dodajmy kilka przydatnych metryk pochodzących z danych wyjściowych debugera. ( scale oznacza współczynnik skalowania kwantyzacji dla każdego tensora).

  • Zakres ( 256 / scale )
  • RMSE / skali ( sqrt(mean_squared_error) / scale )

RMSE / scale jest blisko 1 / sqrt(12) (~ 0,289), gdy skwantowane rozkład podobny do pierwotnego rozmieszczenia pływaka, co wskazuje na dobrą kwantyzacji model. Im większa wartość, tym bardziej prawdopodobne jest, że warstwa nie jest dobrze skwantowana.

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

Istnieje wiele warstw o szerokich zakresach i kilka warstw, które mają wysoki RMSE/scale wartości. Zdobądźmy warstwy z wysokimi metrykami błędów.

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

Dzięki tym warstwom możesz spróbować selektywnej kwantyzacji, aby sprawdzić, czy ich brak poprawia jakość modelu.

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

Oprócz tego pomijanie kwantyzacji dla kilku pierwszych warstw pomaga również poprawić jakość skwantowanego modelu.

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

Selektywna kwantyzacja

Kwantyzacja selektywna pomija kwantyzację niektórych węzłów, dzięki czemu obliczenia mogą odbywać się w oryginalnej domenie zmiennoprzecinkowej. Po pominięciu prawidłowych warstw możemy spodziewać się pewnego przywrócenia jakości modelu kosztem zwiększonego opóźnienia i rozmiaru modelu.

Jeśli jednak planujesz uruchamiać skwantowane modele na akceleratorach działających wyłącznie na liczbach całkowitych (np. Hexagon DSP, EdgeTPU), selektywna kwantyzacja spowodowałaby fragmentację modelu i skutkowałaby wolniejszym opóźnieniem wnioskowania, głównie ze względu na koszt transferu danych między procesorem a tymi akceleratorami . Aby temu zapobiec, można rozważyć uruchomienie kwantyzacji trening świadomego zachować wszystkie warstwy w całkowitej, przy jednoczesnym zachowaniu dokładności modelu.

Opcja kwantyzacji debugger akceptuje denylisted_nodes i denylisted_ops opcje pomijania kwantyzacji dla poszczególnych warstw lub wszystkich przypadkach określonych w OPS. Korzystanie suspected_layers mamy przygotowane z poprzedniego etapu, możemy użyć kwantyzacji debuggera aby uzyskać selektywnie kwantyzacji model.

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%

Dokładność jest nadal niższa w porównaniu z oryginalnym modelem pływającym, ale mamy zauważalną poprawę w stosunku do całego modelu skwantyzowanego, pomijając kwantyzację dla ~10 warstw z 111 warstw.

Możesz także spróbować nie kwantyzować wszystkich operacji w tej samej klasie. Na przykład, aby pominąć kwantyzacji dla wszystkich średnich ops, można przekazać MEAN do 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%

Dzięki tym technikom jesteśmy w stanie poprawić dokładność skwantowanego modelu MobileNet V3. Następnie zbadamy zaawansowane techniki, aby jeszcze bardziej poprawić dokładność modelu.

Zaawansowane zastosowania

Dzięki następującym funkcjom możesz dalej dostosować swój potok debugowania.

Dane niestandardowe

Domyślnie debuger kwantyzacji emituje pięć metryk dla każdej różnicy zmiennoprzecinkowa: rozmiar tensora, odchylenie standardowe, błąd średni, maksymalny błąd bezwzględny i błąd średniokwadratowy. Możesz dodać więcej niestandardowych danych, przekazując je do opcji. Dla każdej metryki wynik powinien być pojedynczą wartością zmiennoprzecinkową, a wynikowa metryka będzie średnią metryk ze wszystkich przykładów.

  • layer_debug_metrics : obliczenia na podstawie metryki diff dla każdego wyjścia op z pływakiem i skwantowanych wyjść OP.
  • layer_direct_compare_metrics : zamiast tylko coraz diff, będzie to obliczyć na podstawie metryki surowego pływaka i skwantowanych tensorów i jego parametrów kwantyzacji (skala, punkt zerowy)
  • model_debug_metrics : stosowany tylko wtedy, gdy float_model_(path|content) jest przekazywany do debuggera. Oprócz metryk na poziomie operacji wyniki końcowej warstwy są porównywane z danymi wyjściowymi z oryginalnego modelu zmiennoprzecinkowego.
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()

Wynikiem model_debug_metrics można oddzielnie wynika z debugger.model_statistics .

debugger.model_statistics
{'argmax_accuracy': 0.36}

Korzystanie z (wewnętrznego) mlir_quantize API w celu uzyskania dostępu do szczegółowych funkcji

from tensorflow.lite.python import convert

Tryb weryfikacji całego modelu

Domyślnym zachowaniem generowania modelu debugowania jest weryfikacja według warstwy. W tym trybie dane wejściowe dla pary operacji float i quantize pochodzą z tego samego źródła (poprzednia operacja skwantowana). Innym trybem jest weryfikacja całego modelu, w której modele zmiennoprzecinkowe i kwantyzacyjne są rozdzielone. Ten tryb byłby przydatny do obserwowania, w jaki sposób błąd rozprzestrzenia się w modelu. Aby włączyć, enable_whole_model_verify=True do convert.mlir_quantize podczas generowania modelu debugowania ręcznie.

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

Selektywna kwantyzacja z już skalibrowanego modelu

Można bezpośrednio zadzwonić convert.mlir_quantize uzyskać selektywną quantized model z już skalibrowanym modelu. Byłoby to szczególnie przydatne, gdy chcesz raz skalibrować model i poeksperymentować z różnymi kombinacjami list odrzuconych.

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%