TensorFlow Lite 8 bit niceleme spesifikasyonu

Aşağıdaki belgede, TensorFlow Lite'ın 8 bit niceleme şemasının spesifikasyonu özetlenmektedir. Bu, nicelleştirilmiş TensorFlow Lite modelleriyle çıkarım için donanım desteği sağlamada donanım geliştiricilerine yardımcı olmak amacıyla tasarlanmıştır.

Spesifikasyon özeti

Bir spesifikasyon sağlıyoruz ve yalnızca spesifikasyona uyulması durumunda davranışla ilgili bazı garantiler verebiliyoruz. Ayrıca, farklı donanımların, spesifikasyonları uygularken küçük sapmalara neden olabilecek tercihler ve kısıtlamalara sahip olabileceğini ve bunun sonucunda bit tam olmayan uygulamalara yol açabileceğini de biliyoruz. Bu çoğu durumda kabul edilebilir olsa da (bildiğimiz kadarıyla birkaç modelden edindiğimiz işlem başına toleransları da içeren bir dizi test sağlayacağız) makine öğreniminin (ve en yaygın durumda derin öğrenmenin) doğası, kesin garanti verilmesini imkansız hale getirir.

8 bit niceleme, aşağıdaki formülü kullanarak kayan nokta değerlerini yaklaşık olarak tahmin eder.

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

Eksen başına (Dönüşüm işlemlerinde kanal başına) veya tensör başına ağırlıklar, [-127, 127] aralığındaki sıfır noktası 0'a eşit olan int8 ikinin tamamlama değerleriyle temsil edilir. Tensör başına etkinleştirmeler/girişler, [-128, 127] aralığında sıfır nokta ile [-128, 127] aralığındaki int8 ikinin tamamlayıcı değerleri ile temsil edilir.

Belirli işlemlere ilişkin başka istisnalar da vardır. Bu istisnalar aşağıda açıklanmıştır.

İmzalı tam sayı ve imzalanmamış tam sayı

TensorFlow Lite nicelemesi, 8 bit için int8 nicelemesi için öncelikle araçlara ve çekirdeklere öncelik verecektir. Bu, sıfır noktasının 0'a eşit olduğu simetrik nicelemenin kolaylık sağlaması içindir. Buna ek olarak, birçok arka uç, int8xint8 birikmesi için ek optimizasyonlara sahiptir.

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

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

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, evrimlerin ağırlıklarının output_channel değeridir ancak teoride bu boyut, çekirdek uygulamasındaki her bir nokta ürününe karşılık gelen boyut olabilir. Bu da, performans üzerinde herhangi bir etkisi olmadan daha fazla nicelik ayrıntısı elde edilmesini sağlar. Bu yöntemin doğruluğunda önemli bir iyileşme vardır.

TFLite'da, giderek artan sayıda işlem için eksen başına destek sunuluyor. Bu belgenin gönderildiği tarihte, Conv2d ve DepthwiseConv2d için destek sağlanmaktadır.

Simetrik ve asimetrik

Etkinleştirmeler asimetriktir: İmzalı int8 aralığı [-128, 127] içinde herhangi bir yerde sıfır puanlarına sahip olabilirler. Birçok etkinleştirme asimetriktir ve sıfır noktası, ekstra ikili bite etkili bir şekilde ulaşmanın nispeten ucuz bir yoludur. Etkinleştirmeler yalnızca sabit ağırlıklarla çarpıldığından, sabit sıfır puan 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ı zorunlu tutulur. Ağırlık değerleri, dinamik giriş ve etkinleştirme değerleriyle çarpılır. Bu, ağırlığın sıfır noktasının etkinleştirme değeriyle çarpılması halinde kaçınılmaz bir çalışma zamanı maliyeti olacağı anlamına gelir. Sıfır noktası 0 olarak ayarlayarak bu maliyeti önleyebiliriz.

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

$A$, sayısalleştirilmiş etkinleştirmelerden oluşan $m \times n$ matrisidir.
$B$, sayısallaştırılmış ağırlıklardan oluşan $n \times p$ matrisidir.
Her ikisi de $n$uzunluğunda olmak üzere, $A$ ve $a_j$ öğesinin $j$. satırını, $B$ ve $b_k$ öğesinin $k$. sütunuyla çarpabilirsiniz. Ölçülen tam sayı değerleri ve sıfır puan 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\]

Girdi değerinin ve ağırlık değerinin nokta çarpımına sahip olduğundan \(\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\) terimler, çıkarım çağrısı başına aynı kalan sabit değerlerden 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\) terimin her çıkarımda hesaplanması gerekir. Ağırlıkların simetrik olmasını zorunlu kılarak bu terimin maliyetini kaldırabiliriz.

int8 nicelenmiş operatör özellikleri

Aşağıda, int8 tflite çekirdeklerimiz için nicelik belirleme gereksinimleri 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