يوضّح هذا المستند مخطط تحديد إصدارات العمليات في LiteRT. تتيح ميزة تحديد إصدارات العمليات للمطوّرين إضافة وظائف ومعلَمات جديدة إلى العمليات الحالية. بالإضافة إلى ذلك، يضمن ما يلي:
- التوافق مع الإصدارات السابقة: يجب أن يتمكّن تطبيق LiteRT الجديد من التعامل مع ملف نموذج قديم.
- التوافق مع الإصدارات الأحدث: يجب أن يكون التنفيذ القديم لـ LiteRT قادرًا على التعامل مع ملف نموذج جديد تم إنشاؤه بواسطة إصدار جديد من المحوّل، طالما لم يتم استخدام أي ميزات جديدة.
- رصد عدم التوافق مع الإصدارات الأحدث: إذا كان تطبيق قديم من LiteRT يقرأ نموذجًا جديدًا يحتوي على إصدار جديد من عملية غير متوافقة، يجب أن يُبلغ عن الخطأ.
مثال: إضافة التوسّع إلى الالتفاف العميق
توضّح بقية هذا المستند عملية تحديد إصدارات العمليات في TFLite من خلال عرض كيفية إضافة مَعلمات التمدّد إلى عملية الالتفاف العميق.
لا تحتاج إلى معرفة التمدد لفهم هذا المستند. يُرجى العِلم بما يلي:
- ستتم إضافة مَعلمتَين جديدتَين للأعداد الصحيحة:
dilation_width_factorوdilation_height_factor. - إنّ نوى الالتفاف العميق القديمة التي لا تتوافق مع التمدد تكون مكافئة لضبط عوامل التمدد على 1.
تغيير مخطط FlatBuffer
لإضافة مَعلمات جديدة إلى عملية، غيِّر جدول الخيارات في
lite/schema/schema.fbs.
على سبيل المثال، يبدو جدول الخيارات الخاص بالالتفاف العميق كما يلي:
table DepthwiseConv2DOptions {
padding:Padding;
stride_w:int;
stride_h:int;
depth_multiplier:int;
fused_activation_function:ActivationFunctionType;
}
عند إضافة مَعلمات جديدة:
- أضِف تعليقات توضّح المعلمات التي يتوافق معها كل إصدار.
- عندما يحصل التنفيذ الجديد على القيم التلقائية للمعلمات المضافة حديثًا، من المفترض أن يعمل تمامًا مثل التنفيذ القديم.
سيظهر الجدول على النحو التالي بعد إضافة المَعلمات الجديدة:
table DepthwiseConv2DOptions {
// Parameters for DepthwiseConv version 1 or above.
padding:Padding;
stride_w:int;
stride_h:int;
depth_multiplier:int;
fused_activation_function:ActivationFunctionType;
// Parameters for DepthwiseConv version 2 or above.
dilation_w_factor:int = 1;
dilation_h_factor:int = 1;
}
يجب إعادة إنشاء الملف lite/schema/schema_generated.h باستخدام المخطط الجديد.
تغيير بنى C وتنفيذ النواة
في LiteRT، يتم فصل تنفيذ النواة عن تعريف FlatBuffer.
تقرأ النواة المَعلمة من بنى C المحدّدة في lite/c/builtin_op_data.h.
في ما يلي مَعلمة الالتفاف الأصلي القابل للفصل حسب العمق:
typedef struct {
TfLitePadding padding;
int stride_width;
int stride_height;
int depth_multiplier;
TfLiteFusedActivation activation;
} TfLiteDepthwiseConvParams;
كما هو الحال مع مخطط FlatBuffer، أضِف تعليقات تشير إلى المَعلمات المتوافقة مع كل إصدار. تظهر النتيجة أدناه:
typedef struct {
// Parameters for DepthwiseConv version 1 or above.
TfLitePadding padding;
int stride_width;
int stride_height;
int depth_multiplier;
TfLiteFusedActivation activation;
// Parameters for DepthwiseConv version 2 or above.
int dilation_width_factor;
int dilation_height_factor;
} TfLiteDepthwiseConvParams;
يُرجى أيضًا تغيير تنفيذ النواة لقراءة المَعلمات المضافة حديثًا من بنى C. تم حذف التفاصيل هنا.
تغيير رمز قراءة FlatBuffer
تتوفّر منطق قراءة FlatBuffer وإنتاج بنية C في
lite/core/api/flatbuffer_conversions.cc.
عدِّل الملف للتعامل مع المَعلمات الجديدة، كما هو موضّح أدناه:
TfLiteStatus ParseDepthwiseConv2D(const Operator* op,
ErrorReporter* error_reporter,
BuiltinDataAllocator* allocator,
void** builtin_data) {
CheckParsePointerParams(op, error_reporter, allocator, builtin_data);
SafeBuiltinDataAllocator safe_allocator(allocator);
std::unique_ptr<TfLiteDepthwiseConvParams,
SafeBuiltinDataAllocator::BuiltinDataDeleter>
params = safe_allocator.Allocate<TfLiteDepthwiseConvParams>();
TF_LITE_ENSURE(error_reporter, params != nullptr);
const DepthwiseConv2DOptions* schema_params =
op->builtin_options_as_DepthwiseConv2DOptions();
if (schema_params != nullptr) {
params->padding = ConvertPadding(schema_params->padding());
params->stride_width = schema_params->stride_w();
params->stride_height = schema_params->stride_h();
params->depth_multiplier = schema_params->depth_multiplier();
params->activation =
ConvertActivation(schema_params->fused_activation_function());
params->dilation_width_factor = schema_params->dilation_w_factor();
params->dilation_height_factor = schema_params->dilation_h_factor();
}
*builtin_data = params.release();
return kTfLiteOk;
}
ليس مطلوبًا التحقّق من إصدار نظام التشغيل هنا. عندما يقرأ التنفيذ الجديد ملف نموذج قديمًا لا تتوفّر فيه عوامل التمديد، سيستخدم القيمة 1 كقيمة تلقائية، وسيعمل النواة الجديدة بشكل متوافق مع النواة القديمة.
تغيير تسجيل النواة
يوفر MutableOpResolver (المحدّد في lite/mutable_op_resolver.h) بعض الدوال لتسجيل نواة العمليات. الإصدار الأدنى والأقصى هما 1 تلقائيًا:
void AddBuiltin(tflite::BuiltinOperator op, TfLiteRegistration* registration,
int min_version = 1, int max_version = 1);
void AddCustom(const char* name, TfLiteRegistration* registration,
int min_version = 1, int max_version = 1);
يتم تسجيل العمليات المضمّنة في lite/kernels/register.cc. في هذا المثال،
نفّذنا نواة عملية جديدة يمكنها التعامل مع الإصدارين 1 و2 من DepthwiseConv2D،
لذا علينا تغيير هذا السطر:
AddBuiltin(BuiltinOperator_DEPTHWISE_CONV_2D, Register_DEPTHWISE_CONV_2D());
إلى:
AddBuiltin(BuiltinOperator_DEPTHWISE_CONV_2D, Register_DEPTHWISE_CONV_2D(),
/* min_version = */ 1,
/* max_version = */ 2);
تغيير إصدار عملية TFLite
الخطوة التالية هي أن يملأ TFLite الحد الأدنى من الإصدار المطلوب لتنفيذ العملية. في هذا المثال، يعني ذلك:
- املأ الحقل version=1 عندما تكون جميع عوامل التوسيع 1.
- املأ version=2 في الحالات الأخرى.
عدِّل الدالة GetBuiltinOperatorVersion للمشغّل في lite/tools/versioning/op_version.cc من خلال إضافة الإصدار الجديد إلى حالة DepthwiseConv2D:
case BuiltinOperator_DEPTHWISE_CONV_2D:
auto depthwise_conv_params =
reinterpret_cast<TfLiteDepthwiseConvParams*>(op_sig.builtin_data);
TFLITE_DCHECK(depthwise_conv_params != nullptr);
if (depthwise_conv_params->dilation_width_factor != 1 ||
depthwise_conv_params->dilation_height_factor != 1) {
return 2;
}
return 1;
تعديل خريطة إصدار المشغّل
الخطوة الأخيرة هي إضافة معلومات الإصدار الجديد إلى خريطة إصدار المشغّل. هذه الخطوة مطلوبة لأنّنا نحتاج إلى إنشاء الحد الأدنى من إصدار وقت التشغيل المطلوب للنموذج استنادًا إلى خريطة الإصدار هذه.
لإجراء ذلك، عليك إضافة إدخال خريطة جديد في
lite/tools/versioning/runtime_version.cc.
في هذا المثال، عليك إضافة الإدخال التالي إلى op_version_map:
{ {BuiltinOperator_DEPTHWISE_CONV_2D, 2}, %CURRENT_RUNTIME_VERSION%}
حيث يمثل %CURRENT_RUNTIME_VERSION% إصدار وقت التشغيل الحالي
المحدّد في release_version.h.
تنفيذ التفويض
توفّر LiteRT واجهة برمجة تطبيقات تفويض تتيح تفويض العمليات إلى الخلفيات المستندة إلى الأجهزة. في الدالة Prepare الخاصة بالكائن الوكيل، تحقَّق مما إذا كان الإصدار متوافقًا مع كل عقدة في رمز التفويض.
const int kMaxVersion = 1;
TfLiteNode* node;
TfLiteRegistration* registration = nullptr;
TF_LITE_ENSURE_STATUS(context->GetNodeAndRegistration(context, node_index, &node, ®istration));
if (registration->version > kMaxVersion) {
// Reject the node if the version isn't supported.
}
هذا الإجراء مطلوب حتى إذا كانت عملية التفويض لا تتيح سوى عمليات الإصدار 1، وبالتالي يمكن أن ترصد عملية التفويض عدم التوافق عند الحصول على عملية إصدار أعلى.