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
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()
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()
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, gdyfloat_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%