Özel Temsilci uygulama

TensorFlow Lite Temsilcisi, modellerinizi (kısmen veya bütün olarak) 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 (Tensör İşleme Birimi) gibi çeşitli cihaz üzerindeki 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 sunar.

Aşağıdaki şemada, yetki verilmiş kullanıcılar özetlenmiştir. Ayrıntılı bilgi için alt kısımlara yer verilmiştir.

TFLite Delegeleri

Ne zaman özel temsilci oluşturmalıyım?

TensorFlow Lite'ta GPU, DSP ve EdgeTPU gibi hedef hızlandırıcılar için çok çeşitli yetki verilmiş kullanıcılar bulunur.

Kendi yetki verdiğiniz kişiyi oluşturmak aşağıdaki senaryolarda faydalıdır:

  • Mevcut bir temsilci tarafından desteklenmeyen yeni bir ML çıkarım motoru 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ı (ör. operatör birleştirme) geliştiriyorsunuz.

Yetki verilmiş kullanıcıların işleyiş şekli

Aşağıdaki gibi basit bir model grafiği ile Conv2D ve Ortalama işlemleri için daha hızlı bir uygulamaya sahip, yetki verilmiş bir "MyDelegate" kullanabilirsiniz.

Orijinal grafik

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

Yetki verilmiş kullanıcı ile grafik

Yukarıdaki grafik, TensorFlow Lite orijinal grafiği iki kural izleyerek böldüğünden elde edilir:

  • Yetki verilmiş kullanıcı 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 yerleştirilir.
  • Yetki verilecek her bölümde, yalnızca yetki verilmiş kullanıcı tarafından işlenmeyen giriş ve çıkış düğümleri bulunur.

Yetki verilmiş kullanıcı tarafından işlenen her bölüm, çağrı çağrısındaki bölümü değerlendiren orijinal grafikte bir temsilci düğümle değiştirilir (temsil çekirdek olarak da çağrılabilir).

Modele bağlı olarak, son grafik bir veya daha fazla düğümle sonuçlanabilir. Bu, bazı işlemlerin yetki verilen kullanıcı tarafından desteklenmediği anlamına gelir. Genel olarak, temsilciden ana grafiğe her geçişinizde, bellek kopyalarından (örneğin, GPU'dan CPU'ya) kaynaklanan sonuçları ana grafiğe aktarmak için bir ek yük söz konusu olduğundan, yetki verdiğiniz kişi tarafından birden fazla bölümlendirme yapılmasını istemezsiniz. Bu tür ek yük, özellikle büyük miktarda bellek kopyası olduğunda performans kazançlarını dengeleyebilir.

Kendi özel yetki verdiğiniz kullanıcıyı uygulama

Yetki verilmiş kullanıcı eklemek için tercih edilen yöntem SimpleDelegate API'yi 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, yetki verilen kullanıcının hangi işlemlerin desteklendiğini ve yetki verilmiş grafiği kapsayan bir çekirdek oluşturmak için fabrika sınıfını temsil eder. Daha fazla bilgi için bu C++ başlık dosyasında tanımlanan arayüze bakın. Koddaki yorumlar, her API'yi ayrıntılı olarak açıklar.

2: SimpleDelegateKernelInterface

Bu sınıf, yetkilendirilen bölümü başlatma / hazırlama / çalıştırma mantığını içerir.

Şunları içerir: (Tanımı inceleyin)

  • Init(...): Herhangi bir tek seferlik başlatma işlemi için bir kez çağrılır.
  • Hazırla(...): Bu düğümün her farklı örneği için çağrılır. Bu durum, birden fazla yetki verilmiş bölümünüz varsa gerçekleşir. Tensörler her yeniden boyutlandırıldığında çağrılacağı için genellikle bellek ayırmalarını burada yapmak istersiniz.
  • Invoke(...): çıkarım için çağrılacaktır.

Örnek

Bu örnekte, yalnızca 2 tür işlem (ADD) ve (SUB) ile float32 tensörlerini 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 öğesinden 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 karşılaştırma ve değerlendirme

TFLite'ta, hızlıca TFLite modeliyle karşılaştırarak test edebileceğiniz bir araç seti bulunur.

  • Model Karşılaştırma Aracı: Araç bir TFLite modeli alır, rastgele girişler oluşturur ve belirli sayıda çalıştırma için modeli tekrar tekrar çalıştırır. Sona, toplu gecikme istatistiklerini yazdırır.
  • Çıkarım Farkı Aracı: Belirli bir model için araç, rastgele Gauss verileri oluşturur ve bu verileri, biri tek iş parçacıklı CPU çekirdeği çalıştıran ve diğeri kullanıcı tanımlı bir spesifikasyon kullanarak iki farklı TFLite yorumlayıcısından geçirir. Her bir yorumlayıcının çıkış tensörleri arasındaki mutlak farkı, her öğe bazında ölçer. Bu araç, doğruluk sorunlarını ayıklama konusunda da faydalı olabilir.
  • Görüntü sınıflandırma ve nesne algılama için göreve özgü değerlendirme araçları da vardır. Bu araçlara buradan erişebilirsiniz

