Özel Delege Uygulama

TensorFlow Lite Temsilcisi nedir?

Bir TensorFlow Lite Temsilcisi, modellerinizi (kısmen veya tamamen) başka bir uygulayıcı üzerinde çalıştırmanıza olanak tanır. Bu mekanizma, çıkarım için GPU veya Edge TPU (Tensör İşlem Birimi) gibi çeşitli cihaz içi hızlandırıcılardan yararlanabilir. Bu, geliştiricilere çıkarımı hızlandırmak için varsayılan TFLite'dan esnek ve bağımsız bir yöntem sağlar.

Aşağıdaki şema delegeleri özetlemektedir; daha fazla ayrıntı aşağıdaki bölümlerde yer almaktadır.

TFLite Delegates

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 faydalıdır:

  • Mevcut herhangi bir temsilci tarafından desteklenmeyen yeni bir makine öğrenimi çıkarım motorunu entegre etmek istiyorsunuz.
  • Bilinen senaryolar için çalışma süresini 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ğini ve Conv2D ve Ortalama işlemleri için daha hızlı bir uygulamaya sahip olan bir "MyDelegate" temsilcisini düşünün.

Original graph

Bu "MyDelegate" uygulandıktan sonra orijinal TensorFlow Lite grafiği aşağıdaki gibi güncellenecektir:

Graph with delegate

Yukarıdaki grafik, TensorFlow Lite'ın orijinal grafiği iki kurala göre bölmesiyle elde edilmiştir:

  • Temsilci tarafından gerçekleştirilebilecek belirli işlemler, işlemler arasındaki orijinal bilgi işlem iş akışı bağımlılıkları yine de karşılanırken bir bölüme yerleştirilir.
  • Temsilci verilecek her bölümde yalnızca temsilci tarafından işlenmeyen giriş ve çıkış düğümleri bulunur.

Bir temsilci tarafından işlenen her bölümün yerini, orijinal grafikte kendi çağırma çağrısında bölümü değerlendiren bir temsilci düğümü (temsilci çekirdeği olarak da adlandırılabilir) alır.

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, birden fazla bölümün delege tarafından yönetilmesini istemezsiniz, çünkü delegeden ana grafiğe her geçiş yaptığınızda, sonuçların delege edilen alt grafikten ana grafiğe aktarılması için bellekten kaynaklanan bir ek yük vardır. kopyalar (örneğin, GPU'dan CPU'ya). Bu tür bir 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'sini kullanmaktır.

Yeni bir temsilci oluşturmak için 2 arayüz uygulamanız ve arayüz yöntemleri için kendi uygulamanızı sağlamanız gerekir.

1 - SimpleDelegateInterface

Bu sınıf, temsilcinin hangi işlemlerin desteklendiği yeteneklerini ve temsilci grafiği kapsayan bir çekirdek oluşturmaya yönelik fabrika sınıfını temsil eder. Daha fazla ayrıntı için bu C++ başlık dosyasında tanımlanan arayüze bakın. Koddaki yorumlar her bir API'yi ayrıntılı olarak açıklamaktadır.

2 - SimpleDelegateKernelInterface

Bu sınıf, temsilci bölümün başlatılması / hazırlanması / çalıştırılması mantığını kapsar.

Şunlara sahiptir: (Tanımlara bakınız)

  • Init(...): Herhangi bir tek seferlik başlatma işlemi 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 fazla temsilci bölümünüz varsa gerçekleşir. Tensörler her yeniden boyutlandırıldığında bu çağrılacağından, genellikle burada bellek ayırma işlemi yapmak istersiniz.
  • Invoke(...): çıkarım için çağrılacak.

Örnek

Bu örnekte, yalnızca float32 tensörleri ile yalnızca 2 tür işlemi (ADD) ve (SUB) 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>();
  }
};

