Specyfikacja kwantyzacji 8-bitowej LiteRT

Poniżej znajdziesz specyfikację 8-bitowych rozwiązań LiteRT. kwantyzacji. Ma na celu pomóc programistom sprzętu w tworzeniu obsługa sprzętu do wnioskowania za pomocą kwantyzowanych modeli LiteRT.

Podsumowanie specyfikacji

Podajemy specyfikację i możemy udzielić jedynie niektórych gwarancji dotyczących w przypadku zachowania zgodności ze specyfikacją. Zdajemy sobie też sprawę, że różne urządzenia mogą mają preferencje i ograniczenia, które mogą powodować niewielkie odchylenia podczas implementując specyfikację, co spowoduje, że implementacje będą nieco niedokładne. Chociaż w większości przypadków może to być akceptowalne (udostępnimy zestaw które według naszej wiedzy obejmują tolerancje dla poszczególnych operacji zebrane z kilku modeli), charakter uczenia maszynowego (oraz deep learning) w najczęstszym przypadku) uniemożliwia podanie żadnych sztywnych gwarancji.

8-bitowa kwantyzacja przybliża wartości zmiennoprzecinkowe za pomocą tego .

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

Na oś (czyli na kanał w operacjach konw.) lub wagi na segmentację przedstawia się przez int8 zwraca wartości 2 dopełniające się wartości z zakresu [-127, 127] o równym punkcie 0 do 0. Aktywacje/dane wejściowe według tendencji są reprezentowane przez dopełnienie dwóch funkcji int8 wartości z zakresu [-128, 127], z zerowym punktem w zakresie [-128, 127].

Istnieją też inne wyjątki dotyczące konkretnych operacji, które zostały opisane poniżej.

Liczba całkowita ze znakiem lub nieoznaczona

W przypadku kwantyzacji LiteRT priorytetowe są głównie narzędzia i jądra Kwantyzacja int8 dla 8-bitowych danych. Dla wygody użytkowników symetrycznych wartość kwantyzacji jest reprezentowana przez punkt zerowy równy 0. Dodatkowo wiele w backendach są dodatkowe optymalizacje do agregacji int8xint8.

Na oś a na Tensor

Kwantyzacja na stężenie intensywności oznacza, że w każdym przypadku cały tensor. Kwantyzacja według osi oznacza, że istnieje 1 skala lub zero_point za wycinek w: quantized_dimension. Wymiar kwantyzowany określa wymiar kształtu Tensor, który skaluje i punkty zerowe . Na przykład tensor t z dims=[4, 3, 2, 1] z parametry kwantyzacji: scale=[1.0, 2.0, 3.0], zero_point=[1, 2, 3], Wartość quantization_dimension=1 będzie poddawana kwantyzacji w ramach drugiego wymiaru t:

t[:, 0, :, :] will have scale[0]=1.0, zero_point[0]=1
t[:, 1, :, :] will have scale[1]=2.0, zero_point[1]=2
t[:, 2, :, :] will have scale[2]=3.0, zero_point[2]=3

Często quantized_dimension stanowi output_channel wag ale teoretycznie może to być wymiar odpowiadający poszczególnym iloczyn skalarny w implementacji jądra, co pozwala na większą szczegółowość kwantyzacji. bez wpływu na wydajność. To znacznie poprawia dokładność.

TFLite obsługuje coraz większą liczbę operacji na poszczególnych osiach. W momencie w tym dokumencie są obsługiwane Conv2d i DepthwiseConv2d.

Symetryczne a asymetryczne

Aktywacje są asymetryczne: mogą mieć punkt zerowy w dowolnym miejscu podpisany zakres int8 [-128, 127]. Wiele aktywacji ma swój charakter asymetryczny. to stosunkowo niedrogi sposób na zdobycie dodatkowych pieniędzy. binarnego bitu dokładności. Ponieważ aktywacje są mnożone tylko przez stałą wagi, stała wartość punktu zerowego może być dość optymalizowana.

Wagi są symetryczne: w punkcie zero punkt początkowy musi być równy 0. Wartości wagi: pomnożonego przez dynamiczne dane wejściowe i wartości aktywacji. Oznacza to, że istnieje nieuniknionego kosztu w czasie działania polegającego na pomnożeniu punktu zerowego wagi przez wartość aktywacji. Aby uniknąć kosztów, egzekwując wartość punktu zerowego równą 0.

Objaśnienie działania matematycznego: jest to podobne do sekcji 2.3 w arXiv:1712.05877, z wyjątkiem różnicy że pozwalamy, aby wartości skali mieściły się na jednej osi. Łatwo to uogólnić, następujące:

$A$ to macierz kwantyzowanych aktywacji o wartości $m \times n$.
$B$ to macierz $n \times p$ kwantyzowanych wag.
Rozważ pomnożenie $j$th wiersza z $A$, $a_j$ przez $k$th kolumnę $B$, $b_k$, oba o długości $n$. Poddane kwantyzacji wartości całkowite oraz wartości zero-punktów to odpowiednio $q_a$, $z_a$ oraz $q_b$ i $z_b$.

