מפרט כימות של 8 סיביות של TensorFlow Lite

המסמך הבא מתאר את המפרט עבור תוכנית הכימות של 8 סיביות של TensorFlow Lite. מטרה זו נועדה לסייע למפתחי חומרה במתן תמיכה בחומרה להסקת דגמי TensorFlow Lite בכמות.

סיכום מפרט

אנו מספקים מפרט ויכולים להבטיח התנהגות מסוימת רק אם המעקב אחר המפרט. אנו גם מבינים שלחומרה שונה עשויות להיות העדפות והגבלות העלולות לגרום לסטיות קלות בעת יישום המפרט המביאות ליישומים שאינם מדויקים מעט. בעוד שזה עשוי להיות מקובל ברוב המקרים (ואנו נספק מערך מבחנים שלמיטב ידיעתנו כוללים סובלנות לכל פעולה שאספנו ממספר מודלים), אופי הלמידה במכונה (ולמידה עמוקה בשכיחות מקרה) אינו מאפשר לספק ערבויות קשות.

כימות של 8 סיביות מתקרבת לערכי נקודה צפה בעזרת הנוסחה הבאה.

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

לכל ציר (aka לכל ערוץ ב- Conv ops) או משקולות פר טנסור מיוצגים על ידי ערכי השלמה של int8 שניים בטווח [-127, 127] עם נקודת אפס שווה ל- 0. הפעלות / כניסות לכל טנסור מיוצגות על ידי ערכי השלמה של int8 two בטווח [-128, 127] , עם נקודת אפס בטווח [-128, 127] .

ישנם חריגים אחרים לפעולות מסוימות המתועדים להלן.

מספר שלם חתום לעומת מספר שלם לא חתום

כימות TensorFlow Lite תעדף בעיקר כלים וגרעינים לכימות int8 עבור 8 סיביות. זה לנוחיות הכימות הסימטרי המיוצג על ידי נקודת אפס שווה ל- 0. בנוסף, int8xint8 רבים יש אופטימיזציות נוספות להצטברות int8xint8 .

לכל ציר לעומת פר טנסור

כימות פר טנסור פירושו שתהיה סולם אחד ו / או נקודת אפס לכל טנסור שלם. כימות לכל ציר פירושו שיהיו קנה מידה אחד ו / או zero_point לכל פרוסה zero_point quantized_dimension . המימד הכמותי מציין את ממד הצורה של הטנסור אליו קשקשים המאזניים ונקודות האפס. לדוגמא, טנסור t , עם dims=[4, 3, 2, 1] עם params כימות: 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 של המשקולות של פיתולים, אבל בתאוריה זה יכול להיות הממד שמתאים לכל נקודה-מוצר ביישום הקרנל, המאפשר גרעיני קוונטיזציה יותר בלי שלכות ביצועים. יש לכך שיפורים גדולים לדיוק.

ל- TFLite יש תמיכה לכל ציר במספר גדל והולך של פעולות. בזמן מסמך זה קיימת תמיכה ב- Conv2d ו- DepthwiseConv2d.

סימטרי לעומת אסימטרי

ההפעלות אינן סימטריות: הן יכולות להיות בנקודת האפס שלהן בכל מקום בטווח int8 החתום [-128, 127] . הפעלות רבות הן בעלות אופי אסימטרי ונקודת אפס היא דרך זולה יחסית לעלות ביעילות לדיוק בינארי נוסף. מכיוון שהפעלות מוכפלות רק במשקולות קבועות, ניתן למטב את די נקודת האפס הקבועה די בכבדות.

המשקלים הם סימטריים: נאלצים להיות בעלי נקודת אפס השווה ל 0. ערכי המשקל מוכפלים בערכי קלט והפעלה דינמיים. המשמעות היא שיש עלות זמן ריצה בלתי נמנעת של הכפלת נקודת האפס של המשקל עם ערך ההפעלה. על ידי אכיפת נקודת אפס היא 0 אנו יכולים להימנע מעלות זו.

הסבר למתמטיקה: זה דומה לסעיף 2.3 ב- arXiv: 1712.05877 , למעט ההבדל שאנו מאפשרים לערכי הסולם להיות לכל ציר. זה מוכלל בקלות באופן הבא:

$ A $ הוא מטריצה ​​של $ m \ times n $ של הפעלות כמותיות.
$ B $ הוא מטריצת $ n \ פעמים p $ של משקולות מכמות.
שקול להכפיל את השורה $ 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

להלן אנו מתארים את דרישות הכימות עבור גרעיני 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-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

הפניות

arXiv: 1712.05877