TensorFlow Lite Delegesi nedir?
Bir TensorFlow Lite Delegesi , modellerinizi (kısmen veya bütün) başka bir yürütücü üzerinde çalıştırmanıza olanak tanır. Bu mekanizma, çıkarım için GPU veya Edge TPU (Tensor Processing Unit) gibi çeşitli cihaz hızlandırıcılarından yararlanabilir. Bu, geliştiricilere çıkarımı hızlandırmak için varsayılan TFLite'dan esnek ve ayrıştırılmış bir yöntem sağlar.
Aşağıdaki diyagram delegeleri özetlemektedir, aşağıdaki bölümlerde daha fazla ayrıntı bulunmaktadır.
Ne zaman Özel temsilci oluşturmalıyım?
TensorFlow Lite, GPU, DSP, EdgeTPU gibi hedef hızlandırıcılar ve Android NNAPI gibi çerçeveler için çok çeşitli delegelere sahiptir.
Kendi temsilcinizi oluşturmak aşağıdaki senaryolarda kullanışlıdır:
- Mevcut herhangi bir temsilci tarafından desteklenmeyen yeni bir ML çıkarım motorunu entegre etmek istiyorsunuz.
- Bilinen senaryolar için çalışma zamanını iyileştiren özel bir donanım hızlandırıcınız var.
- Belirli modelleri hızlandırabilecek CPU optimizasyonları (operatör birleştirme gibi) geliştiriyorsunuz.
Delegeler nasıl çalışır?
Aşağıdaki gibi basit bir model grafiği ve Conv2D ve Mean işlemleri için daha hızlı bir uygulamaya sahip bir temsilci “MyDelegate” düşünün.
Bu “MyDelegate”i uyguladıktan sonra orijinal TensorFlow Lite grafiği aşağıdaki gibi güncellenecektir:
Yukarıdaki grafik, TensorFlow Lite orijinal grafiği iki kuralı izleyerek böldüğünde elde edilir:
- Temsilci tarafından gerçekleştirilebilecek belirli işlemler, işlemler arasındaki orijinal bilgi işlem iş akışı bağımlılıklarını karşılamaya devam ederken bir bölüme konur.
- Yetkilendirilecek her bölüm, yalnızca temsilci tarafından işlenmeyen giriş ve çıkış düğümlerine sahiptir.
Bir temsilci tarafından işlenen her bölüm, bölümü çağırma çağrısında değerlendiren orijinal grafikte bir temsilci düğümü (temsilci çekirdeği olarak da adlandırılabilir) ile değiştirilir.
Modele bağlı olarak, son grafik bir veya daha fazla düğümle sonuçlanabilir; ikincisi, bazı operasyonların temsilci tarafından desteklenmediği anlamına gelir. Genel olarak, temsilci tarafından işlenen birden çok bölümün olmasını istemezsiniz, çünkü temsilciden ana grafiğe her geçiş yaptığınızda, sonuçları temsilci alt grafikten ana grafiğe geçirmek için bellek nedeniyle sonuçlanan bir ek yük vardır. kopyalar (örneğin, GPU'dan CPU'ya). Bu tür ek yük, özellikle büyük miktarda bellek kopyası olduğunda performans kazanımlarını dengeleyebilir.
Kendi Özel temsilcinizi uygulama
Temsilci eklemek için tercih edilen yöntem SimpleDelegate API kullanmaktır.
Yeni bir temsilci oluşturmak için 2 arabirim uygulamanız ve arabirim yöntemleri için kendi uygulamanızı sağlamanız gerekir.
1 - SimpleDelegateInterface
Bu sınıf, hangi işlemlerin desteklendiği temsilcinin yeteneklerini ve temsilci grafiğini içine alan bir çekirdek oluşturmak için bir fabrika sınıfını temsil eder. Daha fazla ayrıntı için bu C++ başlık dosyasında tanımlanan arabirime bakın. Koddaki yorumlar, her bir API'yi ayrıntılı olarak açıklar.
2 - SimpleDelegateKernelInterface
Bu sınıf, atanan bölümü başlatma/hazırlama/ve çalıştırma mantığını kapsar.
Şunlara sahiptir: ( Tanıma bakın)
- Init(...): herhangi bir tek seferlik başlatma yapmak için bir kez çağrılacak.
- Hazırla(...): Bu düğümün her farklı örneği için çağrılır - bu, birden çok temsilci bölümünüz varsa gerçekleşir. Genellikle burada bellek ayırma yapmak istersiniz, çünkü buna tensörler her yeniden boyutlandırıldığında çağrılır.
- Invoke(...): çıkarım için çağrılacak.
Örnek
Bu örnekte, yalnızca 2 tür işlemi (ADD) ve (SUB) yalnızca float32 tensörlü destekleyebilen çok basit bir temsilci oluşturacaksınız.
// 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>();
}
};
Ardından, SimpleDelegateKernelInterface
devralarak kendi temsilci çekirdeğinizi oluşturun
// 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_;
};
Yeni delegeyi kıyaslayın ve değerlendirin
TFLite, bir TFLite modeline karşı hızlı bir şekilde test edebileceğiniz bir dizi araca sahiptir.
- Model Karşılaştırma Aracı : Araç bir TFLite modeli alır, rastgele girdiler üretir ve ardından belirli sayıda çalıştırma için modeli tekrar tekrar çalıştırır. Sonunda toplu gecikme istatistiklerini yazdırır.
- Çıkarım Farkı Aracı : Belirli bir model için, araç rasgele Gauss verileri üretir ve bunu biri tek iş parçacıklı CPU çekirdeği çalıştıran ve diğeri kullanıcı tanımlı bir özellik kullanan iki farklı TFLite yorumlayıcısından geçirir. Her bir yorumlayıcıdan gelen çıktı tensörleri arasındaki mutlak farkı eleman bazında ölçer. Bu araç, doğruluk sorunlarının giderilmesinde de yardımcı olabilir.
- Görüntü sınıflandırma ve nesne algılama için göreve özel değerlendirme araçları da vardır. Bu araçlar burada bulunabilir
Ek olarak, TFLite, yeni delegeyi daha fazla kapsama alanıyla test etmek ve normal TFLite yürütme yolunun bozulmadığından emin olmak için yeniden kullanılabilecek çok sayıda çekirdek ve işlem birimi testi içerir.
Yeni temsilci için TFLite testlerini ve araçlarını yeniden kullanmak için aşağıdaki iki seçenekten birini kullanabilirsiniz:
- Temsilci kayıt şirketi mekanizmasını kullanın.
- Harici delege mekanizmasını kullanın.
En iyi yaklaşımı seçme
Her iki yaklaşım da aşağıda ayrıntılı olarak açıklandığı gibi birkaç değişiklik gerektirir. Ancak ilk yaklaşım, temsilciyi statik olarak birbirine bağlar ve test, kıyaslama ve değerlendirme araçlarının yeniden oluşturulmasını gerektirir. Buna karşılık, ikincisi temsilciyi paylaşılan bir kitaplık yapar ve paylaşılan kitaplıktan oluşturma/silme yöntemlerini ortaya çıkarmanızı gerektirir.
Sonuç olarak, harici temsilci mekanizması TFLite'ın önceden oluşturulmuş Tensorflow Lite araç ikili dosyalarıyla çalışacaktır . Ancak daha az açıktır ve otomatik entegrasyon testlerinde kurulması daha karmaşık olabilir. Daha iyi netlik için temsilci kayıt şirketi yaklaşımını kullanın.
Seçenek 1: Temsilci kayıt kuruluşundan yararlanın
Temsilci kayıt şirketi, her biri komut satırı bayraklarına dayalı olarak TFLite delegeleri oluşturmanın kolay bir yolunu sağlayan ve dolayısıyla araçlar için uygun olan temsilci sağlayıcılarının bir listesini tutar. Yeni temsilciyi yukarıda bahsedilen tüm Tensorflow Lite araçlarına bağlamak için, önce bunun gibi yeni bir temsilci sağlayıcı oluşturursunuz ve ardından BUILD kurallarında yalnızca birkaç değişiklik yaparsınız. Bu entegrasyon sürecinin tam bir örneği aşağıda gösterilmiştir (ve kod burada bulunabilir).
SimpleDelegate API'lerini uygulayan bir temsilciniz olduğunu ve aşağıda gösterildiği gibi bu 'kukla' temsilciyi oluşturmaya/silmeye ilişkin harici "C" API'lerine sahip olduğunuzu varsayarsak:
// 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”i Benchmark Tool ve Inference Tool ile entegre etmek için aşağıdaki gibi bir DelegateProvider tanımlayın:
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 kuralı tanımları önemlidir, çünkü kitaplığın her zaman bağlantılı olduğundan ve optimize edici tarafından bırakılmadığından emin olmanız gerekir.
#### 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.
)
Şimdi, Benchmark Tool ve Inference Tool'un bir sürümünü ve kendi temsilcinizle çalışabilecek diğer değerlendirme araçlarını oluşturmak için BUILD dosyanıza bu iki sarmalayıcı kuralı ekleyin.
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",
],
)
Bu temsilci sağlayıcıyı burada açıklandığı gibi TFLite çekirdek testlerine de bağlayabilirsiniz.
Seçenek 2: Harici temsilciden yararlanın
Bu alternatifte, önce aşağıda gösterildiği gibi external_delegate_adaptor.cc adlı bir harici temsilci bağdaştırıcısı oluşturursunuz. Bu yaklaşımın, yukarıda belirtildiği gibi Seçenek 1'e kıyasla biraz daha az tercih edildiğini unutmayın.
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
Şimdi aşağıda gösterildiği gibi dinamik bir kitaplık oluşturmak için ilgili BUILD hedefini oluşturun:
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",
],
)
Bu harici temsilci .so dosyası oluşturulduktan sonra, ikili dosya burada açıklandığı gibi komut satırı bayraklarını destekleyen external_delegate_provider kitaplığına bağlı olduğu sürece, yeni temsilciyle çalışmak için ikili dosyalar oluşturabilir veya önceden oluşturulmuş olanları kullanabilirsiniz. Not: Bu harici temsilci sağlayıcı zaten mevcut test ve araç ikili dosyalarına bağlanmıştır.
Bu harici delege yaklaşımıyla sahte delegeyi nasıl kıyaslayacağınızı gösteren bir örnek için buradaki açıklamalara bakın. Daha önce bahsedilen test ve değerlendirme araçları için benzer komutları kullanabilirsiniz.
Harici temsilcinin , burada gösterildiği gibi Tensorflow Lite Python bağlamasında temsilcinin karşılık gelen C++ uygulaması olduğunu belirtmekte fayda var. Bu nedenle, burada oluşturulan dinamik harici temsilci bağdaştırıcı kitaplığı, doğrudan Tensorflow Lite Python API'leri ile kullanılabilir.
Kaynaklar
Her gece önceden oluşturulmuş TFLite araç ikili dosyaları için indirme bağlantıları
işletim sistemi | ARCH | BINARY_NAME |
Linux | x86_64 | |
kol | ||
aar64 | ||
Android | kol | |
aar64 |