\[a_j \cdot b_k = \sum_{i=0}^{n} a_{j}^{(i)} b_{k}^{(i)} = \sum_{i=0}^{n} (q_{a}^{(i)} - z_a) (q_{b}^{(i)} - z_b) = \sum_{i=0}^{n} q_{a}^{(i)} q_{b}^{(i)} - \sum_{i=0}^{n} q_{a}^{(i)} z_b - \sum_{i=0}^{n} q_{b}^{(i)} z_a + \sum_{i=0}^{n} z_a z_b\]

Termin \(\sum_{i=0}^{n} q_{a}^{(i)} q_{b}^{(i)}\) jest nie do uniknięcia, ponieważ jest iloczynu skalarnego wartości wejściowej i wagi.

Warunki \(\sum_{i=0}^{n} q_{b}^{(i)} z_a\) i \(\sum_{i=0}^{n} z_a z_b\) dotyczące składa się ze stałych, które pozostają takie same w każdym wywołaniu wnioskowania, dzięki czemu mogą zostać wstępnie obliczony.

Hasło \(\sum_{i=0}^{n} q_{a}^{(i)} z_b\) należy obliczyć z każdym wnioskowaniem bo aktywacja zmienia każde wnioskowanie. Przez egzekwowanie wag symetrycznie możemy usunąć koszt tego hasła.

specyfikacja operatora kwantyzowanego typu int8

Poniżej opisujemy wymagania dotyczące kwantyzacji dla jąder systemu int8 tflite:

ADD
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Input 1:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor

AVERAGE_POOL_2D
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  restriction: Input and outputs must all have same scale/zero_point

CONCATENATION
  Input ...:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  restriction: Input and outputs must all have same scale/zero_point

CONV_2D
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Input 1 (Weight):
    data_type  : int8
    range      : [-127, 127]
    granularity: per-axis (dim = 0)
    restriction: zero_point = 0
  Input 2 (Bias):
    data_type  : int32
    range      : [int32_min, int32_max]
    granularity: per-axis
    restriction: (scale, zero_point) = (input0_scale * input1_scale[...], 0)
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor

DEPTHWISE_CONV_2D
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Input 1 (Weight):
    data_type  : int8
    range      : [-127, 127]
    granularity: per-axis (dim = 3)
    restriction: zero_point = 0
  Input 2 (Bias):
    data_type  : int32
    range      : [int32_min, int32_max]
    granularity: per-axis
    restriction: (scale, zero_point) = (input0_scale * input1_scale[...], 0)
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor

FULLY_CONNECTED
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Input 1 (Weight):
    data_type  : int8
    range      : [-127, 127]
    granularity: per-axis (dim = 0)
    restriction: zero_point = 0
  Input 2 (Bias):
    data_type  : int32
    range      : [int32_min, int32_max]
    granularity: per-tensor
    restriction: (scale, zero_point) = (input0_scale * input1_scale[...], 0)
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor

L2_NORMALIZATION
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
    restriction: (scale, zero_point) = (1.0 / 128.0, 0)

LOGISTIC
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
    restriction: (scale, zero_point) = (1.0 / 256.0, -128)

MAX_POOL_2D
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  restriction: Input and outputs must all have same scale/zero_point

MUL
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Input 1:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor

RESHAPE
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  restriction: Input and outputs must all have same scale/zero_point

RESIZE_BILINEAR
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  restriction: Input and outputs must all have same scale/zero_point

SOFTMAX
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
    restriction: (scale, zero_point) = (1.0 / 256.0, -128)

SPACE_TO_DEPTH
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  restriction: Input and outputs must all have same scale/zero_point

TANH
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
    restriction: (scale, zero_point) = (1.0 / 128.0, 0)

PAD
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  restriction: Input and outputs must all have same scale/zero_point

GATHER
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  restriction: Input and outputs must all have same scale/zero_point

BATCH_TO_SPACE_ND
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  restriction: Input and outputs must all have same scale/zero_point

SPACE_TO_BATCH_ND
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  restriction: Input and outputs must all have same scale/zero_point

TRANSPOSE
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  restriction: Input and outputs must all have same scale/zero_point

MEAN
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor

SUB
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Input 1:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor

SUM
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor

SQUEEZE
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  restriction: Input and outputs must all have same scale/zero_point

LOG_SOFTMAX
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
    restriction: (scale, zero_point) = (16.0 / 256.0, 127)

MAXIMUM
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  restriction: Input and outputs must all have same scale/zero_point

ARG_MAX
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor

MINIMUM
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  restriction: Input and outputs must all have same scale/zero_point

LESS
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Input 1:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor

PADV2
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  restriction: Input and outputs must all have same scale/zero_point

GREATER
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Input 1:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor

GREATER_EQUAL
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Input 1:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor

LESS_EQUAL
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Input 1:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor

SLICE
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  restriction: Input and outputs must all have same scale/zero_point

EQUAL
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Input 1:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor

NOT_EQUAL
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Input 1:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor

SHAPE
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor

QUANTIZE (Requantization)
  Input 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor
  Output 0:
    data_type  : int8
    range      : [-128, 127]
    granularity: per-tensor

Pliki referencyjne

arXiv:1712.05877