מפרט קוונטיזציה של TensorFlow Lite 8 סיביות

קל לארגן דפים בעזרת אוספים אפשר לשמור ולסווג תוכן על סמך ההעדפות שלך.

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

סיכום מפרט

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

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

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

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

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

מספר שלם חתום מול מספר שלם ללא סימן

קוונטיזציה TensorFlow לייט יהיה נוסע לתעדף בעיקר גרעינים עבור int8 קוונטיזציה עבור 8 סיביות. זהו לנוחיות קוונטיזציה סימטרית מיוצג על ידי אפס נקודות שווה ל 0. בנוסף יש הרבה Backends אופטימיזציות נוספות עבור 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 של משקולות של פיתולים, אבל בתיאוריה זה יכול להיות מימד שמתאים לכל נקודה-מוצר ביישום הקרנל, המאפשר גרעיניות קוונטיזציה יותר בלי השלכות הביצועים. יש לזה שיפורים גדולים לדיוק.

ל-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\), הן באורך \(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-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