نماینده TensorFlow Lite چیست؟
یک TensorFlow Lite Delegate به شما امکان میدهد مدلهای خود را (قطع یا کل) روی یک اجراکننده دیگر اجرا کنید. این مکانیسم می تواند از انواع شتاب دهنده های روی دستگاه مانند GPU یا Edge TPU (واحد پردازش تانسور) برای استنتاج استفاده کند. این به توسعه دهندگان یک روش منعطف و جدا شده از TFLite پیش فرض برای سرعت بخشیدن به استنتاج ارائه می دهد.
نمودار زیر نمایندگان را خلاصه می کند، جزئیات بیشتر در بخش های زیر.
چه زمانی باید یک نماینده سفارشی ایجاد کنم؟
TensorFlow Lite دارای طیف گسترده ای از نمایندگان برای شتاب دهنده های هدف مانند GPU، DSP، EdgeTPU و چارچوب هایی مانند Android NNAPI است.
ایجاد نماینده خود در سناریوهای زیر مفید است:
- شما می خواهید یک موتور استنتاج ML جدید را که توسط هیچ نماینده موجود پشتیبانی نمی شود ادغام کنید.
- شما یک شتاب دهنده سخت افزاری سفارشی دارید که زمان اجرا را برای سناریوهای شناخته شده بهبود می بخشد.
- شما در حال توسعه بهینه سازی های CPU (مانند فیوزینگ اپراتور) هستید که می تواند سرعت مدل های خاصی را افزایش دهد.
نمایندگان چگونه کار می کنند؟
یک نمودار مدل ساده مانند شکل زیر و یک نماینده "MyDelegate" را در نظر بگیرید که اجرای سریع تری برای عملیات Conv2D و Mean دارد.
پس از اعمال این "MyDelegate"، نمودار اصلی TensorFlow Lite مانند زیر به روز می شود:
نمودار بالا زمانی به دست می آید که TensorFlow Lite نمودار اصلی را زیر دو قانون تقسیم می کند:
- عملیات خاصی که می تواند توسط نماینده انجام شود در یک پارتیشن قرار می گیرند در حالی که هنوز وابستگی های اولیه گردش کار محاسباتی را در بین عملیات برآورده می کنند.
- هر پارتیشن قابل واگذاری فقط دارای گره های ورودی و خروجی است که توسط نماینده مدیریت نمی شود.
هر پارتیشنی که توسط یک نماینده اداره می شود با یک گره نماینده (همچنین می تواند به عنوان هسته نماینده فراخوانی شود) در گراف اصلی که پارتیشن را در فراخوانی فراخوانی آن ارزیابی می کند، جایگزین می شود.
بسته به مدل، نمودار نهایی می تواند به یک یا چند گره ختم شود، که دومی به این معنی است که برخی از عملیات ها توسط نماینده پشتیبانی نمی شوند. به طور کلی، شما نمی خواهید چندین پارتیشن توسط نماینده مدیریت شود، زیرا هر بار که از نماینده به نمودار اصلی تغییر می کنید، هزینه ای برای ارسال نتایج از زیرگراف واگذار شده به گراف اصلی وجود دارد که به دلیل حافظه ایجاد می شود. کپی (به عنوان مثال، GPU به CPU). چنین سرباری ممکن است افزایش عملکرد را به ویژه زمانی که مقدار زیادی کپی حافظه وجود دارد جبران کند.
پیاده سازی نماینده سفارشی خود
روش ترجیحی برای افزودن نماینده استفاده از SimpleDelegate API است.
برای ایجاد یک نماینده جدید، باید 2 رابط را پیاده سازی کنید و پیاده سازی خود را برای متدهای رابط ارائه دهید.
1 - SimpleDelegateInterface
این کلاس نشان دهنده قابلیت های نماینده است که عملیات پشتیبانی می شود، و یک کلاس کارخانه برای ایجاد یک هسته که گراف واگذار شده را محصور می کند. برای جزئیات بیشتر، رابط تعریف شده در این فایل هدر C++ را ببینید. نظرات موجود در کد هر API را با جزئیات توضیح می دهد.
2 - SimpleDelegateKernelInterface
این کلاس منطق اولیه سازی / آماده سازی / و اجرای پارتیشن تفویض شده را محصور می کند.
دارای: (به تعریف مراجعه کنید)
- Init(...): که یک بار برای انجام هر مقدار اولیه یک بار فراخوانی می شود.
- Prepare(...): برای هر نمونه متفاوت از این گره فراخوانی می شود - اگر چندین پارتیشن تفویض شده داشته باشید این اتفاق می افتد. معمولاً میخواهید تخصیص حافظه را در اینجا انجام دهید، زیرا هر بار که اندازه تانسورها تغییر میکنند، این نام خوانده میشود.
- Invoke(...): که برای استنباط فراخوانی خواهد شد.
مثال
در این مثال، شما یک نماینده بسیار ساده ایجاد می کنید که می تواند تنها 2 نوع عملیات (ADD) و (SUB) را فقط با تانسورهای float32 پشتیبانی کند.
// MyDelegate implements the interface of SimpleDelegateInterface.
// This holds the Delegate capabilities.
class MyDelegate : public SimpleDelegateInterface {
public:
bool IsNodeSupportedByDelegate(const TfLiteRegistration* registration,
const TfLiteNode* node,
TfLiteContext* context) const override {
// Only supports Add and Sub ops.
if (kTfLiteBuiltinAdd != registration->builtin_code &&
kTfLiteBuiltinSub != registration->builtin_code)
return false;
// This delegate only supports float32 types.
for (int i = 0; i < node->inputs->size; ++i) {
auto& tensor = context->tensors[node->inputs->data[i]];
if (tensor.type != kTfLiteFloat32) return false;
}
return true;
}
TfLiteStatus Initialize(TfLiteContext* context) override { return kTfLiteOk; }
const char* Name() const override {
static constexpr char kName[] = "MyDelegate";
return kName;
}
std::unique_ptr<SimpleDelegateKernelInterface> CreateDelegateKernelInterface()
override {
return std::make_unique<MyDelegateKernel>();
}
};
سپس، با به ارث بردن از SimpleDelegateKernelInterface
، هسته نماینده خود را ایجاد کنید.
// My delegate kernel.
class MyDelegateKernel : public SimpleDelegateKernelInterface {
public:
TfLiteStatus Init(TfLiteContext* context,
const TfLiteDelegateParams* params) override {
// Save index to all nodes which are part of this delegate.
inputs_.resize(params->nodes_to_replace->size);
outputs_.resize(params->nodes_to_replace->size);
builtin_code_.resize(params->nodes_to_replace->size);
for (int i = 0; i < params->nodes_to_replace->size; ++i) {
const int node_index = params->nodes_to_replace->data[i];
// Get this node information.
TfLiteNode* delegated_node = nullptr;
TfLiteRegistration* delegated_node_registration = nullptr;
TF_LITE_ENSURE_EQ(
context,
context->GetNodeAndRegistration(context, node_index, &delegated_node,
&delegated_node_registration),
kTfLiteOk);
inputs_[i].push_back(delegated_node->inputs->data[0]);
inputs_[i].push_back(delegated_node->inputs->data[1]);
outputs_[i].push_back(delegated_node->outputs->data[0]);
builtin_code_[i] = delegated_node_registration->builtin_code;
}
return kTfLiteOk;
}
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) override {
return kTfLiteOk;
}
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) override {
// Evaluate the delegated graph.
// Here we loop over all the delegated nodes.
// We know that all the nodes are either ADD or SUB operations and the
// number of nodes equals ''inputs_.size()'' and inputs[i] is a list of
// tensor indices for inputs to node ''i'', while outputs_[i] is the list of
// outputs for node
// ''i''. Note, that it is intentional we have simple implementation as this
// is for demonstration.
for (int i = 0; i < inputs_.size(); ++i) {
// Get the node input tensors.
// Add/Sub operation accepts 2 inputs.
auto& input_tensor_1 = context->tensors[inputs_[i][0]];
auto& input_tensor_2 = context->tensors[inputs_[i][1]];
auto& output_tensor = context->tensors[outputs_[i][0]];
TF_LITE_ENSURE_EQ(
context,
ComputeResult(context, builtin_code_[i], &input_tensor_1,
&input_tensor_2, &output_tensor),
kTfLiteOk);
}
return kTfLiteOk;
}
private:
// Computes the result of addition of 'input_tensor_1' and 'input_tensor_2'
// and store the result in 'output_tensor'.
TfLiteStatus ComputeResult(TfLiteContext* context, int builtin_code,
const TfLiteTensor* input_tensor_1,
const TfLiteTensor* input_tensor_2,
TfLiteTensor* output_tensor) {
if (NumElements(input_tensor_1) != NumElements(input_tensor_2) ||
NumElements(input_tensor_1) != NumElements(output_tensor)) {
return kTfLiteDelegateError;
}
// This code assumes no activation, and no broadcasting needed (both inputs
// have the same size).
auto* input_1 = GetTensorData<float>(input_tensor_1);
auto* input_2 = GetTensorData<float>(input_tensor_2);
auto* output = GetTensorData<float>(output_tensor);
for (int i = 0; i < NumElements(input_tensor_1); ++i) {
if (builtin_code == kTfLiteBuiltinAdd)
output[i] = input_1[i] + input_2[i];
else
output[i] = input_1[i] - input_2[i];
}
return kTfLiteOk;
}
// Holds the indices of the input/output tensors.
// inputs_[i] is list of all input tensors to node at index 'i'.
// outputs_[i] is list of all output tensors to node at index 'i'.
std::vector<std::vector<int>> inputs_, outputs_;
// Holds the builtin code of the ops.
// builtin_code_[i] is the type of node at index 'i'
std::vector<int> builtin_code_;
};
نماینده جدید را محک و ارزیابی کنید
TFLite مجموعه ای از ابزارها را دارد که می توانید به سرعت آنها را در برابر مدل TFLite آزمایش کنید.
- ابزار معیار مدل : این ابزار یک مدل TFLite می گیرد، ورودی های تصادفی تولید می کند و سپس مدل را برای تعداد مشخصی اجرا به طور مکرر اجرا می کند. آمار تأخیر جمعی را در پایان چاپ می کند.
- Inference Diff Tool : برای یک مدل معین، این ابزار داده های تصادفی گاوسی را تولید می کند و آن را از طریق دو مفسر TFLite مختلف، یکی از هسته CPU تک رشته ای اجرا می کند و دیگری با استفاده از مشخصات تعریف شده توسط کاربر. تفاوت مطلق بین تانسورهای خروجی از هر مفسر را بر اساس هر عنصر اندازه گیری می کند. این ابزار همچنین می تواند برای اشکال زدایی مشکلات دقت مفید باشد.
- همچنین ابزارهای ارزیابی ویژه ای برای طبقه بندی تصویر و تشخیص اشیا وجود دارد. این ابزارها را می توانید در اینجا پیدا کنید
علاوه بر این، TFLite مجموعه بزرگی از آزمایشهای هسته و واحد عملیاتی دارد که میتوانند برای آزمایش نماینده جدید با پوشش بیشتر و اطمینان از شکسته نشدن مسیر اجرای معمولی TFLite دوباره استفاده شوند.
برای دستیابی به استفاده مجدد از تست ها و ابزار TFLite برای نماینده جدید، می توانید از یکی از دو گزینه زیر استفاده کنید:
- از مکانیزم ثبت نماینده استفاده کنید.
- از مکانیسم نمایندگی خارجی استفاده کنید.
انتخاب بهترین رویکرد
هر دو رویکرد به چند تغییر نیاز دارند که در زیر توضیح داده شده است. با این حال، رویکرد اول نماینده را به صورت ایستا به هم مرتبط میکند و نیازمند بازسازی ابزارهای آزمایش، معیار و ارزیابی است. در مقابل، مورد دوم نماینده را به عنوان یک کتابخانه مشترک تبدیل میکند و از شما میخواهد که روشهای ایجاد/حذف از کتابخانه مشترک را در معرض نمایش بگذارید.
در نتیجه، مکانیسم نمایندگی خارجی با باینری های ابزار از پیش ساخته شده Tensorflow Lite TFLite کار می کند. اما کمتر صریح است و ممکن است تنظیم آن در تست های یکپارچه سازی خودکار پیچیده تر باشد. برای وضوح بهتر از رویکرد ثبت نماینده استفاده کنید.
گزینه 1: از ثبت کننده نمایندگی استفاده کنید
ثبت کننده نماینده لیستی از ارائه دهندگان نمایندگی را نگه می دارد، که هر یک از آنها راه آسانی برای ایجاد نمایندگان TFLite بر اساس پرچم های خط فرمان فراهم می کند، و از این رو برای ابزارسازی راحت هستند. برای وصل کردن نماینده جدید به همه ابزارهای Tensorflow Lite که در بالا ذکر شد، ابتدا یک ارائهدهنده نماینده جدید مانند این ایجاد میکنید و سپس تنها چند تغییر در قوانین BUILD ایجاد میکنید. یک مثال کامل از این فرآیند یکپارچه سازی در زیر نشان داده شده است (و کد را می توانید در اینجا پیدا کنید ).
با فرض اینکه شما نماینده ای دارید که API های SimpleDelegate را پیاده سازی می کند، و API های خارجی "C" برای ایجاد/حذف این نماینده "ساختگی" همانطور که در زیر نشان داده شده است:
// Returns default options for DummyDelegate.
DummyDelegateOptions TfLiteDummyDelegateOptionsDefault();
// Creates a new delegate instance that need to be destroyed with
// `TfLiteDummyDelegateDelete` when delegate is no longer used by TFLite.
// When `options` is set to `nullptr`, the above default values are used:
TfLiteDelegate* TfLiteDummyDelegateCreate(const DummyDelegateOptions* options);
// Destroys a delegate created with `TfLiteDummyDelegateCreate` call.
void TfLiteDummyDelegateDelete(TfLiteDelegate* delegate);
برای ادغام "DummyDelegate" با ابزار معیار و ابزار استنتاج، یک DelegateProvider مانند زیر تعریف کنید:
class DummyDelegateProvider : public DelegateProvider {
public:
DummyDelegateProvider() {
default_params_.AddParam("use_dummy_delegate",
ToolParam::Create<bool>(false));
}
std::vector<Flag> CreateFlags(ToolParams* params) const final;
void LogParams(const ToolParams& params) const final;
TfLiteDelegatePtr CreateTfLiteDelegate(const ToolParams& params) const final;
std::string GetName() const final { return "DummyDelegate"; }
};
REGISTER_DELEGATE_PROVIDER(DummyDelegateProvider);
std::vector<Flag> DummyDelegateProvider::CreateFlags(ToolParams* params) const {
std::vector<Flag> flags = {CreateFlag<bool>("use_dummy_delegate", params,
"use the dummy delegate.")};
return flags;
}
void DummyDelegateProvider::LogParams(const ToolParams& params) const {
TFLITE_LOG(INFO) << "Use dummy test delegate : ["
<< params.Get<bool>("use_dummy_delegate") << "]";
}
TfLiteDelegatePtr DummyDelegateProvider::CreateTfLiteDelegate(
const ToolParams& params) const {
if (params.Get<bool>("use_dummy_delegate")) {
auto default_options = TfLiteDummyDelegateOptionsDefault();
return TfLiteDummyDelegateCreateUnique(&default_options);
}
return TfLiteDelegatePtr(nullptr, [](TfLiteDelegate*) {});
}
تعاریف قانون BUILD مهم هستند زیرا باید مطمئن شوید که کتابخانه همیشه مرتبط است و توسط بهینه ساز حذف نمی شود.
#### The following are for using the dummy test delegate in TFLite tooling ####
cc_library(
name = "dummy_delegate_provider",
srcs = ["dummy_delegate_provider.cc"],
copts = tflite_copts(),
deps = [
":dummy_delegate",
"//tensorflow/lite/tools/delegates:delegate_provider_hdr",
],
alwayslink = 1, # This is required so the optimizer doesn't optimize the library away.
)
اکنون این دو قانون پوشش را در فایل BUILD خود اضافه کنید تا نسخهای از ابزار معیار و ابزار استنتاج و سایر ابزارهای ارزیابی را ایجاد کنید که میتواند با نماینده خودتان اجرا شود.
cc_binary(
name = "benchmark_model_plus_dummy_delegate",
copts = tflite_copts(),
linkopts = task_linkopts(),
deps = [
":dummy_delegate_provider",
"//tensorflow/lite/tools/benchmark:benchmark_model_main",
],
)
cc_binary(
name = "inference_diff_plus_dummy_delegate",
copts = tflite_copts(),
linkopts = task_linkopts(),
deps = [
":dummy_delegate_provider",
"//tensorflow/lite/tools/evaluation/tasks:task_executor_main",
"//tensorflow/lite/tools/evaluation/tasks/inference_diff:run_eval_lib",
],
)
cc_binary(
name = "imagenet_classification_eval_plus_dummy_delegate",
copts = tflite_copts(),
linkopts = task_linkopts(),
deps = [
":dummy_delegate_provider",
"//tensorflow/lite/tools/evaluation/tasks:task_executor_main",
"//tensorflow/lite/tools/evaluation/tasks/imagenet_image_classification:run_eval_lib",
],
)
cc_binary(
name = "coco_object_detection_eval_plus_dummy_delegate",
copts = tflite_copts(),
linkopts = task_linkopts(),
deps = [
":dummy_delegate_provider",
"//tensorflow/lite/tools/evaluation/tasks:task_executor_main",
"//tensorflow/lite/tools/evaluation/tasks/coco_object_detection:run_eval_lib",
],
)
همچنین میتوانید این ارائهدهنده نماینده را به آزمایشهای هسته TFLite وصل کنید همانطور که در اینجا توضیح داده شده است.
گزینه 2: از نماینده خارجی استفاده کنید
در این جایگزین، ابتدا مطابق شکل زیر یک آداپتور نماینده خارجی به نام external_delegate_adaptor.cc ایجاد می کنید. توجه داشته باشید، این رویکرد در مقایسه با گزینه 1 همانطور که قبلا ذکر شد کمی کمتر ترجیح داده می شود.
TfLiteDelegate* CreateDummyDelegateFromOptions(char** options_keys,
char** options_values,
size_t num_options) {
DummyDelegateOptions options = TfLiteDummyDelegateOptionsDefault();
// Parse key-values options to DummyDelegateOptions.
// You can achieve this by mimicking them as command-line flags.
std::unique_ptr<const char*> argv =
std::unique_ptr<const char*>(new const char*[num_options + 1]);
constexpr char kDummyDelegateParsing[] = "dummy_delegate_parsing";
argv.get()[0] = kDummyDelegateParsing;
std::vector<std::string> option_args;
option_args.reserve(num_options);
for (int i = 0; i < num_options; ++i) {
option_args.emplace_back("--");
option_args.rbegin()->append(options_keys[i]);
option_args.rbegin()->push_back('=');
option_args.rbegin()->append(options_values[i]);
argv.get()[i + 1] = option_args.rbegin()->c_str();
}
// Define command-line flags.
// ...
std::vector<tflite::Flag> flag_list = {
tflite::Flag::CreateFlag(...),
...,
tflite::Flag::CreateFlag(...),
};
int argc = num_options + 1;
if (!tflite::Flags::Parse(&argc, argv.get(), flag_list)) {
return nullptr;
}
return TfLiteDummyDelegateCreate(&options);
}
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// Defines two symbols that need to be exported to use the TFLite external
// delegate. See tensorflow/lite/delegates/external for details.
TFL_CAPI_EXPORT TfLiteDelegate* tflite_plugin_create_delegate(
char** options_keys, char** options_values, size_t num_options,
void (*report_error)(const char*)) {
return tflite::tools::CreateDummyDelegateFromOptions(
options_keys, options_values, num_options);
}
TFL_CAPI_EXPORT void tflite_plugin_destroy_delegate(TfLiteDelegate* delegate) {
TfLiteDummyDelegateDelete(delegate);
}
#ifdef __cplusplus
}
#endif // __cplusplus
اکنون هدف BUILD مربوطه را برای ساخت یک کتابخانه پویا مانند شکل زیر ایجاد کنید:
cc_binary(
name = "dummy_external_delegate.so",
srcs = [
"external_delegate_adaptor.cc",
],
linkshared = 1,
linkstatic = 1,
deps = [
":dummy_delegate",
"//tensorflow/lite/c:common",
"//tensorflow/lite/tools:command_line_flags",
"//tensorflow/lite/tools:logging",
],
)
پس از ایجاد این فایل delegate .so خارجی، میتوانید باینری بسازید یا از موارد از پیش ساخته شده برای اجرا با نماینده جدید استفاده کنید، تا زمانی که باینری با کتابخانه external_delegate_provider مرتبط است که از پرچمهای خط فرمان همانطور که در اینجا توضیح داده شده پشتیبانی میکند. توجه: این ارائهدهنده نمایندگی خارجی قبلاً به باینریهای آزمایشی و ابزارسازی موجود مرتبط شده است.
برای توضیح نحوه محک زدن نماینده ساختگی از طریق این رویکرد نمایندگی خارجی، به توضیحات اینجا مراجعه کنید. می توانید از دستورات مشابه برای ابزارهای تست و ارزیابی که قبلاً ذکر شد استفاده کنید.
شایان ذکر است که نماینده خارجی همان پیاده سازی C++ مربوط به نماینده در Tensorflow Lite Python binding است که در اینجا نشان داده شده است. بنابراین، کتابخانه آداپتور نماینده خارجی پویا ایجاد شده در اینجا میتواند مستقیماً با APIهای Tensorflow Lite Python استفاده شود.
منابع
لینک های دانلود برای باینری های از پیش ساخته شده شبانه ابزار TFLite
سیستم عامل | ARCH | BINARY_NAME |
لینوکس | x86_64 | |
بازو | ||
aarch64 | ||
اندروید | بازو | |
aarch64 |