Daha sonra SimpleDelegateKernelInterface miras alarak 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 modeliyle hızlı bir şekilde test edebileceğiniz bir dizi araca sahiptir.

  • Model Karşılaştırma Aracı : Araç bir TFLite modelini alır, rastgele girdiler üretir ve ardından modeli belirli sayıda çalıştırma boyunca tekrar tekrar çalıştırır. Sonunda toplu gecikme istatistiklerini yazdırır.
  • Çıkarım Farkı Aracı : Belirli bir model için, araç rastgele Gauss verileri üretir ve bunları biri tek iş parçacıklı CPU çekirdeği çalıştıran, diğeri kullanıcı tanımlı bir özellik kullanan iki farklı TFLite yorumlayıcısından geçirir. Her bir yorumlayıcının çıktı tensörleri arasındaki mutlak farkı öğe bazında ölçer. Bu araç aynı zamanda doğruluk sorunlarının giderilmesinde de yararlı olabilir.
  • Görüntü sınıflandırması ve nesne tespiti için göreve özel değerlendirme araçları da vardır. Bu araçları burada bulabilirsiniz

Ek olarak TFLite, yeni delegeyi daha fazla kapsamla test etmek ve normal TFLite yürütme yolunun bozulmamasını sağlamak için yeniden kullanılabilecek geniş bir çekirdek ve işlem birimi testleri kümesine sahiptir.

Yeni delege için TFLite testlerini ve araçlarını yeniden kullanmak amacıyla aşağıdaki iki seçenekten birini kullanabilirsiniz:

En iyi yaklaşımın seçilmesi

Her iki yaklaşım da aşağıda ayrıntılı olarak açıklandığı gibi birkaç değişiklik gerektirir. Ancak ilk yaklaşım, delegeyi 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 haline getirir ve paylaşılan kitaplıktan oluşturma/silme yöntemlerini açığa çıkarmanızı gerektirir.

Sonuç olarak, harici temsilci mekanizması TFLite'ın önceden oluşturulmuş Tensorflow Lite takım ikili dosyalarıyla çalışacaktır. Ancak daha az açıktır ve otomatik entegrasyon testlerinde kurulumu daha karmaşık olabilir. Daha iyi netlik için temsilci kayıt şirketi yaklaşımını kullanın.

1. Seçenek: Temsilci kayıt şirketinden yararlanın

Temsilci kayıt şirketi , her biri komut satırı işaretlerine dayalı olarak TFLite delegeleri oluşturmanın kolay bir yolunu sağlayan ve dolayısıyla araç oluşturmaya uygun olan temsilci sağlayıcıların bir listesini tutar. Yeni temsilciyi yukarıda bahsedilen tüm Tensorflow Lite araçlarına eklemek 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österilmektedir (ve kod burada bulunabilir).

SimpleDelegate API'lerini uygulayan bir temsilciniz olduğunu ve bu 'kukla' temsilciyi aşağıda gösterildiği gibi oluşturmanın/silmenin harici "C" API'lerinin olduğunu varsayalım:

// 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*) {});
}

Kitaplığın her zaman bağlantılı olduğundan ve optimize edici tarafından bırakılmadığından emin olmanız gerektiğinden BUILD kural tanımları önemlidir.

#### 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, Karşılaştırma Aracı ve Çıkarım Aracının ve diğer değerlendirme araçlarının kendi temsilcinizle çalışabilecek bir sürümünü oluşturmak için BUILD dosyanıza bu iki sarmalayıcı kuralını 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",
    ],
)

Ayrıca bu temsilci sağlayıcıyı burada açıklandığı gibi TFLite çekirdek testlerine de bağlayabilirsiniz.

2. Seçenek: Harici temsilciden yararlanın

Bu alternatifte, öncelikle aşağıda gösterildiği gibi harici_delegate_adaptor.cc adında harici bir 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 karşılık gelen 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ı işaretlerini destekleyen external_delegate_provider kitaplığına bağlı olduğu sürece ikili dosyalar oluşturabilir veya yeni temsilciyle çalışmak için önceden oluşturulmuş olanları kullanabilirsiniz. Not: Bu harici temsilci sağlayıcı, mevcut test ve araç ikili dosyalarına zaten bağlanmıştır.

Bu harici delege yaklaşımı yoluyla sahte delegenin nasıl karşılaştırılacağına ilişkin bir örnek için buradaki açıklamalara bakın. Daha önce bahsettiğimiz test ve değerlendirme araçları için de benzer komutları kullanabilirsiniz.

Harici temsilcinin, burada gösterildiği gibi Tensorflow Lite Python bağlamasındaki 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'leriyle kullanılabilir.

Kaynaklar

işletim sistemi kemer BINARY_NAME
Linux x86_64
kol
aarch64
Android kol
aarch64