Tham dự Hội nghị chuyên đề Women in ML vào ngày 7 tháng 12 Đăng ký ngay

Triển khai một đại diện tùy chỉnh

Sử dụng bộ sưu tập để sắp xếp ngăn nắp các trang Lưu và phân loại nội dung dựa trên lựa chọn ưu tiên của bạn.

Đại biểu TensorFlow Lite là gì?

TensorFlow Lite Delegate cho phép bạn chạy các mô hình của mình (một phần hoặc toàn bộ) trên một trình thực thi khác. Cơ chế này có thể tận dụng nhiều bộ tăng tốc trên thiết bị như GPU hoặc Edge TPU (Bộ xử lý căng) để suy luận. Điều này cung cấp cho các nhà phát triển một phương pháp linh hoạt và được tách rời khỏi TFLite mặc định để tăng tốc độ suy luận.

Sơ đồ dưới đây tóm tắt các đại biểu, chi tiết hơn trong các phần bên dưới.

TFLite Delegates

Khi nào tôi nên tạo đại biểu Tùy chỉnh?

TensorFlow Lite có nhiều loại đại diện cho các trình tăng tốc mục tiêu như GPU, DSP, EdgeTPU và các khuôn khổ như Android NNAPI.

Tạo đại biểu của riêng bạn rất hữu ích trong các trường hợp sau:

  • Bạn muốn tích hợp một công cụ suy luận ML mới không được hỗ trợ bởi bất kỳ đại biểu hiện có nào.
  • Bạn có một trình tăng tốc phần cứng tùy chỉnh để cải thiện thời gian chạy cho các tình huống đã biết.
  • Bạn đang phát triển các tính năng tối ưu hóa CPU (chẳng hạn như kết hợp người vận hành) để có thể tăng tốc một số mô hình nhất định.

Cơ chế hoạt động của các đại biểu như thế nào?

Hãy xem xét một biểu đồ mô hình đơn giản như biểu đồ sau và một đại biểu “MyDelegate” có triển khai nhanh hơn cho các hoạt động Conv2D và Mean.

Original graph

Sau khi áp dụng “MyDelegate” này, biểu đồ TensorFlow Lite ban đầu sẽ được cập nhật như sau:

Graph with delegate

Biểu đồ trên có được khi TensorFlow Lite tách biểu đồ ban đầu theo hai quy tắc:

  • Các hoạt động cụ thể có thể được xử lý bởi người được ủy quyền được đưa vào một phân vùng trong khi vẫn đáp ứng các phụ thuộc quy trình tính toán ban đầu giữa các hoạt động.
  • Mỗi phân vùng được ủy quyền chỉ có các nút đầu vào và đầu ra không được người ủy quyền xử lý.

Mỗi phân vùng được quản lý bởi một đại biểu được thay thế bằng một nút đại biểu (cũng có thể được gọi là một nhân đại biểu) trong biểu đồ ban đầu đánh giá phân vùng đó trong lệnh gọi của nó.

Tùy thuộc vào mô hình, biểu đồ cuối cùng có thể kết thúc bằng một hoặc nhiều nút, điều này có nghĩa là một số hoạt động không được hỗ trợ bởi đại biểu. Nói chung, bạn không muốn có nhiều phân vùng do người ủy quyền xử lý, bởi vì mỗi lần bạn chuyển từ người được ủy quyền sang đồ thị chính, có một chi phí để chuyển kết quả từ đồ thị con được ủy quyền sang đồ thị chính dẫn đến kết quả do bộ nhớ. bản sao (ví dụ: GPU sang CPU). Chi phí này có thể bù đắp cho việc tăng hiệu suất, đặc biệt là khi có một lượng lớn các bản sao bộ nhớ.

Triển khai đại biểu tùy chỉnh của riêng bạn

Phương pháp ưu tiên để thêm đại biểu là sử dụng API SimpleDelegate .

Để tạo một đại biểu mới, bạn cần triển khai 2 giao diện và cung cấp triển khai của riêng bạn cho các phương thức giao diện.

