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