Terima kasih telah mendengarkan Google I/O. Lihat semua sesi sesuai permintaan Tonton sesuai permintaan

Spesifikasi kuantisasi TensorFlow Lite 8-bit

Dokumen berikut menguraikan spesifikasi skema kuantisasi 8-bit TensorFlow Lite. Ini dimaksudkan untuk membantu pengembang perangkat keras dalam memberikan dukungan perangkat keras untuk inferensi dengan model TensorFlow Lite terkuantisasi.

Ringkasan spesifikasi

Kami menyediakan spesifikasi, dan kami hanya dapat memberikan beberapa jaminan perilaku jika spesifikasi diikuti. Kami juga memahami bahwa perangkat keras yang berbeda mungkin memiliki preferensi dan batasan yang dapat menyebabkan sedikit penyimpangan saat mengimplementasikan spesifikasi yang menghasilkan implementasi yang tidak tepat. Sementara itu mungkin dapat diterima dalam banyak kasus (dan kami akan menyediakan serangkaian tes yang sejauh pengetahuan kami mencakup toleransi per-operasi yang kami kumpulkan dari beberapa model), sifat pembelajaran mesin (dan pembelajaran mendalam yang paling umum case) tidak memungkinkan untuk memberikan jaminan keras apa pun.

Kuantisasi 8-bit mendekati nilai floating point menggunakan rumus berikut.

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

Per-axis (alias per-channel di ops Conv) atau per-tensor bobot yang diwakili oleh int8 dua ini nilai pelengkap dalam rentang [-127, 127] dengan titik nol sama dengan 0. aktivasi per-tensor / input diwakili oleh int8 dua itu nilai pelengkap dalam rentang [-128, 127] , dengan titik nol dalam rentang [-128, 127] .

Ada pengecualian lain untuk operasi tertentu yang didokumentasikan di bawah ini.

Bilangan bulat bertanda vs bilangan bulat tidak bertanda

TensorFlow Lite kuantisasi akan perkakas terutama memprioritaskan dan kernel untuk int8 kuantisasi untuk 8-bit. Ini adalah untuk kenyamanan kuantisasi simetris yang diwakili oleh titik nol sama dengan 0. Selain itu banyak backends memiliki optimasi tambahan untuk int8xint8 akumulasi.

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. Sebagai contoh, sebuah tensor t , dengan dims=[4, 3, 2, 1] dengan params quantization: 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 adalah asimetris: mereka dapat memiliki mana saja mereka titik nol dalam menandatangani int8 rentang [-128, 127] . Banyak aktivasi bersifat asimetris dan titik nol adalah cara yang relatif murah untuk secara efektif mendapatkan bit presisi ekstra biner. Karena aktivasi hanya dikalikan dengan bobot konstan, nilai titik nol konstan dapat dioptimalkan cukup banyak.

Bobotnya simetris: dipaksa memiliki titik nol sama dengan 0. Nilai bobot dikalikan dengan nilai input dan aktivasi dinamis. Ini berarti bahwa ada biaya runtime yang tidak dapat dihindari untuk mengalikan titik nol dari bobot dengan nilai aktivasi. Dengan menegakkan bahwa titik nol adalah 0 kita dapat menghindari biaya ini.

Penjelasan matematika: ini mirip dengan bagian 2.3 di arXiv: 1712,05877 , kecuali perbedaan yang kita membiarkan nilai-nilai skala untuk menjadi per-axis. Ini menggeneralisasi dengan mudah, sebagai berikut:

\(A\) adalah \(m \times n\) matriks aktivasi terkuantisasi.
\(B\) adalah \(n \times p\) matriks bobot terkuantisasi.
Pertimbangkan mengalikan \(j\)th deretan \(A\), \(a_j\) oleh \(k\)th kolom\(B\), \(b_k\), baik panjang \(n\). Nilai-nilai bilangan bulat terkuantisasi dan nol-poin nilai yang \(q_a\), \(z_a\) dan \(q_b\), \(z_b\) masing-masing.

\[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\]

The \(\sum_{i=0}^{n} q_{a}^{(i)} q_{b}^{(i)}\) jangka tidak dapat dihindari karena ini melakukan dot product dari nilai input dan nilai berat badan.

The \(\sum_{i=0}^{n} q_{b}^{(i)} z_a\) dan \(\sum_{i=0}^{n} z_a z_b\) hal terdiri dari konstanta yang tetap sama per inferensi doa, dan dengan demikian dapat pra-dihitung.

The \(\sum_{i=0}^{n} q_{a}^{(i)} z_b\) jangka perlu dihitung setiap inferensi sejak aktivasi berubah setiap inferensi. Dengan menegakkan bobot menjadi simetris, kita 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

Referensi

arXiv: 1712.05877