LiteRT 8 bit miktar belirleme spesifikasyonu

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

Referanslar

arXiv:1712.05877