ข้อกำหนดการกำหนดปริมาณ TensorFlow Lite 8 บิต

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

สรุปข้อมูลจำเพาะ

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

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

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

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

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

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

การหาปริมาณของ TensorFlow Lite จะจัดลำดับความสำคัญของเครื่องมือและเคอร์เนลเป็นหลักสำหรับการหาปริมาณ int8 สำหรับ 8 บิต นี่เป็นเพื่อความสะดวกของการหาปริมาณแบบสมมาตรซึ่งแสดงด้วยจุดศูนย์เท่ากับ 0 นอกจากนี้ แบ็กเอนด์จำนวนมากยังมีการปรับให้เหมาะสมเพิ่มเติมสำหรับการสะสม 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 จะถูกหาปริมาณข้าม มิติที่สองของ 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 ของน้ำหนักของการโน้มน้าวใจ แต่ในทางทฤษฎีแล้ว มันอาจเป็นมิติที่สอดคล้องกับแต่ละ dot-product ในการใช้งานเคอร์เนล ซึ่งช่วยให้มีรายละเอียดเชิงปริมาณมากขึ้นโดยไม่มีผลกระทบต่อประสิทธิภาพ สิ่งนี้มีการปรับปรุงความแม่นยำอย่างมาก

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\) ด้วยคอลัมน์ที่ l10n \(k\)ของ\(B\), \(b_k\)ทั้งสองความยาว \(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)}\) เป็นสิ่งที่หลีกเลี่ยงไม่ได้ เนื่องจากกำลังดำเนินการผลคูณดอทของค่าอินพุตและค่าน้ำหนัก

เงื่อนไข \(\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\) จำเป็นต้องได้รับการคำนวณทุกการอนุมาน เนื่องจากการเปิดใช้งานจะเปลี่ยนทุกการอนุมาน ด้วยการบังคับให้น้ำหนักมีความสมมาตร เราสามารถขจัดต้นทุนของคำนี้ได้

ข้อมูลจำเพาะของตัวดำเนินการเชิงปริมาณ int8

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

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