Aşağıdaki belgede, LiteRT'nin 8 bitlik nicemleme şemasıyla ilgili spesifikasyon özetlenmektedir. Bu, donanım geliştiricilerin, nicelenmiş LiteRT modelleriyle çıkarım için donanım desteği sağlamasına yardımcı olmayı amaçlamaktadır.
Spesifikasyon özeti
Bir spesifikasyon sunuyoruz ve yalnızca spesifikasyona uyulması durumunda davranışla ilgili bazı garantiler verebiliriz. Ayrıca, farklı donanımların, spesifikasyon uygulanırken küçük sapmalara neden olabilecek ve bit olarak tam eşleşmeyen uygulamalarla sonuçlanabilecek tercihlerinin ve kısıtlamalarının olabileceğini de anlıyoruz. Bu durum çoğu durumda kabul edilebilir olsa da (ve elimizden geldiğince, çeşitli modellerden topladığımız işlem başına toleransları içeren bir dizi test sunacağız) makine öğreniminin (ve en yaygın durumda derin öğrenmenin) doğası, kesin garantiler vermeyi imkansız kılar.
8 bit nicemleme, kayan nokta değerlerini aşağıdaki formülü kullanarak yaklaşık olarak hesaplar.
\[real\_value = (int8\_value - zero\_point) \times scale\]
Eksen başına (Conv işlemlerinde kanal başına olarak da bilinir) veya tensör başına ağırlıklar, sıfır noktası 0'a eşit olan int8 aralığındaki [-127, 127] ikiye tamamlama değerleriyle gösterilir. Tensor başına etkinleştirmeler/girişler, int8 aralığında [-128, 127] değerleriyle gösterilir. Sıfır noktası [-128, 127] aralığındadır.
Aşağıda belgelenen belirli işlemler için başka istisnalar da vardır.
İşaretli tamsayı ve işaretsiz tamsayı
LiteRT nicemleme, öncelikle int8
8 bit için nicemleme ile ilgili araçlara ve çekirdeklere öncelik verir. Bu, simetrik nicemlemenin 0'a eşit sıfır noktasıyla gösterilmesinin kolaylığı içindir. Ayrıca, birçok arka uçta int8xint8 birikimi için ek optimizasyonlar bulunur.
Eksen başına ve tensör başına
Tensör başına nicemleme, tensörün tamamı için tek bir ölçek ve/veya sıfır noktası olacağı anlamına gelir. Eksen başına nicemleme, quantized_dimension içinde dilim başına bir ölçek ve/veya zero_point olacağı anlamına gelir. Kuantize edilmiş boyut, ölçeklerin ve sıfır noktalarının karşılık geldiği Tensor şeklinin boyutunu belirtir. Örneğin, t tensörü, dims=[4, 3, 2, 1] ile birlikte nicemleme parametreleri: scale=[1.0, 2.0, 3.0], zero_point=[1, 2, 3], quantization_dimension=1 ile t'nın ikinci boyutunda nicemlenir:
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
Genellikle quantized_dimension, output_channel ağırlıklarının quantized_dimension'ıdır ancak teoride çekirdek uygulamasındaki her nokta çarpımına karşılık gelen boyut olabilir ve performans etkileri olmadan daha fazla nicemleme ayrıntısı sağlar. Bu, doğrulukta büyük iyileşmeler sağlar.
TFLite, artan sayıda işlem için eksen başına destek sunar. Bu belgenin hazırlandığı sırada Conv2d ve DepthwiseConv2d destekleniyordu.
Simetrik ve asimetrik
Etkinleştirmeler asimetriktir: Sıfır noktaları, işaretli int8 aralığın [-128, 127] herhangi bir yerinde olabilir. Birçok etkinleştirme doğası gereği asimetriktir ve sıfır noktası, nispeten ucuz bir şekilde etkili bir şekilde ek bir ikili bit hassasiyeti elde etmenin bir yoludur. Etkinleştirmeler yalnızca sabit ağırlıklarla çarpıldığından sabit sıfır noktası değeri oldukça fazla optimize edilebilir.
Ağırlıklar simetriktir: Sıfır noktası 0'a eşit olmalıdır. Ağırlık değerleri, dinamik giriş ve etkinleştirme değerleriyle çarpılır. Bu, ağırlığın sıfır noktasını etkinleştirme değeriyle çarpmanın kaçınılmaz bir çalışma zamanı maliyeti olduğu anlamına gelir. Sıfır noktasının 0 olmasını zorunlu kılarak bu maliyeti önleyebiliriz.
Matematiksel açıklama: Bu, arXiv:1712.05877'deki 2.3 bölümüne benzer. Tek fark, ölçek değerlerinin eksen başına olmasına izin vermemizdir. Bu, aşağıdaki gibi kolayca genellenebilir:
$A$, nicelenmiş etkinleştirmelerin $m \times n$ matrisidir.
$B$, nicelleştirilmiş ağırlıklardan oluşan bir $n \times p$ matrisidir.
Uzunluğu $n$olan $A$ matrisinin $j$ inci satırı $a_j$ile $B$ matrisinin $k$ıncı sütunu $b_k$ çarpıldığında elde edilen sonucu düşünün. Nicelenmiş tam sayı değerleri ve sıfır noktası değerleri sırasıyla $q_a$, $z_a$ ve $q_b$, $z_b$ olur.
\[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\]
Giriş değeri ile ağırlık değerinin nokta çarpımını yaptığından \(\sum_{i=0}^{n} q_{a}^{(i)} q_{b}^{(i)}\) terimi kaçınılmazdır.
\(\sum_{i=0}^{n} q_{b}^{(i)} z_a\) ve \(\sum_{i=0}^{n} z_a z_b\) terimleri, çıkarım çağrısı başına aynı kalan sabitlerden oluşur ve bu nedenle önceden hesaplanabilir.
Etkinleştirme her çıkarımda değiştiğinden \(\sum_{i=0}^{n} q_{a}^{(i)} z_b\) teriminin her çıkarımda hesaplanması gerekir. Ağırlıkların simetrik olmasını zorunlu kılarak bu terimin maliyetini ortadan kaldırabiliriz.
int8 nicelendirilmiş operatör spesifikasyonları
Aşağıda, int8 tflite çekirdeklerimiz için nicemleme koşulları açıklanmaktadır:
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