Kwantyzacja po trenowaniu

Kwantyzacja po trenowaniu to technika konwersji, która może zmniejszyć rozmiar modelu, a jednocześnie poprawić czas oczekiwania procesora i akceleratora sprzętowego przy niewielkim pogorszeniu dokładności modelu. Wytrenowany już wytrenowany model zmiennoprzecinkowy możesz poddać kwantyzacji, gdy konwertujesz go na format TensorFlow Lite za pomocą konwertera TensorFlow Lite.

Metody optymalizacji

Masz do wyboru kilka opcji kwantyzacji po zakończeniu trenowania. Oto tabela podsumowania dostępnych opcji i korzyści, jakie zapewniają:

Metoda Zalety Urządzenie
Kwantyzacja zakresu dynamicznego 4 razy mniejsze, 2–3 razy szybsze przyspieszenie CPU
Pełna kwantyzacja 4 razy mniejsze, ponad 3-krotne przyspieszenie CPU, Edge TPU, mikrokontrolery
Kwantyzacja obiektu Float16 2 razy mniejsze przyspieszenie GPU CPU, GPU

To drzewo decyzyjne pomoże Ci określić, która metoda kwantyzacji po trenowaniu jest najlepsza w Twoim przypadku:

opcje optymalizacji po szkoleniu

Brak kwantyzacji

Zalecanym punktem wyjścia jest przekonwertowanie na model TFLite bez kwantyzacji. Spowoduje to wygenerowanie pływającego modelu TFLite.

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
tflite_quant_model = converter.convert()

Zalecamy wykonanie tego wstępu, aby sprawdzić, czy operatory oryginalnego modelu TF są zgodne z TFLite. Może też zostać wykorzystane jako punkt odniesienia do debugowania błędów kwantyzacji wprowadzonych w kolejnych metodach kwantyzacji po trenowaniu. Jeśli na przykład poddany ilościowym modelom TFLite wyniki są nieoczekiwane, a zmiennoprzecinkowy model TFLite jest dokładny, możemy zawęzić problem do błędów spowodowanych kwantową wersją operatorów TFLite.

Kwantyzacja zakresu dynamicznego

Kwantyzacja zakresu dynamicznego zapewnia mniejsze wykorzystanie pamięci i szybsze obliczenia bez konieczności dostarczania reprezentatywnego zbioru danych do kalibracji. Ten typ kwantyzacji statycznie kwantyzuje tylko wagi ze zmiennoprzecinkowego na liczbę całkowitą w czasie konwersji, co zapewnia 8-bitową precyzję:

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_quant_model = converter.convert()

Aby jeszcze bardziej zmniejszyć czas oczekiwania podczas wnioskowania, operatory zakresu dynamicznego kwantyfikują aktywacje dynamicznie na podstawie ich zakresu do 8-bitów i wykonują obliczenia z użyciem 8-bitowych wag i aktywacji. Ta optymalizacja zapewnia opóźnienia, które są zbliżone do w pełni stałych punktów. Dane wyjściowe są jednak nadal przechowywane w postaci liczby zmiennoprzecinkowej, więc zwiększona szybkość operacji zakresu dynamicznego jest mniejsza niż pełne obliczenie stałego punktu.

Pełna kwantyzacja liczby całkowitej

Aby uzyskać dalsze skrócenie czasu oczekiwania, zmniejszenie szczytowego wykorzystania pamięci i zgodność ze sprzętem lub akceleratorami tylko dla liczb całkowitych, zadbaj o to, aby wszystkie wartości matematyczne modelu były poddane kwantyzacji.

Aby uzyskać pełną kwantyzację, musisz skalibrować lub oszacować zakres, np. (minimalna, maksymalna) wszystkich tensorów zmiennoprzecinkowych w modelu. W przeciwieństwie do stałych tensorów, takich jak wagi i odchylenia, tensory zmienne, np. dane wejściowe modelu, aktywacje (wyjścia warstw pośrednich) i dane wyjściowe modelu, nie mogą być kalibrowane, jeśli nie uruchomimy kilku cykli wnioskowania. Dlatego konwerter wymaga reprezentatywnego zbioru danych do kalibracji. Ten zbiór danych może być małym podzbiorem danych (około 100–500 próbek) danych treningowych lub walidacyjnych. Wykorzystuj do tego poniższą funkcję representative_dataset().