1 - SimpleDelegateInterface

Lớp này đại diện cho các khả năng của người được ủy quyền, những hoạt động nào được hỗ trợ và một lớp nhà máy để tạo hạt nhân đóng gói đồ thị được ủy quyền. Để biết thêm chi tiết, hãy xem giao diện được xác định trong tệp tiêu đề C ++ này. Các nhận xét trong mã giải thích chi tiết từng API.

2 - SimpleDelegateKernelInterface

Lớp này đóng gói logic để khởi tạo / chuẩn bị / và chạy phân vùng được ủy quyền.

Nó có: (Xem định nghĩa )

  • Init (...): sẽ được gọi một lần để thực hiện bất kỳ quá trình khởi tạo một lần nào.
  • Chuẩn bị (...): được gọi cho mỗi phiên bản khác nhau của nút này - điều này xảy ra nếu bạn có nhiều phân vùng được ủy quyền. Thông thường, bạn muốn thực hiện phân bổ bộ nhớ ở đây, vì điều này sẽ được gọi là mỗi khi các bộ căng được thay đổi kích thước.
  • Invoke (...): sẽ được gọi để suy luận.

Thí dụ

Trong ví dụ này, bạn sẽ tạo một ủy nhiệm rất đơn giản chỉ có thể hỗ trợ 2 loại phép toán (ADD) và (SUB) chỉ với float32 tensors.

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

Tiếp theo, tạo hạt nhân đại biểu của riêng bạn bằng cách kế thừa từ 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_;
};


Điểm chuẩn và đánh giá đại biểu mới

TFLite có một bộ công cụ mà bạn có thể nhanh chóng kiểm tra dựa trên mô hình TFLite.

  • Công cụ điểm chuẩn mô hình : Công cụ lấy mô hình TFLite, tạo đầu vào ngẫu nhiên, sau đó chạy lặp lại mô hình trong một số lần chạy cụ thể. Nó in số liệu thống kê tổng hợp về độ trễ ở cuối.
  • Công cụ Sự khác biệt: Đối với một mô hình nhất định, công cụ tạo dữ liệu Gaussian ngẫu nhiên và chuyển nó qua hai trình thông dịch TFLite khác nhau, một trình thông dịch chạy nhân CPU luồng đơn và công cụ kia sử dụng thông số kỹ thuật do người dùng xác định. Nó đo lường sự khác biệt tuyệt đối giữa các bộ căng đầu ra từ mỗi trình thông dịch, trên cơ sở từng phần tử. Công cụ này cũng có thể hữu ích để gỡ lỗi các vấn đề về độ chính xác.
  • Ngoài ra còn có các công cụ đánh giá nhiệm vụ cụ thể, để phân loại hình ảnh và phát hiện đối tượng. Các công cụ này có thể được tìm thấy tại đây

Ngoài ra, TFLite có một tập hợp lớn các bài kiểm tra hạt nhân và đơn vị op có thể được sử dụng lại để kiểm tra đại biểu mới với phạm vi bao phủ nhiều hơn và để đảm bảo đường dẫn thực thi TFLite thông thường không bị hỏng.

Để sử dụng lại các bài kiểm tra và công cụ TFLite cho đại biểu mới, bạn có thể sử dụng một trong hai tùy chọn sau:

Chọn cách tiếp cận tốt nhất

Cả hai cách tiếp cận đều yêu cầu một số thay đổi như được trình bày chi tiết bên dưới. Tuy nhiên, cách tiếp cận đầu tiên liên kết tĩnh và yêu cầu xây dựng lại các công cụ kiểm tra, điểm chuẩn và đánh giá. Ngược lại, cái thứ hai làm cho ủy quyền như một thư viện được chia sẻ và yêu cầu bạn hiển thị các phương thức tạo / xóa từ thư viện được chia sẻ.

