নিম্নলিখিত নথিতে TensorFlow Lite-এর 8-বিট কোয়ান্টাইজেশন স্কিমের স্পেসিফিকেশনের রূপরেখা দেওয়া হয়েছে। এটি হার্ডওয়্যার ডেভেলপারদের কোয়ান্টাইজড টেনসরফ্লো লাইট মডেলের অনুমানের জন্য হার্ডওয়্যার সমর্থন প্রদানে সহায়তা করার উদ্দেশ্যে।
স্পেসিফিকেশন সারাংশ
আমরা একটি স্পেসিফিকেশন প্রদান করছি, এবং আমরা শুধুমাত্র আচরণের কিছু গ্যারান্টি দিতে পারি যদি স্পেসিফিকেশন অনুসরণ করা হয়। আমরা এটাও বুঝি যে বিভিন্ন হার্ডওয়্যারের পছন্দ এবং বিধিনিষেধ থাকতে পারে যা স্পেক প্রয়োগ করার সময় সামান্য বিচ্যুতি ঘটাতে পারে যার ফলস্বরূপ বাস্তবায়নগুলি বিট-সঠিক নয়। যদিও এটি বেশিরভাগ ক্ষেত্রেই গ্রহণযোগ্য হতে পারে (এবং আমরা পরীক্ষাগুলির একটি স্যুট প্রদান করব যা আমাদের সর্বোত্তম জ্ঞানের মধ্যে রয়েছে প্রতি-অপারেশন সহনশীলতা যা আমরা বিভিন্ন মডেল থেকে সংগ্রহ করেছি), মেশিন লার্নিংয়ের প্রকৃতি (এবং সবচেয়ে সাধারণ ক্ষেত্রে গভীর শিক্ষা) ক্ষেত্রে) কোনো কঠিন গ্যারান্টি প্রদান করা অসম্ভব করে তোলে।
8-বিট কোয়ান্টাইজেশন নিম্নলিখিত সূত্র ব্যবহার করে ভাসমান বিন্দু মান আনুমানিক।
\[real\_value = (int8\_value - zero\_point) \times scale\]
প্রতি-অক্ষ (ওরফে প্রতি-চ্যানেল কনভ অপস) বা প্রতি-টেনসর ওজনগুলিকে int8
দুই-এর পরিপূরক মান দ্বারা উপস্থাপিত করা হয় [-127, 127]
পরিসরে শূন্য-বিন্দু 0 এর সমান। প্রতি-টেনসর অ্যাক্টিভেশন/ইনপুটগুলি দ্বারা প্রতিনিধিত্ব করা হয় পরিসরে int8
দুই এর পরিপূরক মান [-128, 127]
, পরিসরে একটি শূন্য-বিন্দু সহ [-128, 127]
।
নিচে নথিভুক্ত করা নির্দিষ্ট অপারেশনের জন্য অন্যান্য ব্যতিক্রম আছে।
স্বাক্ষরিত পূর্ণসংখ্যা বনাম স্বাক্ষরবিহীন পূর্ণসংখ্যা
TensorFlow Lite কোয়ান্টাইজেশন প্রাথমিকভাবে 8-বিটের জন্য int8
কোয়ান্টাইজেশনের জন্য টুলিং এবং কার্নেলকে অগ্রাধিকার দেবে। এটি 0-এর সমান শূন্য-পয়েন্ট দ্বারা উপস্থাপিত প্রতিসম কোয়ান্টাইজেশনের সুবিধার জন্য। উপরন্তু অনেক ব্যাকএন্ডে int8xint8
সঞ্চয়ের জন্য অতিরিক্ত অপ্টিমাইজেশন রয়েছে।
প্রতি-অক্ষ বনাম প্রতি-টেনসর
প্রতি-টেনসর কোয়ান্টাইজেশনের অর্থ হল প্রতি টেনসরে একটি স্কেল এবং/অথবা শূন্য-পয়েন্ট থাকবে। প্রতি-অক্ষের পরিমাপকরণের অর্থ হল quantized_dimension
এ প্রতি স্লাইসে একটি স্কেল এবং/অথবা zero_point
থাকবে। পরিমাপকৃত মাত্রা টেনসরের আকৃতির মাত্রা নির্দিষ্ট করে যা দাঁড়িপাল্লা এবং শূন্য-পয়েন্টগুলির সাথে সামঞ্জস্যপূর্ণ। উদাহরণস্বরূপ, একটি টেনসর 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 প্রয়োগ করে আমরা এই খরচ এড়াতে পারি।
গণিতের ব্যাখ্যা: এটি arXiv:1712.05877- এর অধ্যায় 2.3-এর অনুরূপ, এই পার্থক্য ব্যতীত যে আমরা স্কেলের মানগুলিকে প্রতি-অক্ষে হতে দিই। এটি সহজেই সাধারণীকরণ করে, নিম্নরূপ:
\(A\) হল কোয়ান্টাইজড অ্যাক্টিভেশনের একটি \(m \times n\) ম্যাট্রিক্স।
\(B\) হল কোয়ান্টাইজড ওজনের একটি \(n \times p\) ম্যাট্রিক্স।
l10n- \(A\) \(j\)তম সারি , \(a_j\) l10n\(B\)এর \(k\)তম কলাম দ্বারা \(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