Özel Temsilci Uygulama

TensorFlow Lite Temsilcisi nedir?

TensorFlow Lite Delegesi , modellerinizi (kısmen veya tamamen) başka bir uygulayıcıda çalıştırmanıza olanak tanır. Bu mekanizma, çıkarım için GPU veya Edge TPU (Tensor İşleme 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'tan esnek ve ayrıştırılmış bir yöntem sağlar.

Aşağıdaki şema delegeleri özetlemektedir, aşağıdaki bölümlerde daha fazla ayrıntı.

TFLite Delegates

Ne zaman Özel bir 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 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 kaynaştırma gibi) geliştiriyorsunuz.

Temsilciler nasıl çalışır?

Aşağıdaki gibi basit bir model grafiğini ve Conv2D ve Mean işlemleri için daha hızlı bir uygulamaya sahip bir temsilci "MyDelegate" düşünün.

Original graph

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

Graph with delegate

Yukarıdaki grafik, TensorFlow Lite orijinal grafiği iki kurala göre böldüğü için 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ı karşılanırken bir bölüme yerleştirilir.
  • Yetki verilecek 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, orijinal grafikte, bölümü çağırma çağrısında değerlendiren bir temsilci düğümü (temsilci çekirdek 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ı işlemlerin temsilci tarafından desteklenmediği anlamına gelir. Genel olarak, temsilci tarafından birden fazla bölümün işlenmesini istemezsiniz, çünkü temsilciden ana grafiğe her geçtiğinizde, sonuçların yetki verilen alt grafikten ana grafiğe aktarılması için 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 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, hangi işlemlerin desteklendiği temsilcinin yeteneklerini ve temsil edilen grafiği kapsayan 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çıklamaktadır.

2 - SimpleDelegateKernelInterface

Bu sınıf, temsil edilen bölümü başlatmak / hazırlamak / ve çalıştırmak için mantığı kapsüller.

Ş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ırmaları yapmak istersiniz, çünkü bu, tensörler her yeniden boyutlandırıldığında çağrılacaktır.
  • Çağır (...): çıkarım için çağrılacak.

Misal

Bu örnekte, yalnızca float32 tensörlü 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>();
  }
};

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ızla 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 için tekrar tekrar çalıştırır. Sonunda toplu gecikme istatistiklerini yazdırır.
  • Çıkarım Diff Aracı : Belirli bir model için, araç rastgele Gauss verisi oluşturur ve bu verileri iki farklı TFLite yorumlayıcısından geçirir; biri tek iş parçacıklı CPU çekirdeği çalıştırırken diğeri kullanıcı tanımlı bir özellik kullanır. Her bir yorumlayıcıdan gelen çıkış tensörleri arasındaki mutlak farkı, öğe başına esasına göre ölçer. Bu araç, doğruluk sorunlarının giderilmesi için 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 bulunabilir

Ek olarak, TFLite, yeni delegeyi daha fazla kapsama alanıyla 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 testlerine sahiptir.

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

En iyi yaklaşımı seçmek

Her iki yaklaşım da aşağıda ayrıntıları verildiği gibi birkaç değişiklik gerektirir. Bununla birlikte, 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 yapar ve sizden, paylaşılan kitaplıktaki oluşturma / silme yöntemlerini açığa çı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çık ve otomatik entegrasyon testlerinde kurulması daha karmaşık olabilir. Daha iyi netlik için temsilci kayıt şirketi yaklaşımını kullanın.

1. Seçenek: Temsilci kayıt operatöründen yararlanın

Temsilci kayıt sorumlusu, her biri komut satırı bayraklarına dayalı TFLite temsilcileri oluşturmak için kolay bir yol sağlayan ve bu nedenle araçlar için uygun olan temsilci sağlayıcılarının bir listesini tutar. Tensorflow Lite araçları Yukarıda belirtilen tüm yeni temsilci takmak için, öncelikle böyle bir yeni temsilci sağlayıcı oluşturmak biri , sonra YAPI kurallarında ancak birkaç değişiklik yapar. 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 bu "kukla" temsilciyi aşağıda gösterildiği gibi oluşturma / silme 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 Karşılaştırma Aracı ve Çıkarım Aracı 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ı, kitaplığın her zaman bağlantılı olduğundan ve optimize edici tarafından bırakılmadığından emin olmanız gerektiğinden ö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 bu iki sarmalayıcı kuralı, kendi temsilcinizle çalışabilecek bir Karşılaştırma Aracı ve Çıkarım Aracı sürümü ve diğer değerlendirme araçları oluşturmak için BUILD dosyanıza 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.

2. Seçenek: Harici temsilciden yararlanın

Bu alternatifte, öncelikle aşağıda gösterildiği gibi external_delegate_adaptor.cc ile harici bir temsilci bağdaştırıcısı oluşturursunuz. Bu yaklaşım, yukarıda belirtildiği gibi Seçenek 1'e kıyasla biraz daha az tercih edilir.

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ı bayraklarını destekleyen external_delegate_provider kitaplığına bağlı olduğu sürece yeni temsilci ile çalıştırmak için ikili dosyalar oluşturabilir veya önceden oluşturulmuş olanları kullanabilirsiniz. Not: Bu harici temsilci sağlayıcı, mevcut test ve araç oluşturma ikili dosyalarına zaten bağlanmıştır.

Bu harici delege yaklaşımı aracılığıyla sahte delegenin nasıl kıyaslanacağına ilişkin bir örnek için buradaki açıklamalara bakın. Daha önce bahsedilen test ve değerlendirme araçları için benzer komutları kullanabilirsiniz.

Harici delegenin , burada gösterildiği gibi Tensorflow Lite Python bağlamasında delegenin karşılık gelen C ++ uygulaması olduğuna dikkat etmek önemlidir. Bu nedenle, burada oluşturulan dinamik harici temsilci bağdaştırıcısı kitaplığı doğrudan Tensorflow Lite Python API'leri ile kullanılabilir.

Kaynaklar

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