TensorFlow Lite 8 bit niceleme özelliği

Aşağıdaki belge TensorFlow Lite'ın 8 bit niceleme şemasının özelliklerini özetlemektedir. Bunun amacı, donanım geliştiricilerinin nicelenmiş TensorFlow Lite modelleri ile çıkarım için donanım desteği sağlamalarına yardımcı olmaktır.

Şartname özeti

Bir spesifikasyon sağlıyoruz ve yalnızca spesifikasyona uyulması durumunda davranışa ilişkin bazı garantiler verebiliriz. Ayrıca, farklı donanımların, spesifikasyonu uygularken tam olarak bit olmayan uygulamalarla sonuçlanan hafif sapmalara neden olabilecek tercih ve kısıtlamalara sahip olabileceğini de anlıyoruz. Bu çoğu durumda kabul edilebilir olsa da (ve bilgimiz dahilinde çeşitli modellerden topladığımız işlem başına toleransları içeren bir dizi test sunacağız), makine öğreniminin doğası (ve en yaygın olarak derin öğrenme) durumda) herhangi bir kesin garanti verilmesini imkansız hale getirir.

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 (diğer bir deyişle Dönüşüm işlemlerinde kanal başına) veya tensör başına ağırlıklar, sıfır noktası 0'a eşit olacak şekilde int8 ikinin [-127, 127] aralığındaki tamamlayıcı değerleri ile temsil edilir. Tensör başına aktivasyonlar/girişler şu şekilde temsil edilir: int8 two'nun tamamlayıcı değerleri [-128, 127] aralığında, sıfır noktası ise [-128, 127] aralığındadır.

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

İşaretli tam sayı ve işaretsiz tam sayı

TensorFlow Lite nicelemesi öncelikle 8 bit için int8 nicelemesi için araçlara ve çekirdeklere öncelik verecektir. Bu, 0'a eşit sıfır noktasıyla temsil edilen simetrik nicelemenin rahatlığı 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, tüm tensör başına bir ölçek ve/veya sıfır noktası olacağı anlamına gelir. Eksen başına niceleme, quantized_dimension 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 Tensör şeklinin boyutunu belirtir. Örneğin, dims=[4, 3, 2, 1] niceleme parametrelerine sahip bir t tensörü: scale=[1.0, 2.0, 3.0] , zero_point=[1, 2, 3] , quantization_dimension=1 genelinde nicelenecektir. 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 evrişimlerin ağırlıklarının output_channel olur, ancak teoride, çekirdek uygulamasındaki her bir nokta çarpımına karşılık gelen boyut olabilir ve performans sonuçları olmadan daha fazla niceleme ayrıntı düzeyine izin verir. Bunun doğruluk açısından büyük iyileştirmeleri var.

TFLite, giderek artan sayıda operasyon için eksen başına desteğe sahiptir. Bu belgenin yayınlandığı tarihte Conv2d ve DepthwiseConv2d için destek mevcuttur.

Simetrik ve asimetrik

Aktivasyonlar asimetriktir: sıfır noktaları imzalı int8 aralığı [-128, 127] dahilinde herhangi bir yerde olabilir. Pek çok aktivasyon doğası gereği asimetriktir ve sıfır noktası, ekstra ikili hassasiyete etkili bir şekilde ulaşmanın nispeten ucuz bir yoludur. Aktivasyonlar 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ının 0'a eşit olması zorunludur. Ağırlık değerleri dinamik giriş ve aktivasyon değerleriyle çarpılır. Bu, ağırlığın sıfır noktasının aktivasyon değeriyle çarpılmasını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 vermemiz farkı dışında arXiv:1712.05877'deki bölüm 2.3'e benzer. Bu, aşağıdaki şekilde kolayca genelleştirilir:

\(A\) nicelenmiş aktivasyonların bir \(m \times n\) matrisidir.
\(B\) nicelenmiş ağırlıkların bir \(n \times p\) matrisidir.
\(j\)\(A\), \(a_j\) l10n-yer tutucu6'ncı satırını \(k\)yer tutucu10, \(b_k\)\(B\)l10n-yer tutucu9'uncu sütunu ile çarpmayı düşünün; her ikisi de \(n\)uzunluğundadır. Nicelenmiş tam sayı değerleri ve sıfır noktası değerleri sırasıyla \(q_a\), \(z_a\) ve \(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)}\) terimi, giriş değeri ile ağırlık değerinin nokta çarpımını gerçekleştirdiğinden dolayı kaçınılmazdır.

\(\sum_{i=0}^{n} q_{b}^{(i)} z_a\) ve \(\sum_{i=0}^{n} z_a z_b\) terimleri, her çıkarım çağrısında aynı kalan sabitlerden oluşur ve bu nedenle önceden hesaplanabilir.

Etkinleştirme 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ın simetrik olmasını sağlayarak bu terimin maliyetini ortadan kaldırabiliriz.

int8 nicelenmiş 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-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