W TensorFlow 2.7 możesz określić reprezentatywny zbiór danych za pomocą podpisu, jak w tym przykładzie:

def representative_dataset():
  for data in dataset:
    yield {
      "image": data.image,
      "bias": data.bias,
    }

Jeśli w modelu TensorFlow jest więcej niż 1 podpis, możesz określić kilka zbiorów danych, podając klucze podpisu:

def representative_dataset():
  # Feed data set for the "encode" signature.
  for data in encode_signature_dataset:
    yield (
      "encode", {
        "image": data.image,
        "bias": data.bias,
      }
    )

  # Feed data set for the "decode" signature.
  for data in decode_signature_dataset:
    yield (
      "decode", {
        "image": data.image,
        "hint": data.hint,
      },
    )

Możesz wygenerować reprezentatywny zbiór danych, podając listę tensorów wejściowych:

def representative_dataset():
  for data in tf.data.Dataset.from_tensor_slices((images)).batch(1).take(100):
    yield [tf.dtypes.cast(data, tf.float32)]

Od wersji TensorFlow 2.7 zalecamy stosowanie podejścia opartego na podpisach zamiast metody opartej na liście tensorów wejściowych, ponieważ kolejność tensorów wejściowych można łatwo zmienić.

Do testowania możesz użyć przykładowego zbioru danych w ten sposób:

def representative_dataset():
    for _ in range(100):
      data = np.random.rand(1, 244, 244, 3)
      yield [data.astype(np.float32)]
 

Liczba całkowita z zastępczą wartością zmiennoprzecinkową (z użyciem domyślnego pływającego wejścia/wyjścia)

Aby w pełni poddać modelowi kwantyzację całkowitą, ale używać operatorów zmiennoprzecinkowych, gdy nie mają implementacji liczb całkowitych (co zapewni płynną konwersję), wykonaj te czynności:

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset
tflite_quant_model = converter.convert()

Tylko liczba całkowita

Tworzenie modeli zawierających tylko liczby całkowite jest typowym przypadkiem użycia TensorFlow Lite dla mikrokontrolerów i Coral Edge TPU.

Dodatkowo, aby zapewnić zgodność z urządzeniami obsługującymi wyłącznie liczby całkowite (np. 8-bitowymi mikrokontrolerami) i akceleratorami (takimi jak Coral Edge TPU), możesz wymusić pełną kwantyzację wszystkich operacji, w tym danych wejściowych i wyjściowych. W tym celu wykonaj te czynności:

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_dataset
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
converter.inference_input_type = tf.int8  # or tf.uint8
converter.inference_output_type = tf.int8  # or tf.uint8
tflite_quant_model = converter.convert()

Kwantyzacja obiektu Float16

Możesz zmniejszyć rozmiar modelu zmiennoprzecinkowego przez konwertowanie wagi na float16, czyli standard IEEE dla 16-bitowych liczb zmiennoprzecinkowych. Aby włączyć kwantyzację wag typu float16, wykonaj te czynności:

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_types = [tf.float16]
tflite_quant_model = converter.convert()

Zalety kwantyzacji float16 są następujące:

  • Zmniejsza on rozmiar modelu nawet o połowę (wszystkie wagi stają się połową pierwotnego rozmiaru).
  • Powoduje to minimalny spadek dokładności.
  • Obsługuje niektóre delegacje (np. delegację GPU), które mogą działać bezpośrednio na danych float16, dzięki czemu można wykonywać szybciej niż obliczenia float32.

Kwantyfikacja float16 ma takie wady:

  • Nie redukuje opóźnień w takim stopniu jak w przypadku kwantyzacji do obliczeń matematycznych ze stałą wartością.
  • Domyślnie kwantyzowany model float16 „dekwantyzuje” wartości wag do wartości float32 uruchamia się na CPU. (Pamiętaj, że dekwantyzacja nie wykona tej dekwantyzacji, ponieważ może działać na danych typu float16).

Tylko liczby całkowite: 16-bitowe aktywacje z wagą 8-bitową (eksperymentalna)