Do đó, cơ chế ủy quyền bên ngoài sẽ hoạt động với các mã nhị phân công cụ Tensorflow Lite được xây dựng trước của TFLite. Nhưng nó ít rõ ràng hơn và có thể phức tạp hơn khi thiết lập trong các bài kiểm tra tích hợp tự động. Sử dụng phương pháp tiếp cận công ty đăng ký ủy quyền để rõ ràng hơn.

Tùy chọn 1: Tận dụng công ty đăng ký ủy quyền

Cơ quan đăng ký ủy quyền giữ một danh sách các nhà cung cấp ủy quyền, mỗi nhà cung cấp cung cấp một cách dễ dàng để tạo các đại biểu TFLite dựa trên các cờ dòng lệnh và do đó, thuận tiện cho việc tạo công cụ. Để cắm ủy quyền mới vào tất cả các công cụ Tensorflow Lite được đề cập ở trên, trước tiên bạn phải tạo một trình cung cấp ủy quyền mới như công cụ này , sau đó chỉ thực hiện một số thay đổi đối với quy tắc BUILD. Dưới đây là một ví dụ đầy đủ về quá trình tích hợp này (và mã có thể được tìm thấy tại đây ).

Giả sử bạn có một đại diện triển khai các API SimpleDelegate và các API "C" bên ngoài để tạo / xóa đại biểu 'dummy' này như được hiển thị bên dưới:

// 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);

Để tích hợp “DummyDelegate” với Benchmark Tool và Inference Tool, hãy xác định một DelegateProvider như bên dưới:

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

Định nghĩa quy tắc BUILD rất quan trọng vì bạn cần đảm bảo rằng thư viện luôn được liên kết và không bị bỏ bởi trình tối ưu hóa.

#### 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.
)

Bây giờ, hãy thêm hai quy tắc trình bao bọc này vào tệp BUILD của bạn để tạo phiên bản Công cụ điểm chuẩn và Công cụ suy luận, và các công cụ đánh giá khác, có thể chạy với đại biểu của riêng bạn.

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",
    ],
)

Bạn cũng có thể cắm nhà cung cấp ủy quyền này để kiểm tra hạt nhân TFLite như được mô tả ở đây .

Tùy chọn 2: Tận dụng ủy quyền bên ngoài

Trong cách thay thế này, trước tiên bạn tạo một bộ điều hợp đại biểu bên ngoài là external_delegate_adaptor.cc như được hiển thị bên dưới. Lưu ý, phương pháp này ít được ưu tiên hơn một chút so với Phương án 1 như đã nói ở trê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

Bây giờ, hãy tạo mục tiêu BUILD tương ứng để xây dựng một thư viện động như được hiển thị bên dưới:

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",
    ],
)

Sau khi tệp .so đại biểu bên ngoài này được tạo, bạn có thể tạo tệp nhị phân hoặc sử dụng tệp nhị phân được tạo sẵn để chạy với đại biểu mới miễn là tệp nhị phân được liên kết với thư viện external_delegate_provider hỗ trợ cờ dòng lệnh như được mô tả ở đây . Lưu ý: nhà cung cấp đại biểu bên ngoài này đã được liên kết với các tệp nhị phân thử nghiệm và công cụ hiện có.

Tham khảo mô tả tại đây để biết minh họa về cách đánh giá đại biểu giả thông qua phương pháp tiếp cận đại biểu bên ngoài này. Bạn có thể sử dụng các lệnh tương tự cho các công cụ kiểm tra và đánh giá đã đề cập trước đó.

Điều đáng chú ý là đại biểu bên ngoài là triển khai C ++ tương ứng của đại biểu trong liên kết Python của Tensorflow Lite như được hiển thị ở đây . Do đó, thư viện bộ điều hợp đại biểu động bên ngoài được tạo ở đây có thể được sử dụng trực tiếp với các API Tensorflow Lite Python.

Tài nguyên

Hệ điều hành ARCH BINARY_NAME
Linux x86_64
cánh tay
aarch64
Android cánh tay
aarch64