Kwantyzacja po treningu

Zadbaj o dobrą organizację dzięki kolekcji Zapisuj i kategoryzuj treści zgodnie ze swoimi preferencjami.

Kwantyzacja po treningu to technika konwersji, która może zmniejszyć rozmiar modelu, jednocześnie poprawiając opóźnienie procesora i akceleratora sprzętowego, przy niewielkim pogorszeniu dokładności modelu. Możesz skwantyzować już wytrenowany model float TensorFlow, konwertując go do formatu TensorFlow Lite za pomocą TensorFlow Lite Converter .

Metody optymalizacji

Do wyboru jest kilka opcji kwantyzacji po treningu. Oto tabela podsumowująca wybory i korzyści, jakie zapewniają:

Technika Korzyści Sprzęt komputerowy
Kwantyzacja zakresu dynamicznego 4x mniejsze, 2x-3x przyspieszenie procesor
Pełna kwantyzacja liczb całkowitych 4x mniejsze, 3x+ przyspieszenie Procesor, Edge TPU, mikrokontrolery
Kwantyzacja Float16 2x mniejszy, akceleracja GPU CPU, GPU

Poniższe drzewo decyzyjne może pomóc w ustaleniu, która metoda kwantyzacji po szkoleniu jest najlepsza dla danego przypadku użycia:

opcje optymalizacji po szkoleniu

Kwantyzacja zakresu dynamicznego

Kwantyzacja zakresu dynamicznego jest zalecanym punktem wyjścia, ponieważ zapewnia mniejsze zużycie pamięci i szybsze obliczenia bez konieczności dostarczania reprezentatywnego zestawu danych do kalibracji. Ten rodzaj kwantyzacji statycznie kwantyzuje tylko wagi od zmiennoprzecinkowych do całkowitych 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ć opóźnienie podczas wnioskowania, operatory „zakresu dynamicznego” dynamicznie kwantyzują aktywacje w oparciu o ich zakres do 8-bitów i wykonują obliczenia z 8-bitowymi wagami i aktywacjami. Ta optymalizacja zapewnia opóźnienia zbliżone do wnioskowań w pełni ustalonych. Jednak dane wyjściowe są nadal przechowywane w postaci zmiennoprzecinkowej, więc zwiększona prędkość operacji zakresu dynamicznego jest mniejsza niż pełne obliczenia stałoprzecinkowe.

Pełna kwantyzacja liczb całkowitych

Możesz uzyskać dalsze ulepszenia opóźnień, zmniejszenie szczytowego zużycia pamięci i kompatybilność z urządzeniami sprzętowymi lub akceleratorami wykorzystującymi tylko liczby całkowite, upewniając się, że wszystkie obliczenia matematyczne modelu są skwantowane.

Aby uzyskać pełną kwantyzację liczb całkowitych, należy skalibrować lub oszacować zakres, tj. (min., maks.) wszystkich tensorów zmiennoprzecinkowych w modelu. W przeciwieństwie do stałych tensorów, takich jak wagi i obciążenia, zmienne tensory, takie jak dane wejściowe modelu, aktywacje (wyjścia warstw pośrednich) i dane wyjściowe modelu, nie mogą być kalibrowane, chyba że przeprowadzimy kilka cykli wnioskowania. W rezultacie konwerter wymaga reprezentatywnego zestawu danych do ich kalibracji. Ten zbiór danych może być małym podzbiorem (około ~100-500 próbek) danych uczących lub walidacyjnych. Zapoznaj się z funkcją representative_dataset() poniżej.

Od wersji TensorFlow 2.7 możesz określić reprezentatywny zestaw danych za pomocą podpisu , jak w poniższym przykładzie:

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

Jeśli w danym modelu TensorFlow istnieje więcej niż jedna sygnatura, możesz określić wiele zestawów danych, określają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, dostarczają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 sygnaturach zamiast podejścia opartego na liście tensorów wejściowych, ponieważ kolejność tensorów wejściowych można łatwo odwrócić.

Do celów testowych możesz użyć fikcyjnego zbioru danych w następujący 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 rezerwą zmiennoprzecinkową (przy użyciu domyślnego wejścia/wyjścia zmiennoprzecinkowego)

Aby w pełni skwantyzować model, ale używaj operatorów zmiennoprzecinkowych, gdy nie mają implementacji liczb całkowitych (aby zapewnić płynną konwersję), wykonaj następujące kroki:

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 liczby całkowite

Tworzenie modeli wyłącznie z liczbami całkowitymi jest powszechnym przypadkiem użycia dla TensorFlow Lite dla mikrokontrolerów i TPU Coral Edge .

