Tài liệu sau đây phác thảo đặc điểm kỹ thuật cho lược đồ lượng tử hóa 8 bit của TensorFlow Lite. Điều này nhằm mục đích hỗ trợ các nhà phát triển phần cứng cung cấp hỗ trợ phần cứng để 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 một 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 từng bit. Mặc dù đ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ộ kiểm tra mà theo hiểu biết tốt nhất của chúng tôi bao gồm dung sai cho mỗi hoạt động 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 trường hợp) làm cho nó không thể cung cấp bất kỳ đảm bảo cứng.
Lượng tử hóa 8 bit xấp xỉ các giá trị dấu chấm động bằng công thức sau.
Trọng số trên mỗi trục (hay còn gọi là mỗi kênh trong Hoạt động chuyển đổi) hoặc trọng số trên tensor được biểu thị bằng giá trị bổ sung của int8
hai trong phạm vi [-127, 127]
với điểm 0 bằng 0. Kích hoạt / đầu vào mỗi tensor được biểu thị bằng int8
hai giá trị phần bù trong phạm vi [-128, 127]
, với một điểm 0 trong phạm vi [-128, 127]
.
Có những ngoại lệ khác cho các hoạt động cụ thể được ghi lại bên dưới.
Số nguyên có dấu so với số nguyên không dấu
Lượng tử hóa TensorFlow Lite chủ yếu sẽ ư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 lượng tử hóa đối xứng được biểu diễn bằng điểm không bằng 0. Ngoài ra, nhiều phụ trợ có các tối ưu hóa bổ sung cho 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 thứ nguyên quantized_dimension
. Kích thướ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ụ: tensor t
, với dims=[4, 3, 2, 1]
với các tham 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 qua 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, thứ nguyên quantized_dimension
là đầu output_channel
của trọng số của các chập, nhưng về lý thuyết, nó có thể là thứ nguyên tương ứng với mỗi sản phẩm chấm trong việc triển khai kernel, cho phép lượng tử hóa chi tiết 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ợ mỗi trục cho một số hoạt động ngày càng tăng. Tại thời điểm tài liệu này, hỗ trợ cho Conv2d và DepthwiseConv2d.
Đối xứng và không đố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 là không đối xứng và điểm 0 là một cách tương đối rẻ tiền để đạt được hiệu quả thêm một bit nhị phân chính xác. Vì các lần kích hoạt chỉ được nhân với trọng số không đổi, giá trị điểm 0 không đổi có thể được tối ưu hóa khá nhiều.
Trọng lượng là đối xứng: buộc phải có điểm không bằng 0. Giá trị trọng lượng được nhân với giá trị đầu vào và kích hoạt động. Điều này có nghĩa là có một chi phí thời gian chạy không thể tránh khỏi là 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: điều này tương tự như phần 2.3 trong arXiv: 1712.05877 , ngoại trừ sự khác biệt là chúng tôi cho phép các giá trị tỷ lệ theo 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ủa các trọng số đã lượng tử hóa.
Xem xét 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ó độ dài $ n $. 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 $.
Số hạng \(\sum_{i=0}^{n} q_{a}^{(i)} q_{b}^{(i)}\) là không thể tránh khỏi vì nó thực hiện tích số chấm của giá trị đầu vào và giá trị trọng số.
Các
và
các thuật ngữ được tạo thành từ các hằng số không thay đổi cho mỗi lần 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 suy luận vì việc kích hoạt thay đổi mọi suy luận. Bằng cách bắt buộc các trọng số phải đối xứng, chúng ta có thể loại bỏ chi phí của thuật ngữ này.
thông số kỹ thuật 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 đối với 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-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