ข้อมูลจำเพาะของการแปลงรหัส 8 บิต LiteRT

เอกสารต่อไปนี้ระบุข้อกำหนดสำหรับรูปแบบการหาปริมาณ 8 บิตของ LiteRT ซึ่งมีจุดประสงค์เพื่อช่วยนักพัฒนาฮาร์ดแวร์ในการให้ การสนับสนุนฮาร์ดแวร์สำหรับการอนุมานด้วยโมเดล LiteRT ที่มีการวัดปริมาณ

สรุปข้อกำหนด

เราจะระบุข้อกำหนด และรับประกันลักษณะการทำงานได้ก็ต่อเมื่อมีการปฏิบัติตามข้อกำหนด นอกจากนี้ เรายังเข้าใจว่าฮาร์ดแวร์ที่แตกต่างกันอาจมีค่ากำหนดและข้อจำกัดที่อาจทำให้เกิดความคลาดเคลื่อนเล็กน้อยเมื่อใช้ข้อกำหนด ซึ่งส่งผลให้การใช้งานไม่ตรงกันทุกบิต แม้ว่าในกรณีส่วนใหญ่จะยอมรับได้ (และเราจะจัดชุดการทดสอบที่รวมค่าความคลาดเคลื่อนต่อการดำเนินการตามความรู้ของเรา ซึ่งเราได้รวบรวมจากโมเดลหลายรายการ) แต่ลักษณะของแมชชีนเลิร์นนิง (และดีปเลิร์นนิงในกรณีที่พบบ่อยที่สุด) ทำให้เราไม่สามารถรับประกันได้อย่างแน่นอน

การหาปริมาณ 8 บิตจะประมาณค่าทศนิยมโดยใช้สูตรต่อไปนี้

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

น้ำหนักต่อแกน (หรือที่เรียกว่าต่อแชแนลใน Conv ops) หรือต่อเทนเซอร์แสดงด้วย int8 ค่าส่วนเติมเต็มของ 2 ในช่วง [-127, 127] โดยมีจุดศูนย์เท่ากับ 0 การเปิดใช้งาน/อินพุตต่อเทนเซอร์แสดงด้วยค่าint8ส่วนเติมเต็มของ 2 ในช่วง [-128, 127] โดยมีจุดศูนย์ในช่วง [-128, 127]

นอกจากนี้ ยังมีข้อยกเว้นอื่นๆ สำหรับการดำเนินการบางอย่างที่ระบุไว้ด้านล่าง

จำนวนเต็มที่มีเครื่องหมายเทียบกับจำนวนเต็มที่ไม่มีเครื่องหมาย

การวัดปริมาณ LiteRT จะให้ความสำคัญกับเครื่องมือและเคอร์เนลสำหรับการint8 วัดปริมาณสำหรับ 8 บิตเป็นหลัก ซึ่งจะช่วยให้การหาปริมาณแบบสมมาตร แสดงด้วยจุดศูนย์เท่ากับ 0 ได้สะดวก นอกจากนี้ Backend จำนวนมากยังมีการเพิ่มประสิทธิภาพเพิ่มเติมสำหรับint8xint8การสะสม

ต่อแกนเทียบกับต่อเทนเซอร์

การวัดปริมาณต่อเทนเซอร์หมายความว่าจะมีสเกลและ/หรือจุดศูนย์ต่อเทนเซอร์ทั้งหมด การกำหนดปริมาณต่อแกนหมายความว่าจะมีสเกลและ/หรือ zero_point ต่อชิ้นใน quantized_dimension มิติข้อมูลที่กำหนดปริมาณ จะระบุขนาดของรูปร่างของเทนเซอร์ที่สเกลและจุดศูนย์ สอดคล้องกัน ตัวอย่างเช่น เทนเซอร์ t ที่มี dims=[4, 3, 2, 1] ที่มี พารามิเตอร์การหาปริมาณ scale=[1.0, 2.0, 3.0], zero_point=[1, 2, 3], quantization_dimension=1 จะได้รับการหาปริมาณในมิติที่ 2 ของ 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

