Quy cách lượng tử hoá LiteRT 8 bit

Tài liệu sau đây trình bày thông số kỹ thuật cho phiên bản 8 bit của LiteRT sơ đồ lượng tử hoá. Thông tin 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 để suy luận với các mô hình LiteRT lượng tử hoá.

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

Lượng tử hoá 8 bit ước lượng giá trị dấu phẩy động bằng cách sử dụng .

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

Mỗi trục (còn gọi là mỗi kênh trong chiến dịch Chuyển đổi) hoặc trọng số trên mỗi tensor được biểu thị bằng int8 giá trị bù của hai trong dải ô [-127, 127] có điểm 0 bằng thành 0. Số lần kích hoạt/đầu vào trên mỗi tensor được biểu thị bằng phần bổ sung của int8 hai các giá trị trong dải ô [-128, 127], với một điểm 0 trong dải ô [-128, 127].

Có những ngoại lệ khác đối với các thao tác cụ thể được nêu dưới đây.

Số nguyên đã ký và số nguyên chưa ký

Quá trình lượng tử LiteRT sẽ chủ yếu ưu tiên cho công cụ và hạt nhân cho Lượng tử hoá int8 cho 8 bit. Điều này để thuận tiện cho tính đối xứng lượng tử hoá được biểu diễn bằng điểm không bằng 0. Ngoài ra, nhiều phần phụ trợ có các tính năng tối ưu hoá bổ sung để tích luỹ int8xint8.

Trên mỗi trục so với trên mỗi tensor

Lượng tử hoá trên mỗi tensor có nghĩa là sẽ có một tỷ lệ và/hoặc điểm không trên mỗi toàn bộ tensor. Lượng tử hoá trên mỗi trục nghĩa là sẽ có một tỷ lệ và/hoặc zero_point cho mỗi lát trong quantized_dimension. Phương diện lượng tử hoá xác định kích thước hình dạng của Tensor mà các tỷ lệ và điểm không tương ứng với. Ví dụ: một tensor t, với dims=[4, 3, 2, 1] có tham số lượng tử hoá: scale=[1.0, 2.0, 3.0], zero_point=[1, 2, 3], quantization_dimension=1 sẽ được lượng tử hoá 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 trọng số của tích chập, nhưng về lý thuyết, nó có thể là chiều tương ứng với mỗi chấm-product trong triển khai nhân, cho phép độ chi tiết lượng tử hoá cao hơn mà không ảnh hưởng đến hiệu suất. Điều này giúp cải thiện đáng kể độ chính xác.

TFLite hỗ trợ theo mỗi trục cho số lượng thao tác ngày càng tăng. Vào thời điểm trong tài liệu này, có hỗ trợ cho Conv2d và DepthwiseConv2d.

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

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

Trọng số đối xứng: bắt buộc phải có điểm không bằng 0. Giá trị trọng số là nhân với giá trị đầu vào động và giá trị kích hoạt. Điều này có nghĩa là chi phí thời gian chạy không thể tránh khỏi của việc 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 không bằng 0, chúng ta có thể tránh chi phí này.

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

$A$ là một ma trận $m \times n$ của các kích hoạt lượng tử hoá.
$B$ là một ma trận $n \times p$ của các trọng số lượng tử hoá.
Hãy cân nhắc nhân hàng thứ $j$của $A$, $a_j$ với cột thứ $k$của $B$, $b_k$, cả hai đều có thời lượng $n$. Các giá trị số nguyên lượng tử hoá 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ì đó thực hiện tích vô hướng của giá trị nhập vào và giá trị trọng số.

Các điều khoản \(\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ố không đổi đối với 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 mỗi lần suy luận vì việc kích hoạt sẽ thay đổi mọi suy luận. Bằng cách thực thi trọng số để đối xứng nên chúng tôi có thể bỏ chi phí của số hạng này.

quy cách toán tử lượng tử hoá int8

Dưới đây, chúng tôi mô tả các yêu cầu lượng tử cho hạt nhân int8 tflite 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

Tài liệu tham khảo

arXiv:1712.05877