Dodatkowo, aby zapewnić kompatybilność z urządzeniami wyłącznie z liczbami całkowitymi (takimi jak 8-bitowe mikrokontrolery) i akceleratorami (takimi jak Coral Edge TPU), możesz wymusić pełną kwantyzację liczb całkowitych dla wszystkich operacji, w tym wejścia i wyjścia, wykonując następujące kroki:

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 Float16

Możesz zmniejszyć rozmiar modelu zmiennoprzecinkowego, kwantyzując wagi do float16, standardu IEEE dla 16-bitowych liczb zmiennoprzecinkowych. Aby włączyć kwantyzację wagi float16, wykonaj następujące kroki:

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 rozmiar modelu nawet o połowę (ponieważ wszystkie wagi stają się o połowę mniejsze).
  • Powoduje minimalną utratę dokładności.
  • Obsługuje niektóre delegaty (np. delegat GPU), które mogą działać bezpośrednio na danych float16, co skutkuje szybszym wykonaniem niż obliczenia float32.

Wady kwantyzacji float16 są następujące:

  • Nie zmniejsza latencji tak bardzo, jak kwantyzacja do matematyki stałoprzecinkowej.
  • Domyślnie, skwantyzowany model float16 „zdekwantyzuje” wartości wag do float32, gdy jest uruchamiany na CPU. (Zauważ, że delegat GPU nie wykona tej dekwantyzacji, ponieważ może operować na danych float16).

Tylko liczby całkowite: 16-bitowe aktywacje z 8-bitowymi wagami (eksperymentalne)

To jest eksperymentalny schemat kwantyzacji. Jest podobny do schematu „tylko liczby całkowite”, ale aktywacje są kwantowane na podstawie ich zakresu do 16-bitów, wagi są kwantowane do 8-bitowej liczby całkowitej, a odchylenie jest kwantowane do 64-bitowej liczby całkowitej. Nazywa się to dalej kwantyzacją 16x8.

Główną zaletą tej kwantyzacji jest to, że może znacznie poprawić 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 kwantyzacja 16x8 nie jest obsługiwana dla niektórych operatorów w modelu, model nadal może być skwantowany, ale nieobsługiwane operatory są utrzymywane w postaci zmiennoprzecinkowej. Aby to umożliwić, należy dodać następującą opcję do target_spec.

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

Przykłady przypadków użycia, w których ulepszenia dokładności zapewnione przez ten schemat kwantyzacji obejmują: * super rozdzielczość, * przetwarzanie sygnału audio, takie jak redukcja szumów i formowanie wiązki, * usuwanie szumów obrazu, * rekonstrukcja HDR z pojedynczego obrazu.

Wadą tej kwantyzacji jest:

  • Obecnie wnioskowanie jest zauważalnie wolniejsze niż 8-bitowa liczba całkowita ze względu na brak zoptymalizowanej implementacji jądra.
  • Obecnie jest niekompatybilny z istniejącymi delegatami TFLite z akceleracją sprzętową.

Samouczek dotyczący tego trybu kwantyzacji można znaleźć tutaj .

Dokładność modelu

Ponieważ wagi są skwantowane po uczeniu, może wystąpić utrata dokładności, szczególnie w przypadku mniejszych sieci. Wstępnie wytrenowane, w pełni skwantyzowane modele są dostarczane dla określonych sieci w TensorFlow Hub . Ważne jest, aby sprawdzić dokładność modelu skwantowanego, aby zweryfikować, czy jakiekolwiek pogorszenie dokładności mieści się w dopuszczalnych granicach. Istnieją narzędzia do oceny dokładności modelu TensorFlow Lite .

Alternatywnie, jeśli spadek dokładności jest zbyt duży, rozważ skorzystanie z treningu uwzględniającego kwantyzację . Jednak wykonanie tego wymaga modyfikacji podczas uczenia modelu, aby dodać fałszywe węzły kwantyzacji, podczas gdy techniki kwantyzacji po szkoleniu na tej stronie wykorzystują istniejący wstępnie wytrenowany model.

Reprezentacja skwantowanych tensorów

Kwantyzacja 8-bitowa aproksymuje wartości zmiennoprzecinkowe za pomocą następującego wzoru.

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

Przedstawienie składa się z dwóch głównych części:

  • Wagi na oś (czyli na kanał) lub na tensor reprezentowane przez wartości dopełnienia do dwóch int8 z zakresu [-127, 127] z punktem zerowym równym 0.

  • Aktywacje/wejścia na tensor reprezentowane przez wartości dopełnienia do dwóch int8 w zakresie [-128, 127], z punktem zerowym w zakresie [-128, 127].

Aby uzyskać szczegółowy widok naszego schematu kwantyzacji, zapoznaj się z naszą specyfikacją kwantyzacji . Producentów sprzętu, którzy chcą podłączyć się do interfejsu delegata TensorFlow Lite, zachęca się do wdrożenia opisanego tam schematu kwantyzacji.