โดยส่วนใหญ่แล้ว quantized_dimension คือ output_channel ของน้ำหนักของ การสังวัตนาการ แต่ในทางทฤษฎีแล้วอาจเป็นมิติที่สอดคล้องกับ การคูณเมทริกซ์แต่ละรายการในการใช้งานเคอร์เนล ซึ่งช่วยให้การแบ่งระดับการหาปริมาณมีความละเอียดมากขึ้น โดยไม่มีผลต่อประสิทธิภาพ ซึ่งช่วยปรับปรุงความแม่นยำได้อย่างมาก

TFLite รองรับการดำเนินการตามแกนสำหรับจำนวนการดำเนินการที่เพิ่มขึ้น ในขณะที่เขียนเอกสารนี้ มีการรองรับ Conv2d และ DepthwiseConv2d

สมมาตรเทียบกับไม่สมมาตร

การเปิดใช้งานไม่สมมาตร โดยมีจุดศูนย์กลางอยู่ที่ใดก็ได้ภายในint8ช่วง[-128, 127]ที่ลงชื่อ การเปิดใช้งานหลายอย่างมีลักษณะที่ไม่สมมาตร และ จุดศูนย์เป็นวิธีที่ไม่แพงนักในการเพิ่มความแม่นยำได้สูงสุดอีก บิตไบนารี เนื่องจากการเปิดใช้งานจะคูณด้วยค่าคงที่ เท่านั้น ค่าคงที่ของจุดศูนย์จึงได้รับการเพิ่มประสิทธิภาพอย่างมาก

น้ำหนักจะสมมาตร: บังคับให้มีจุดศูนย์เท่ากับ 0 ค่าถ่วงน้ำหนักจะคูณด้วยค่าอินพุตแบบไดนามิกและค่าการเปิดใช้งาน ซึ่งหมายความว่าจะมี ค่าใช้จ่ายรันไทม์ที่หลีกเลี่ยงไม่ได้ในการคูณจุดศูนย์ของน้ำหนักกับ ค่าการเปิดใช้งาน การบังคับให้จุดศูนย์เป็น 0 จะช่วยให้เราหลีกเลี่ยงค่าใช้จ่ายนี้ได้

คำอธิบายทางคณิตศาสตร์: วิธีนี้คล้ายกับส่วนที่ 2.3 ใน arXiv:1712.05877 ยกเว้นความแตกต่าง ที่เราอนุญาตให้ค่าสเกลเป็นต่อแกน ซึ่งสามารถสรุปได้ดังนี้

$A$ คือเมทริกซ์ $m \times n$ ของการเปิดใช้งานที่หาปริมาณแล้ว
$B$ คือเมทริกซ์ $n \times p$ ของน้ำหนักที่วัดปริมาณ
ลองคูณแถวที่ $j$ ของ $A$, $a_j$ ด้วยคอลัมน์ที่ $k$ ของ $B$, $b_k$ ซึ่งทั้ง 2 มีความยาว $n$ ค่าจำนวนเต็มที่หาปริมาณและ ค่าจุดศูนย์คือ $q_a$, $z_a$ และ $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\]

\(\sum_{i=0}^{n} q_{a}^{(i)} q_{b}^{(i)}\) term is unavoidable since it’s performing the dot product of the input value and the weight value.

คำว่า \(\sum_{i=0}^{n} q_{b}^{(i)} z_a\) และ \(\sum_{i=0}^{n} z_a z_b\) ประกอบด้วยค่าคงที่ที่ยังคงเหมือนเดิมต่อการเรียกใช้การอนุมานแต่ละครั้ง จึงสามารถคำนวณล่วงหน้าได้

\(\sum_{i=0}^{n} q_{a}^{(i)} z_b\) ต้องคำนวณเทอมทุกครั้ง ที่อนุมานเนื่องจากการเปิดใช้งานจะเปลี่ยนไปทุกครั้งที่อนุมาน การบังคับให้น้ำหนักสมมาตรจะช่วยให้เราตัดค่าใช้จ่ายของเทอมนี้ออกได้

ข้อกำหนดของตัวดำเนินการที่แปลงเป็นจำนวนเต็ม 8 บิต

ด้านล่างนี้เราจะอธิบายข้อกำหนดการหาปริมาณสำหรับเคอร์เนล TFLite แบบ int8

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

ข้อมูลอ้างอิง

arXiv:1712.05877