ข้อมูลจำเพาะของการวัดปริมาณ TensorFlow Lite แบบ 8 บิต

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

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

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

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

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

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

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

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

การวัดปริมาณของ TensorFlow Lite จะให้ความสำคัญกับเครื่องมือและเคอร์เนลเป็นหลักสำหรับ int8 การวัดปริมาณสำหรับ 8 บิต เพื่อความสะดวกของการหาปริมาณแบบสมมาตรที่แสดงด้วยค่า 0 จุดเท่ากับ 0 นอกจากนี้ แบ็กเอนด์จํานวนมากมีการเพิ่มประสิทธิภาพเพิ่มเติมสําหรับการสะสม int8xint8 ด้วย

ต่อแกนเทียบกับต่อ tensor

การวัดปริมาณต่อ tensor หมายความว่าจะมี 1 สเกลและ/หรือ 0 พอยต์ต่อ tensor ทั้งหมด การหาปริมาณตามแกนหมายความว่าจะมี 1 สเกลและ/หรือ zero_point ต่อส่วนแบ่งใน quantized_dimension มิติข้อมูลที่วัดปริมาณเป็นขนาดจะระบุมิติของรูปร่างของ Tensor ที่สเกลและจุดศูนย์ตรงกับค่า ตัวอย่างเช่น Tensor 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 ด้วย

สมมาตรกับอสมมาตร

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

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

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

$A$ คือเมทริกซ์ $m \times n$ ของการเปิดใช้งานเชิงปริมาณ
$B$ คือเมทริกซ์ p $ของ$ n \times ของน้ำหนักเชิงปริมาณ
ลองคูณแถว $j$th ของ $A$, $a_j$ ด้วยคอลัมน์ $k$th ของ $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 quantized

เราอธิบายข้อกำหนดเกี่ยวกับการวัดปริมาณสำหรับเคอร์เนล 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