Dokumen berikut menguraikan spesifikasi untuk skema kuantisasi 8-bit TensorFlow Lite. Ini dimaksudkan untuk membantu developer hardware dalam memberikan dukungan hardware untuk inferensi dengan model TensorFlow Lite terkuantisasi.
Ringkasan spesifikasi
Kami menyediakan spesifikasi, dan kami hanya dapat memberikan jaminan atas perilaku jika spesifikasi tersebut diikuti. Kami juga memahami bahwa perangkat keras yang berbeda mungkin memiliki preferensi dan batasan yang dapat menyebabkan sedikit penyimpangan saat mengimplementasikan spesifikasi yang mengakibatkan implementasi yang tidak tepat bit. Sementara itu mungkin dapat diterima dalam banyak kasus (dan kami akan memberikan serangkaian pengujian yang sejauh pengetahuan kami termasuk toleransi per operasi yang kami kumpulkan dari beberapa model), sifat pembelajaran mesin (dan pembelajaran mendalam pada yang paling umum case) tidak memungkinkan untuk memberikan jaminan keras apa pun.
Kuantisasi 8-bit mendekati nilai floating point menggunakan rumus berikut.
Per-sumbu (alias per-saluran dalam operasi konv.) Atau bobot per-tensor diwakili oleh nilai komplemen int8
dua dalam rentang [-127, 127]
dengan titik nol sama dengan 0. Aktivasi / input per-tensor diwakili oleh nilai komplemen int8
two dalam rentang [-128, 127]
, dengan titik nol dalam rentang [-128, 127]
.
Ada pengecualian lain untuk operasi tertentu yang didokumentasikan di bawah ini.
Integer bertanda vs integer tak bertanda tangan
Kuantisasi TensorFlow Lite terutama akan memprioritaskan perkakas dan kernel untuk kuantisasi int8
untuk 8-bit. Ini untuk kenyamanan kuantisasi simetris yang diwakili oleh titik nol yang sama dengan 0. Selain itu, banyak backend memiliki pengoptimalan tambahan untuk akumulasi int8xint8
.
Per sumbu vs per tensor
Kuantisasi per-tensor berarti bahwa akan ada satu skala dan / atau titik nol per seluruh tensor. Per-axis kuantisasi berarti bahwa akan ada satu skala dan / atau zero_point
per slice di quantized_dimension
. Dimensi terkuantisasi menentukan dimensi bentuk Tensor yang sesuai dengan skala dan titik nol. Misalnya, tensor t
, dengan dims=[4, 3, 2, 1]
dengan parameter kuantisasi: scale=[1.0, 2.0, 3.0]
, zero_point=[1, 2, 3]
, quantization_dimension=1
akan dikuantisasi di dimensi kedua t
:
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
Seringkali, quantized_dimension
adalah output_channel
dari bobot convolutions, tetapi dalam teori dapat menjadi dimensi yang sesuai dengan masing-masing dot-produk dalam pelaksanaan kernel, memungkinkan lebih banyak rincian kuantisasi tanpa implikasi kinerja. Ini memiliki peningkatan besar pada akurasi.
TFLite memiliki dukungan per sumbu untuk semakin banyak operasi. Pada saat dokumen ini dibuat, dukungan tersedia untuk Conv2d dan DepthwiseConv2d.
Simetris vs asimetris
Aktivasi asimetris: mereka dapat memiliki titik nol di mana saja dalam rentang int8
ditandatangani [-128, 127]
. Banyak aktivasi yang bersifat asimetris dan titik nol adalah cara yang relatif murah untuk secara efektif mencapai bit presisi biner ekstra. Karena aktivasi hanya dikalikan dengan bobot konstan, nilai titik nol konstan dapat sangat dioptimalkan.
Bobot simetris: dipaksa memiliki titik nol yang sama dengan 0. Nilai bobot dikalikan dengan nilai input dan aktivasi dinamis. Ini berarti bahwa ada biaya waktu proses yang tidak dapat dihindari untuk mengalikan titik nol bobot dengan nilai aktivasi. Dengan menetapkan bahwa titik nol adalah 0 kita dapat menghindari biaya ini.
Penjelasan matematika: ini mirip dengan bagian 2.3 di arXiv: 1712.05877 , kecuali untuk perbedaannya kita mengizinkan nilai skala menjadi per sumbu. Ini mudah digeneralisasikan, sebagai berikut:
$ A $ adalah matriks $ m \ times n $ aktivasi terkuantisasi.
$ B $ adalah matriks $ n \ times p $ bobot terkuantisasi.
Pertimbangkan untuk mengalikan baris $ j $ th $ A $, $ a_j $ dengan kolom $ k $ th $ B $, $ b_k $, keduanya panjangnya $ n $. Nilai integer terkuantisasi dan nilai titik nol masing-masing adalah $ q_a $, $ z_a $ dan $ q_b $, $ z_b $.
Istilah \(\sum_{i=0}^{n} q_{a}^{(i)} q_{b}^{(i)}\) tidak dapat dihindari karena melakukan perkalian titik dari nilai input dan nilai bobot.
Itu
dan
suku terdiri dari konstanta yang tetap sama per permintaan inferensi, dan dengan demikian dapat dihitung sebelumnya.
Istilah \(\sum_{i=0}^{n} q_{a}^{(i)} z_b\) perlu dihitung setiap inferensi karena aktivasi mengubah setiap inferensi. Dengan memaksakan bobot menjadi simetris, kami dapat menghapus biaya istilah ini.
spesifikasi operator terkuantisasi int8
Di bawah ini kami menjelaskan persyaratan kuantisasi untuk kernel tflite int8 kami:
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