To jest eksperymentalny schemat kwantyzacji. Jest to podobne do schematu „tylko liczba całkowita”, z tą różnicą, że aktywacje są kwantyzowane na podstawie ich zakresu do 16-bitów. Wagi są kwantyzowane w postaci 8-bitowej liczby całkowitej, a odchylenie – do 64-bitowej liczby całkowitej. Nazywa się to kwantyzacją 16 × 8.

Główną zaletą tej kwantyzacji jest to, że może ona znacznie zwiększyć dokładność, ale tylko nieznacznie zwiększyć rozmiar modelu.

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.representative_dataset = representative_dataset
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_ops = [tf.lite.OpsSet.EXPERIMENTAL_TFLITE_BUILTINS_ACTIVATIONS_INT16_WEIGHTS_INT8]
tflite_quant_model = converter.convert()

Jeśli niektóre operatory w modelu nie obsługują kwantyzacji 16 × 8, model można poddać kwantyzacji, ale nieobsługiwane operatory są zachowywane jako liczba zmiennoprzecinkowa. Aby to umożliwić, do atrybutu target_spec należy dodać poniższą opcję.

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
converter.representative_dataset = representative_dataset
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_ops = [tf.lite.OpsSet.EXPERIMENTAL_TFLITE_BUILTINS_ACTIVATIONS_INT16_WEIGHTS_INT8,
tf.lite.OpsSet.TFLITE_BUILTINS]
tflite_quant_model = converter.convert()

Oto przykłady zastosowań, w których poprawa dokładności zapewniana przez ten schemat kwantyzacji:

  • super-resolution,
  • przetwarzania sygnałów dźwiękowych, np. redukcji szumów i kształtowania wiązki,
  • redukcji szumów obrazu,
  • Rekonstrukcja HDR z jednego zdjęcia.

Wadą tej kwantyzacji jest:

  • Obecnie wnioskowanie jest znacznie wolniejsze niż 8-bitowa pełna liczba całkowita ze względu na brak zoptymalizowanego jądra systemu.
  • Obecnie jest to niezgodne z obecnymi delegatami TFLite z akceleracją sprzętową.

Samouczek dotyczący tego trybu kwantyzacji znajdziesz tutaj.

Dokładność modelu

Wagi są kwantyzowane po zakończeniu trenowania, więc może wystąpić utrata dokładności, zwłaszcza w przypadku mniejszych sieci. Wstępnie wytrenowane, w pełni kwantowe modele są udostępniane dla określonych sieci w modelach Kaggle. Ważne jest sprawdzanie dokładności modelu poddanego kwantyzacji, aby upewnić się, że wszelkie pogorszenia dokładności mieszczą się w akceptowalnych granicach. Istnieją narzędzia do oceny dokładności modelu TensorFlow Lite.

Jeśli spadek dokładności jest zbyt duży, rozważ zastosowanie trenowania z uwzględnieniem kwantyzacji. Wymaga to jednak modyfikacji podczas trenowania modelu, aby dodać fałszywe węzły kwantyzacyjne, natomiast techniki kwantyzacji po trenowaniu na tej stronie wykorzystują już istniejący już wytrenowany model.

Reprezentacja tensorów kwantowych

8-bitowa kwantyzacja przybliża wartości zmiennoprzecinkowe za pomocą tej formuły.

\[real\_value = (int8\_value - zero\_point) \times scale\]

Reprezentacja składa się z 2 głównych części:

  • Wagi na oś (czyli na kanał) lub wagi na poziomie tendencji reprezentowane przez wartości uzupełniające w zakresie [-127; 127], gdzie punkt zerowy równy 0.

  • Aktywacje/dane wejściowe na tendencji reprezentowane przez wartości dopełnienia 2 int8 w zakresie [-128, 127], przy punkcie zerowym [-128, 127].

Szczegółowy widok naszego schematu kwantyzacji znajdziesz w naszej specyfikacji kwantyzacji. Zachęcamy dostawców sprzętu, którzy chcą podłączyć interfejs TensorFlow Lite do interfejsu delegowania TensorFlow Lite, do wdrożenia opisanego tam schematu kwantyzacji.