Thông số lượng tử hóa 8 bit của TensorFlow Lite

Tài liệu sau đây phác thảo thông số kỹ thuật cho sơ đồ lượng tử hóa 8 bit của TensorFlow Lite. Điều này nhằm hỗ trợ các nhà phát triển phần cứng trong việc cung cấp hỗ trợ phần cứng cho việc suy luận với các mô hình TensorFlow Lite được lượng tử hóa.

Tóm tắt thông số kỹ thuật

Chúng tôi đang cung cấp thông số kỹ thuật và chúng tôi chỉ có thể cung cấp một số đảm bảo về hành vi nếu thông số kỹ thuật được tuân thủ. Chúng tôi cũng hiểu rằng các phần cứng khác nhau có thể có các tùy chọn và hạn chế có thể gây ra sai lệch nhỏ khi triển khai thông số kỹ thuật dẫn đến việc triển khai không chính xác chút nào. Trong khi đó, điều đó có thể được chấp nhận trong hầu hết các trường hợp (và chúng tôi sẽ cung cấp một bộ thử nghiệm theo hiểu biết tốt nhất của chúng tôi bao gồm dung sai cho mỗi thao tác mà chúng tôi thu thập được từ một số mô hình), bản chất của học máy (và học sâu trong các mô hình phổ biến nhất). case) khiến không thể cung cấp bất kỳ sự đảm bảo chắc chắn nào.

Lượng tử hóa 8 bit xấp xỉ các giá trị dấu phẩy động bằng công thức sau.

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

Trọng số trên mỗi trục (còn gọi là trên mỗi kênh trong Hoạt động chuyển đổi) hoặc trọng số trên mỗi tensor được biểu thị bằng các giá trị bù của int8 two trong phạm vi [-127, 127] với điểm 0 bằng 0. Kích hoạt/đầu vào trên mỗi tensor được biểu thị bằng int8 các giá trị bù của hai trong phạm vi [-128, 127] , với điểm 0 trong phạm vi [-128, 127] .

Có những trường hợp ngoại lệ khác cho các hoạt động cụ thể được ghi lại dưới đây.

Số nguyên có dấu và số nguyên không dấu

Lượng tử hóa TensorFlow Lite sẽ chủ yếu ưu tiên công cụ và hạt nhân để lượng tử hóa int8 cho 8 bit. Điều này là để thuận tiện cho việc lượng tử hóa đối xứng được biểu thị bằng điểm 0 bằng 0. Ngoài ra, nhiều chương trình phụ trợ còn có các tối ưu hóa bổ sung cho việc tích lũy int8xint8 .

Mỗi trục so với mỗi tensor

Lượng tử hóa trên mỗi tensor có nghĩa là sẽ có một thang đo và/hoặc điểm 0 trên toàn bộ tensor. Lượng tử hóa trên mỗi trục có nghĩa là sẽ có một tỷ lệ và/hoặc zero_point trên mỗi lát trong quantized_dimension . Thứ nguyên được lượng tử hóa chỉ định kích thước của hình dạng Tensor mà tỷ lệ và điểm 0 tương ứng. Ví dụ: một tensor t , với dims=[4, 3, 2, 1] với các thông số lượng tử hóa: scale=[1.0, 2.0, 3.0] , zero_point=[1, 2, 3] , quantization_dimension=1 sẽ được lượng tử hóa trên chiều thứ hai của 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

Thông thường, quantized_dimensionoutput_channel của các trọng số tích chập, nhưng về lý thuyết, nó có thể là thứ nguyên tương ứng với từng tích số chấm trong quá trình triển khai kernel, cho phép độ chi tiết lượng tử hóa nhiều hơn mà không ảnh hưởng đến hiệu suất. Điều này có những cải tiến lớn về độ chính xác.

TFLite có hỗ trợ trên mỗi trục cho số lượng hoạt động ngày càng tăng. Tại thời điểm thực hiện tài liệu này, đã có hỗ trợ cho Conv2d và DepthwiseConv2d.

Đối xứng và bất đối xứng

Kích hoạt không đối xứng: chúng có thể có điểm 0 ở bất kỳ đâu trong phạm vi int8 đã ký [-128, 127] . Nhiều kích hoạt có bản chất không đối xứng và điểm 0 là một cách tương đối rẻ tiền để đạt được độ chính xác nhị phân bổ sung một cách hiệu quả. Vì số lần kích hoạt chỉ được nhân với trọng số không đổi nên giá trị điểm 0 không đổi có thể được tối ưu hóa khá nhiều.

Các trọng số đối xứng: buộc phải có điểm 0 bằng 0. Giá trị trọng số được nhân với giá trị kích hoạt và đầu vào động. Điều này có nghĩa là không thể tránh khỏi chi phí thời gian chạy khi nhân điểm 0 của trọng số với giá trị kích hoạt. Bằng cách thực thi điểm 0 là 0, chúng ta có thể tránh được chi phí này.

Giải thích về phép toán: phần này tương tự như phần 2.3 trong arXiv:1712.05877 , ngoại trừ điểm khác biệt là chúng tôi cho phép các giá trị tỷ lệ trên mỗi trục. Điều này khái quát dễ dàng, như sau:

\(A\) là ma trận \(m \times n\) của các kích hoạt được lượng tử hóa.
\(B\) là ma trận \(n \times p\) có trọng số được lượng tử hóa.
Hãy xem xét nhân hàng thứ \(j\)của \(A\), \(a_j\) với cột thứ l10n \(k\)của\(B\), \(b_k\), cả hai đều có độ dài \(n\). Các giá trị số nguyên được lượng tử hóa và giá trị điểm 0 lần lượt là \(q_a\), \(z_a\) và \(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\]

Thuật ngữ \(\sum_{i=0}^{n} q_{a}^{(i)} q_{b}^{(i)}\) là không thể tránh khỏi vì nó đang thực hiện tích số chấm của giá trị đầu vào và giá trị trọng số.

Các thuật ngữ \(\sum_{i=0}^{n} q_{b}^{(i)} z_a\) và \(\sum_{i=0}^{n} z_a z_b\) được tạo thành từ các hằng số giữ nguyên cho mỗi lệnh gọi suy luận và do đó có thể được tính toán trước.

Thuật ngữ \(\sum_{i=0}^{n} q_{a}^{(i)} z_b\) cần được tính toán cho mỗi suy luận vì việc kích hoạt sẽ thay đổi mọi suy luận. Bằng cách buộc các trọng số phải đối xứng, chúng ta có thể loại bỏ chi phí của số hạng này.

Thông số toán tử lượng tử hóa int8

Dưới đây chúng tôi mô tả các yêu cầu lượng tử hóa cho hạt nhân tflite int8 của chúng tôi:

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

Người giới thiệu

arXiv:1712.05877