يوضّح المستند التالي مواصفات نظام التكميم 8 بت في LiteRT. يهدف ذلك إلى مساعدة مطوّري الأجهزة في توفير دعم للأجهزة من أجل الاستدلال باستخدام نماذج LiteRT المكمَّمة.
ملخّص المواصفات
نحن نقدّم مواصفات، ولا يمكننا تقديم بعض الضمانات بشأن السلوك إلا إذا تم اتّباع المواصفات. ندرك أيضًا أنّ الأجهزة المختلفة قد تتضمّن إعدادات مفضّلة وقيودًا قد تؤدي إلى حدوث انحرافات طفيفة عند تنفيذ المواصفات، ما يؤدي إلى عمليات تنفيذ غير متطابقة تمامًا. مع أنّ ذلك قد يكون مقبولاً في معظم الحالات (وسنقدّم مجموعة من الاختبارات التي تتضمّن، حسب علمنا، حدودًا مسموحًا بها لكل عملية جمعناها من عدّة نماذج)، إلا أنّ طبيعة تعلُّم الآلة (والتعلُّم العميق في معظم الحالات) تجعل من المستحيل تقديم أي ضمانات قاطعة.
يتم تقريب قيم النقطة العائمة باستخدام صيغة التكميم ذات 8 بتات التالية.
\[real\_value = (int8\_value - zero\_point) \times scale\]
يتم تمثيل الأوزان لكل محور (المعروف أيضًا باسم لكل قناة في عمليات Conv) أو لكل موتر بقيم int8 المكمل الثنائي في النطاق [-127, 127] مع نقطة صفرية تساوي 0. يتم تمثيل عمليات التنشيط/الإدخال لكل موتر باستخدام قيم int8 المكمل الثنائي
في النطاق [-128, 127]، مع نقطة صفرية في النطاق [-128, 127].
تتوفّر استثناءات أخرى لعمليات معيّنة موضّحة أدناه.
العدد الصحيح الموقّع مقابل العدد الصحيح غير الموقّع
ستمنح عملية تحديد الكميات في LiteRT الأولوية بشكل أساسي للأدوات والنواة من أجل int8
تحديد الكميات بمقدار 8 بت. هذا لتسهيل تمثيل التكميم المتماثل بنقطة صفر تساوي 0. بالإضافة إلى ذلك، تتضمّن العديد من الأنظمة الخلفية تحسينات إضافية لتجميع int8xint8.
لكل محور مقابل لكل موتر
يعني التكميم على مستوى كل موتر أنّه سيكون هناك مقياس و/أو نقطة صفرية لكل الموتر. يعني التكميم لكل محور أنّه سيكون هناك مقياس واحد و/أو
zero_point لكل شريحة في quantized_dimension. تحدّد السمة "البُعد الكمّي" بُعد شكل Tensor الذي تتوافق معه المقاييس ونقاط الصفر. على سبيل المثال، سيتم تحديد كمية الموتر 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، يمكننا تجنُّب هذه التكلفة.
توضيح للعمليات الحسابية: هذا الإجراء مشابه للفقرة 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