다음 문서에서는 LiteRT의 8비트 양자화 스키마입니다. 이는 하드웨어 개발자가 양자화된 LiteRT 모델을 사용한 추론을 위한 하드웨어 지원
사양 요약
Google은 사양을 제공하고 있으며 제품 또는 서비스에 대해 일부 보증만 제공할 수 있습니다. 동작의 영향을 받습니다. 또한 다른 하드웨어로 인해 특정 광고나 판매 의도가 있는 광고와의 비트가 정확하지 않은 구현을 초래하는 사양을 구현합니다. 그러나 대부분의 경우 허용될 수 있지만 테스트에는 우리가 알고 있는 한 머신러닝의 특성, 딥 러닝의 특성, 절대적인 보장을 제공하는 것이 불가능합니다.
8비트 양자화는 다음을 사용하여 부동 소수점 값을 근사화합니다. 공식을 사용할 수 있습니다.
\[real\_value = (int8\_value - zero\_point) \times scale\]
축당 (전환 작업에서는 채널별) 또는 텐서당 가중치는 다음과 같이 표현됩니다.
int8
[-127, 127]
범위에서 2의 보수 값(0점 같음)
0으로 설정합니다. 텐서별 활성화/입력은 int8
2의 보수로 표현
[-128, 127]
범위의 값([-128, 127]
범위의 0점)
특정 작업에 대한 다른 예외는 아래에 설명되어 있습니다.
부호 있는 정수와 부호 없는 정수 비교
LiteRT 양자화는 주로 이러한 하드웨어를 대상으로 하는 도구와 커널에
8비트에 대한 int8
양자화 이는 대칭 이동의 편의를 위한 것입니다.
0과 같은 0점으로 표현되는 양자화입니다. 또한 많은
백엔드에는 int8xint8
축적을 위한 추가 최적화가 있습니다.
축 및 텐서당 비교
텐서별 양자화는 각 특성마다 하나의 척도 또는 제로포인트가
전체 텐서에 해당합니다. 축당 양자화는 하나의 스케일 및/또는
quantized_dimension
의 슬라이스당 zero_point
입니다. 양자화 차원은
는 텐서의 배율 및 0점인 텐서 도형의 차원을 지정합니다.
나타냅니다. 예를 들어 dims=[4, 3, 2, 1]
가 있는 t
텐서
양자화 매개변수: scale=[1.0, 2.0, 3.0]
, zero_point=[1, 2, 3]
,
quantization_dimension=1
는 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
종종 quantized_dimension
는 다음 가중치의 output_channel
입니다.
컨볼루션이지만 이론적으로는 각 컨볼루션에 해당하는 차원이 될 수 있습니다.
커널 구현의 dot-product를 사용하여 더 많은 양자화 세분성을 허용
성능을 향상할 수 있습니다 이렇게 하면 정확성이 크게 향상됩니다.
TFLite는 증가하는 작업 수를 위한 축당 지원을 제공합니다. 이 문서에서는 Conv2d 및 DepthwayConv2d가 지원됩니다.
대칭 대 비대칭
활성화는 비대칭입니다. 활성화 지점은
부호 있는 int8
범위 [-128, 127]
. 많은 활성화는 본질적으로 비대칭이며
제로 포인트는 추가 값을 효과적으로 얻을 수 있는 상대적으로 저렴한 방법입니다.
정밀도가 매우 높습니다. 활성화 함수가 상수로만 곱하기 때문에
상수인 0점 값은 상당히 많이 최적화될 수 있습니다.
가중치는 대칭적입니다. 즉, 0이 0이 되도록 강제됩니다. 가중치 값은 곱하기만 하면 됩니다. 이것은 가중치의 0점을 활성화 값 0을 0으로 적용하면 이 비용을 피할 수 있습니다.
계산에 대한 설명: 이 내용은 arXiv:1712.05877 배율 값을 축당으로 설정할 수 있습니다. 이것은 쉽게 일반화됩니다. 다음과 같습니다.
$A$ 는 양자화된 활성화 함수의 $m \times n$ 행렬입니다.
$B$ 는 양자화된 가중치의 $n \times p$ 행렬입니다.
$A$의 $j$번째 행, $a_j$에
두 단어 모두 길이가 $n$인 $B$, $b_k$입니다. 양자화된 정수 값과
0포인트 값은 각각 $q_a$, $z_a$, $q_b$, $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\]
와 함께 실패합니다.\(\sum_{i=0}^{n} q_{a}^{(i)} q_{b}^{(i)}\) 용어는 내적을 구합니다.
\(\sum_{i=0}^{n} q_{b}^{(i)} z_a\) 및 \(\sum_{i=0}^{n} z_a z_b\) 약관 추론 호출마다 동일하게 유지되는 상수로 구성되므로 사전에 계산되어야 합니다.
추론할 때마다 \(\sum_{i=0}^{n} q_{a}^{(i)} z_b\) 항을 계산해야 함 활성화가 모든 추론을 변경하기 때문입니다. 가중치를 적용하여 대칭적으로 이 항의 비용을 제거할 수 있습니다.
int8 양자화 연산자 사양
아래에서는 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