Google I / O là một kết quả hoàn hảo! Cập nhật các phiên TensorFlow Xem phiên

Đặc điểm kỹ thuật lượng tử hóa 8-bit của TensorFlow Lite

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 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 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 mô hình phổ biến nhất 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 tính gần đúng giá trị dấu phẩy động bằng công thức sau.

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

Per-trục (aka mỗi kênh trong ops Conv) hoặc mỗi tensor trọng số được đại diện bởi int8 giá trị bổ sung hai trong phạm vi [-127, 127] với zero-điểm bằng 0. kích hoạt mỗi tensor / đầu vào được đại diện bởi int8 của hai giá trị bổ sung trong khoảng [-128, 127] , với một điểm zero 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

TensorFlow Lite lượng tử sẽ dụng cụ chủ yếu ưu tiên và hạt nhân cho int8 lượng tử 8-bit. Này là dành cho sự tiện lợi của lượng tử đối xứng được đại diện bởi zero-điểm bằng 0. Thêm vào đó nhiều backends có tối ưu hóa bổ sung cho int8xint8 tích lũy.

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

Lượng tử hóa trên tensor có nghĩa là sẽ có một thang đo và / hoặc điểm 0 trên toàn bộ tensor. Per-trục phương tiện lượng tử rằng sẽ có một quy mô và / hoặc zero_point mỗi lát trong 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ới nhau. Ví dụ, một tensor t , với dims=[4, 3, 2, 1] với params lượng tử: 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 trọng lượng của nhiều nếp cuộn, nhưng về mặt lý thuyết nó có thể được kích thước tương ứng với mỗi dot-sản phẩm trong việc thực hiện hạt nhân, cho phép nhiều lượng tử granularity mà không ảnh hưởng 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 là không đối xứng: họ có thể có bất cứ nơi nào zero-quan điểm của họ trong ký int8 phạm vi [-128, 127] . Nhiều kích hoạt về 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, nên 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 tôi có thể tránh được chi phí này.

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

\(A\) là một \(m \times n\) ma trận kích hoạt lượng tử.
\(B\) là một \(n \times p\) ma trận của trọng lượng tử.
Xem xét nhân \(j\)thứ hàng \(A\), \(a_j\) bởi \(k\)thứ cột của\(B\), \(b_k\), cả hai chiều dài \(n\). Các giá trị số nguyên lượng tử và zero-điểm giá trị là \(q_a\), \(z_a\) và \(q_b\), \(z_b\) tương ứng.

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

Các \(\sum_{i=0}^{n} q_{a}^{(i)} q_{b}^{(i)}\) hạn là không thể tránh khỏi vì nó thực hiện chấm sản phẩm giá trị đầu vào và giá trị cân.

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

Các \(\sum_{i=0}^{n} q_{a}^{(i)} z_b\) hạn cần phải được tính toán mỗi suy luận kể từ khi 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ử được 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

Người giới thiệu

arXiv: 1712.05877