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