Buna ek olarak TFLite, yeni temsilciyi daha fazla kapsamla test etmek ve normal TFLite yürütme yolunun bozuk olmadığından emin olmak için yeniden kullanılabilecek çok sayıda çekirdek ve işlem birimi testine sahiptir.

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

En iyi yaklaşımı seçme

Her iki yaklaşım da aşağıda açıklandığı gibi birkaç değişiklik gerektirir. Ancak ilk yaklaşım, temsilciyi statik olarak bağlar ve test, karşılaştırma ve değerlendirme araçlarının yeniden oluşturulmasını gerektirir. Öte yandan, ikincisi yetkiyi paylaşılan bir kitaplık olarak yapar ve paylaşılan kitaplıktaki oluşturma/silme yöntemlerini göstermenizi gerektirir.

Sonuç olarak, harici yetki verme mekanizması TFLite'ın önceden oluşturulmuş Tensorflow Lite araç ikili programları ile çalışır. Ancak daha az belirgindir ve otomatik entegrasyon testlerinde ayarlanması daha karmaşık olabilir. Daha net ifadeler için temsilci kayıt operatörü yaklaşımını kullanın.

1. Seçenek: Yetkili kayıt operatöründen yararlanma

Yetki verilmiş kayıt operatörü, yetki verilmiş sağlayıcıların bir listesini tutar. Bu listelerin her biri, komut satırı işaretlerine göre TFLite yetki verilmiş kullanıcıları oluşturmanın kolay bir yolunu sunar ve bu nedenle araçlar için uygundur. Yeni temsilciyi yukarıda bahsedilen tüm Tensorflow Lite araçlarına bağlamak için önce yeni bir yetki verilmiş sağlayıcı oluşturmanız ve ardından DERLEME kurallarında yalnızca birkaç değişiklik yapmanız gerekir. Bu entegrasyon sürecinin tam bir örneği aşağıda gösterilmiştir (ve kodu burada bulabilirsiniz).

SimpleDelegate API'lerini uygulayan bir temsilciniz ve bu "dummy" yetkiyi oluşturmak/silmek için harici "C" API'lerinin aşağıda gösterildiği gibi 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 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*) {});
}

DERLEME kuralı tanımları, kitaplığın her zaman bağlı olduğundan ve optimize edici tarafından bırakılmadığından emin olmanız gerektiği için ö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 yanı sıra kendi temsilcinizle çalışabilecek diğer değerlendirme araçlarının 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",
    ],
)

Bu yetkili sağlayıcıyı, burada açıklandığı gibi TFLite çekirdek testlerine de bağlayabilirsiniz.

2. Seçenek: Harici delege özelliğinden yararlanın

Bu alternatifte, önce aşağıda gösterildiği gibi external_delegate_adaptor.cc için harici bir temsilci bağdaştırıcı oluşturursunuz. Bu yaklaşımın, daha önce de belirtildiği gibi 1. seçeneğ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 DERLEME 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 yetki verilmiş .so dosyası oluşturulduktan sonra, ikili programlar oluşturabilir veya ikili program burada açıklandığı gibi komut satırı işaretlerini destekleyen external_delegate_provider kitaplığına bağlı olduğu sürece yeni temsilciyle çalıştırmak için önceden oluşturulmuş olanları kullanabilirsiniz. Not: Bu harici temsilci sağlayıcı, mevcut test ve araç ikili programlarına zaten bağlanmıştır.

Bu harici delege yaklaşımıyla temsili yetki verilmiş kullanıcının nasıl karşılaştırılacağını görmek için buradaki açıklamalara bakın. Daha önce bahsedilen test ve değerlendirme araçları için de benzer komutlar kullanabilirsiniz.

Harici yetkinin, burada gösterildiği gibi Tensorflow Lite Python bağlamasında yetki verilmiş için ilgili C++ uygulaması olduğunu belirtmekte fayda vardır. Bu nedenle, burada oluşturulan dinamik harici temsilci bağdaştırıcı kitaplığı Tensorflow Lite Python API'leriyle doğrudan kullanılabilir.

Kaynaklar

OS ARK BINARY_NAME
Linux x86_64
kol
aarch64
Android kol
aarch64