TensorFlow Lite 8-bit niceleme özelliği

Aşağıdaki belge, TensorFlow Lite'ın 8 bitlik niceleme şemasının özelliklerini özetlemektedir. Bu, donanım geliştiricilerine nicelleştirilmiş TensorFlow Lite modelleriyle çıkarım için donanım desteği sağlamada yardımcı olmayı amaçlamaktadır.

Şartname özeti

Bir şartname sağlıyoruz ve sadece spesifikasyona uyulursa davranışla ilgili bazı garantiler sağlayabiliriz. Ayrıca, farklı donanımların, spesifikasyonu uygularken bit-tam olmayan uygulamalarla sonuçlanan küçük sapmalara neden olabilecek tercihleri ​​ve kısıtlamaları olabileceğini de anlıyoruz. Çoğu durumda bu kabul edilebilir olsa da (ve birkaç modelden topladığımız işlem başına toleransları da bildiğimiz kadarıyla en iyi şekilde içeren bir test paketi sunacağız) durum) herhangi bir kesin teminat vermeyi imkansız kılar.

8 bitlik nicemleme, aşağıdaki formülü kullanarak kayan nokta değerlerine yaklaşır.

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

Eksen başına (Dönüşüm işlemlerinde kanal başına aka) veya tensör başına ağırlıklar int8 [-127, 127] aralığında sıfır noktası 0'a eşit tümleyici değerleriyle temsil edilir. int8 aralığında ikinin tümleyici değerleri [-128, 127] aralığı içinde bir sıfır noktası, [-128, 127] .

Aşağıda belgelenen belirli işlemler için başka istisnalar da vardır.

İşaretli tamsayı vs işaretsiz tamsayı

TensorFlow Lite niceleme, 8-bit için int8 nicemlemesi için öncelikle int8 ve çekirdeklere öncelik verecektir. Bu, 0'a eşit sıfır noktası ile temsil edilen simetrik nicemlemenin rahatlığı içindir. Ayrıca birçok arka uç, int8xint8 birikimi için ek optimizasyonlara sahiptir.

Eksen başına ve tensör başına

Tensör başına niceleme, tüm tensör başına bir ölçek ve / veya sıfır noktası olacağı anlamına gelir. Eksen başına quantized_dimension , quantized_dimension zero_point dilim başına bir ölçek ve / veya zero_point olacağı anlamına gelir. Nicelenmiş boyut, ölçeklerin ve sıfır noktalarının karşılık geldiği Tensor şeklinin boyutunu belirtir. Örneğin, kuantizasyon parametreli zero_point=[1, 2, 3] dims=[4, 3, 2, 1] olan bir tensör t : scale=[1.0, 2.0, 3.0] , zero_point=[1, 2, 3] , quantization_dimension=1 , t ikinci boyutu:

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

Çoğu zaman, quantized_dimension olan output_channel konvolüsyonlar ağırlıklarının fakat teorik olarak çekirdek uygulanmasında her nokta ürüne karşılık gelir, performans üzerindeki etkileri olmadan daha fazla niceleme ayrıntı sağlayan bu boyut olabilir. Bu, doğruluk açısından büyük iyileştirmelere sahiptir.

TFLite, artan sayıda operasyon için eksen başına desteğe sahiptir. Bu belge sırasında, Conv2d ve DepthwiseConv2d için destek mevcuttur.

Simetrik ve asimetrik

Etkinleştirmeler asimetriktir: işaretli int8 aralığı [-128, 127] içinde herhangi bir yerde sıfır noktalarına sahip olabilirler. Çoğu aktivasyon, doğası gereği asimetriktir ve sıfır noktası, ekstra ikili hassasiyet bitine etkili bir şekilde ulaşmak için nispeten ucuz bir yoldur. Etkinleştirmeler yalnızca sabit ağırlıklarla çarpıldığından, sabit sıfır noktası değeri oldukça yoğun bir şekilde optimize edilebilir.

Ağırlıklar simetriktir: sıfır noktası 0'a eşit olmaya zorlanır. Ağırlık değerleri dinamik giriş ve aktivasyon değerleriyle çarpılır. Bu, ağırlığın sıfır noktasını aktivasyon değeriyle çarpmanın kaçınılmaz bir çalışma süresi maliyeti olduğu anlamına gelir. Sıfır noktasının 0 olmasını zorunlu kılarak bu maliyeti önleyebiliriz.

Matematiğin açıklaması: Bu, ölçek değerlerinin eksen başına olmasına izin verdiğimiz fark dışında, arXiv: 1712.05877'deki bölüm 2.3'e benzer. Bu, aşağıdaki gibi kolayca genelleşir:

$ A $, nicelleştirilmiş etkinleştirmelerden oluşan bir $ m \ times n $ matrisidir.
$ B $, nicelleştirilmiş ağırlıkların bir $ n \ times p $ matrisidir.
$ A $, $ a_j $’ın $ j $ inci satırını, her ikisi de $ n $ uzunluğunda olan $ B $, $ b_k $’ın $ k $ inci sütunuyla çarpmayı düşünün. Nicelleştirilmiş tam sayı değerleri ve sıfır nokta değerleri sırasıyla $ q_a $, $ z_a $ ve $ q_b $, $ z_b $ şeklindedir.

$$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)}\) terimi, girdi değerinin ve ağırlık değerinin iç çarpımını gerçekleştirdiği için kaçınılmazdır.

$$\sum_{i=0}^{n} q_{b}^{(i)} z_a$$

ve

$$\sum_{i=0}^{n} z_a z_b$$

terimler, çıkarım çağrısı başına aynı kalan sabitlerden oluşur ve bu nedenle önceden hesaplanabilir.

Aktivasyon her çıkarımı değiştirdiğinden \(\sum_{i=0}^{n} q_{a}^{(i)} z_b\) teriminin her çıkarımda hesaplanması gerekir. Ağırlıkları simetrik olmaya zorlayarak bu terimin maliyetini ortadan kaldırabiliriz.

int8 nicelleştirilmiş operatör özellikleri

Aşağıda, int8 tflite çekirdeklerimiz için niceleme gereksinimlerini açıklıyoruz:

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-tensor
    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