مشخصات کوانتیزاسیون 8 بیتی TensorFlow Lite

سند زیر مشخصات طرح کوانتیزاسیون 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] .

استثناهای دیگری برای عملیات خاص وجود دارد که در زیر مستند شده است.

عدد صحیح امضا شده در مقابل عدد صحیح بدون علامت

Quantization TensorFlow Lite در درجه اول ابزارها و هسته ها را برای کوانتیشن 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 در عرض کوانتیزه می شود. بعد دوم 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-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