این سند طرح نسخهسازی عملیاتی LiteRT را شرح میدهد. نسخهسازی Op به توسعهدهندگان امکان میدهد تا عملکردها و پارامترهای جدیدی را به عملیاتهای موجود اضافه کنند. علاوه بر این موارد زیر را تضمین می کند:
- سازگاری به عقب: اجرای جدید LiteRT باید یک فایل مدل قدیمی را مدیریت کند.
- سازگاری پیشرو: پیاده سازی قدیمی LiteRT باید یک فایل مدل جدید تولید شده توسط نسخه جدید مبدل را مدیریت کند، تا زمانی که هیچ ویژگی جدیدی استفاده نشود.
- تشخیص ناسازگاری پیشرو: اگر یک پیادهسازی قدیمی LiteRT مدل جدیدی را میخواند که حاوی نسخه جدیدی از یک عملیات است که پشتیبانی نمیشود، باید خطا را گزارش کند.
مثال: افزودن اتساع به پیچیدگی عمقی
بقیه این سند با نشان دادن نحوه افزودن پارامترهای اتساع به عملیات پیچش عمقی، نسخهسازی عملیات در TFLite را توضیح میدهد.
برای درک این سند نیازی به دانستن اتساع نیست. توجه داشته باشید که:
- 2 پارامتر عدد صحیح جدید اضافه خواهد شد:
dilation_width_factor
وdilation_height_factor
. - هستههای کانولوشن عمیق قدیمی که از اتساع پشتیبانی نمیکنند، معادل تنظیم فاکتورهای اتساع روی ۱ هستند.
تغییر طرح FlatBuffer
برای افزودن پارامترهای جدید به یک op، جدول گزینه ها را در 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;
}
در اینجا نیازی به بررسی نسخه op نیست. هنگامی که پیاده سازی جدید یک فایل مدل قدیمی را می خواند که در آن فاکتورهای اتساع وجود ندارد، از 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
ثبت شده است. در این مثال، ما یک هسته عملیات جدید را پیاده سازی کردیم که می تواند DepthwiseConv2D
نسخه 1 و 2 را مدیریت کند، بنابراین باید این خط را تغییر دهیم:
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 op را تغییر دهید
گام بعدی این است که TFLite حداقل نسخه مورد نیاز برای اجرای عملیات را پر کند. در این مثال به این معناست:
- وقتی فاکتورهای اتساع همه 1 هستند، نسخه=1 را پر کنید.
- در غیر این صورت نسخه=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%
با نسخه زمان اجرا فعلی تعریف شده در tensorflow/core/public/version.h مطابقت دارد.
اجرای تفویض اختیار
LiteRT یک API تفویض اختیار را ارائه می دهد که امکان تفویض عملیات به پشتیبان های سخت افزاری را فراهم می کند. در تابع Prepare
نماینده، بررسی کنید که آیا نسخه برای هر گره در کد Delegation پشتیبانی میشود یا خیر.
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 عملیات پشتیبانی کند، لازم است، بنابراین نمایندگی می تواند هنگام دریافت نسخه بالاتر، ناسازگاری را تشخیص دهد.