การใช้การมอบสิทธิ์ที่กำหนดเอง

Delegate ของ TensorFlow Lite ช่วยให้คุณเรียกใช้โมเดล (บางส่วนหรือทั้งหมด) กับตัวดำเนินการอื่นได้ กลไกนี้จะใช้ประโยชน์จาก Accelerator ต่างๆ ในอุปกรณ์ เช่น GPU หรือ Edge TPU (TensorProcessing Unit) สำหรับการอนุมาน วิธีนี้จะช่วยให้นักพัฒนาแอปมีวิธีที่ยืดหยุ่นและแยกส่วนออกจาก TFLite เริ่มต้นเพื่อเพิ่มความเร็วในการอนุมาน

แผนภาพด้านล่างจะสรุปผู้รับมอบสิทธิ์และรายละเอียดเพิ่มเติมในส่วนด้านล่าง

ผู้แทน TFLite

ฉันควรสร้างการมอบสิทธิ์ที่กำหนดเองเมื่อใด

TensorFlow Lite มีผู้มอบสิทธิ์ที่หลากหลายสำหรับ Accelerator เป้าหมาย เช่น GPU, DSP และ EdgeTPU

การสร้างผู้รับมอบสิทธิ์ของคุณเองจะเป็นประโยชน์ในสถานการณ์ต่อไปนี้

  • คุณต้องการผสานรวมเครื่องมือการอนุมาน ML ใหม่ที่ผู้รับมอบสิทธิ์ที่มีอยู่ไม่รองรับ
  • คุณมีตัวเร่งฮาร์ดแวร์ที่กำหนดเองซึ่งปรับปรุงรันไทม์สำหรับสถานการณ์ที่รู้จัก
  • คุณกำลังพัฒนาการเพิ่มประสิทธิภาพ CPU (เช่น การใช้ Function ของโอเปอเรเตอร์) ที่ทำให้โมเดลบางรุ่นทำงานได้เร็วขึ้น

ผู้รับมอบสิทธิ์ทำงานอย่างไร

ลองใช้กราฟโมเดลง่ายๆ ดังตัวอย่างต่อไปนี้ และตัวอย่าง "MyDelegate" ของผู้รับมอบสิทธิ์ซึ่งใช้งานได้เร็วยิ่งขึ้นสำหรับการดำเนินการ Conv2D และค่าเฉลี่ย

กราฟเดิม

หลังจากใช้ "MyDelegate" นี้ ระบบจะอัปเดตกราฟ TensorFlow Lite เดิมดังนี้

กราฟที่มีผู้ได้รับมอบสิทธิ์

ระบบจะได้กราฟด้านบนเมื่อ TensorFlow Lite แยกกราฟต้นฉบับโดยใช้กฎ 2 ข้อดังนี้

  • ระบบจะใส่การดำเนินการเฉพาะที่ผู้ได้รับมอบสิทธิ์จัดการลงในพาร์ติชัน ขณะที่ยังคงตอบสนองการทำงานของเวิร์กโฟลว์การประมวลผลข้อมูลดั้งเดิมระหว่างการดำเนินการต่างๆ
  • แต่ละพาร์ติชันที่จะได้รับมอบสิทธิ์มีเพียงโหนดอินพุตและเอาต์พุตที่ผู้รับมอบสิทธิ์ไม่ได้จัดการ

แต่ละพาร์ติชันที่จัดการโดยผู้รับมอบสิทธิ์จะถูกแทนที่ด้วยโหนดที่ได้รับมอบสิทธิ์ (หรือเรียกว่าเคอร์เนลที่ได้รับมอบสิทธิ์) ในกราฟเดิมที่ประเมินพาร์ติชันในการเรียกใช้

กราฟสุดท้ายอาจมีโหนดอย่างน้อย 1 โหนดก็ได้ ซึ่งขึ้นอยู่กับโมเดลแต่ละโมเดล หมายความว่าผู้รับมอบสิทธิ์ไม่รองรับการดำเนินการบางอย่าง โดยทั่วไป คุณไม่ควรต้องการให้ผู้รับมอบสิทธิ์จัดการพาร์ติชันหลายรายการ เนื่องจากการเปลี่ยนจากผู้รับมอบสิทธิ์ไปยังกราฟหลักในแต่ละครั้งจะมีค่าใช้จ่ายในการส่งผลลัพธ์จากกราฟย่อยที่มอบหมายไปยังกราฟหลักที่เป็นผลสืบเนื่องจากการคัดลอกหน่วยความจำ (เช่น GPU เป็น CPU) ค่าใช้จ่ายดังกล่าวอาจชดเชยประสิทธิภาพที่เพิ่มขึ้น โดยเฉพาะเมื่อมีการทำสำเนาหน่วยความจำจำนวนมาก

การใช้การมอบสิทธิ์ที่กำหนดเอง

วิธีที่แนะนำในการเพิ่มผู้ที่ได้รับมอบสิทธิ์คือการใช้ SimpleDelegate API

หากต้องการสร้างผู้รับมอบสิทธิ์ใหม่ คุณจะต้องใช้อินเทอร์เฟซ 2 รายการและระบุการใช้งานของตนเองสำหรับเมธอดอินเทอร์เฟซ

1 - SimpleDelegateInterface รายการ

คลาสนี้แสดงความสามารถของผู้รับมอบสิทธิ์ การดำเนินการที่รองรับ และคลาสโรงงานสำหรับการสร้างเคอร์เนลที่รวมกราฟที่ได้รับมอบสิทธิ์ โปรดดูรายละเอียดเพิ่มเติมในอินเทอร์เฟซที่ระบุไว้ในไฟล์ส่วนหัว C++ นี้ ความคิดเห็นในโค้ดจะอธิบาย API แต่ละรายการอย่างละเอียด

2 - SimpleDelegateKernelInterface แถว

คลาสนี้จะสรุปตรรกะสำหรับการเริ่มต้น / การจัดเตรียม / และเรียกใช้พาร์ติชันที่ได้รับมอบสิทธิ์

มี (ดู คำจำกัดความ)

  • Init(...): ซึ่งจะมีการเรียก 1 ครั้งเพื่อเริ่มการทำงานแบบครั้งเดียว
  • Prepare(...): เรียกใช้แต่ละอินสแตนซ์ของโหนดนี้ ซึ่งจะเกิดขึ้นหากคุณมีพาร์ติชันที่ได้รับมอบสิทธิ์หลายพาร์ติชัน ปกติแล้วคุณต้องจัดสรรหน่วยความจำที่นี่ เพราะจะเรียกว่าทุกครั้งที่ Tensor ที่มีการปรับขนาด
  • Invoke(...): ซึ่งจะถูกเรียกใช้เพื่อการอนุมาน

ตัวอย่าง

ในตัวอย่างนี้ คุณจะสร้างการมอบสิทธิ์ที่เรียบง่ายมากซึ่งรองรับการดำเนินการได้เพียง 2 ประเภท (ADD) และ (SUB) ด้วย 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>();
  }
};

ถัดไป ให้สร้างเคอร์เนลที่มอบสิทธิ์ของคุณเองโดยรับค่าจาก 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_;
};


เปรียบเทียบและประเมินผู้รับมอบสิทธิ์ใหม่

TFLite มีชุดเครื่องมือที่คุณทดสอบเทียบกับโมเดล TFLite ได้อย่างรวดเร็ว

  • เครื่องมือการเปรียบเทียบรูปแบบการระบุแหล่งที่มา: เครื่องมือนี้ใช้โมเดล TFLite แล้วสร้างอินพุตแบบสุ่ม จากนั้นเรียกใช้โมเดลซ้ำๆ หลายครั้งตามจำนวนครั้งที่กำหนด ซึ่งจะพิมพ์สถิติเวลาในการตอบสนอง แบบรวมในตอนท้าย
  • เครื่องมือความแตกต่างของการอนุมาน: สำหรับโมเดลที่กำหนด เครื่องมือจะสร้างข้อมูลเกาส์เชียนแบบสุ่มและส่งผ่าน TFLite อินเทอร์พรีเตอร์ 2 ตัว ตัวหนึ่งใช้เคอร์เนล CPU แบบเทรดเดียวและอีกตัวหนึ่งใช้ข้อกำหนดที่ผู้ใช้กำหนด ซึ่งจะวัดความแตกต่างสัมบูรณ์ระหว่าง Tensor เอาต์พุตจากอินเตอร์พรีเตอร์แต่ละแบบตามองค์ประกอบ เครื่องมือนี้มีประโยชน์ในการแก้ไขข้อบกพร่อง ด้านความถูกต้องด้วย
  • นอกจากนี้ยังมีเครื่องมือการประเมินเฉพาะงานสำหรับการจัดประเภทรูปภาพและการตรวจจับวัตถุ ดูเครื่องมือเหล่านี้ได้ ที่นี่

นอกจากนี้ TFLite ยังมีการทดสอบเคอร์เนลและหน่วยปฏิบัติการจำนวนมากที่สามารถนำมาใช้ซ้ำเพื่อทดสอบผู้รับมอบสิทธิ์ใหม่โดยครอบคลุมมากขึ้น และเพื่อให้มั่นใจว่าเส้นทางการดำเนินการ TFLite แบบปกติจะไม่เสียไป

หากต้องการเลือกใช้การทดสอบและเครื่องมือ TFLite ซ้ำสำหรับผู้รับมอบสิทธิ์ใหม่ คุณสามารถเลือกใช้ 1 ใน 2 ตัวเลือกต่อไปนี้

การเลือกวิธีที่ดีที่สุด

ทั้ง 2 วิธีนี้จำเป็นต้องเปลี่ยนแปลงบางอย่างตามรายละเอียดด้านล่าง อย่างไรก็ตาม วิธีการแรกจะลิงก์ผู้รับมอบสิทธิ์อย่างคงที่และจำเป็นต้องสร้างเครื่องมือการทดสอบ การเปรียบเทียบ และการประเมินผลใหม่ ในทางตรงกันข้าม วิธีที่ 2 จะมอบสิทธิ์เป็นไลบรารีที่ใช้ร่วมกัน และคุณต้องการแสดงวิธีสร้าง/ลบจากไลบรารีที่ใช้ร่วมกัน

ด้วยเหตุนี้ กลไกการมอบสิทธิ์ภายนอกจึงจะทำงานร่วมกับไบนารีเครื่องมือ Tensorflow Lite ที่สร้างไว้ล่วงหน้าของ TFLite ได้ แต่วิธีนี้อาจไม่ชัดเจนนักและอาจตั้งค่าในการทดสอบการผสานรวมอัตโนมัติอาจซับซ้อนกว่า ใช้วิธีการของผู้รับจดทะเบียนที่ได้รับมอบสิทธิ์เพื่อความชัดเจนยิ่งขึ้น

ตัวเลือกที่ 1: ใช้ผู้รับจดทะเบียนที่มอบสิทธิ์

ผู้รับจดทะเบียนที่ได้รับมอบสิทธิ์จะเก็บรายชื่อผู้ให้บริการที่ได้รับมอบสิทธิ์ไว้ ซึ่งแต่ละรายช่วยให้สร้างผู้รับมอบสิทธิ์ TFLite ได้อย่างง่ายดายโดยอิงตามแฟล็กบรรทัดคำสั่ง จึงอำนวยความสะดวกในการใช้เครื่องมือ หากต้องการมอบสิทธิ์ใหม่ให้กับเครื่องมือ Tensorflow Lite ทั้งหมดที่กล่าวถึงข้างต้น คุณต้องสร้างผู้ให้บริการผู้รับมอบสิทธิ์ใหม่ก่อน แล้วจึงทำการเปลี่ยนแปลงเล็กน้อยกับกฎ BUILD ตัวอย่างที่สมบูรณ์ของกระบวนการผสานรวมนี้จะแสดงด้านล่าง (และดูโค้ดได้ที่นี่)

สมมติว่าคุณมีผู้รับมอบสิทธิ์ที่ใช้งาน API ของ SimpleDelegate และ API "C" ภายนอกสำหรับสร้าง/ลบการมอบสิทธิ์ "จำลอง" นี้ ดังต่อไปนี้

// 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" เข้ากับเครื่องมือการเปรียบเทียบและเครื่องมืออนุมาน ให้กำหนด DelegateProvider ดังตัวอย่างต่อไปนี้

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 มีความสำคัญ เนื่องจากคุณต้องตรวจสอบว่าไลบรารีลิงก์เสมอและไม่ได้ทิ้งโดยเครื่องมือเพิ่มประสิทธิภาพ

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

ตอนนี้ ให้เพิ่มกฎ Wrapper 2 ข้อนี้ในไฟล์ BUILD เพื่อสร้างเวอร์ชันของเครื่องมือเปรียบเทียบและเครื่องมือการอนุมาน รวมถึงเครื่องมือการประเมินอื่นๆ ที่สามารถทำงานกับผู้รับมอบสิทธิ์ของคุณเอง

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

นอกจากนี้ คุณยังเชื่อมโยงผู้ให้บริการที่ได้รับมอบสิทธิ์รายนี้กับการทดสอบเคอร์เนลของ TFLite ได้ตามที่อธิบายไว้ที่นี่

ตัวเลือกที่ 2: ใช้ผู้รับมอบสิทธิ์ภายนอก

ในทางเลือกนี้ คุณต้องสร้างอะแดปเตอร์ภายนอกสำหรับ external_delegate_adaptor.cc ตามที่แสดงด้านล่างก่อน โปรดทราบว่าวิธีนี้ไม่ค่อยดีกว่าเมื่อเทียบกับตัวเลือกที่ 1 ที่กล่าวถึงก่อนหน้านี้

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

ต่อไปให้สร้างเป้าหมาย BUILD ที่เกี่ยวข้องเพื่อสร้างไลบรารีแบบไดนามิกดังที่แสดงด้านล่าง

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

หลังจากสร้างไฟล์ .so ที่มอบสิทธิ์ภายนอกนี้ขึ้น คุณจะสร้างไบนารีหรือใช้ไบนารีที่สร้างไว้ล่วงหน้าเพื่อเรียกใช้กับผู้รับมอบสิทธิ์ใหม่ ตราบใดที่ไบนารีนั้นลิงก์กับไลบรารี external_delegate_provider ที่รองรับแฟล็กบรรทัดคำสั่งตามที่อธิบายไว้ที่นี่ หมายเหตุ: ผู้ให้บริการผู้รับมอบสิทธิ์ภายนอกรายนี้ลิงก์กับไบนารีการทดสอบและเครื่องมืออยู่แล้ว

ดูคำอธิบาย ที่นี่ เพื่อดูภาพประกอบเกี่ยวกับวิธีเปรียบเทียบผู้รับมอบสิทธิ์จำลองผ่าน วิธีการมอบสิทธิ์ให้ตัวแทนภายนอกนี้ คุณใช้คำสั่งที่คล้ายกันสำหรับเครื่องมือทดสอบและประเมินตามที่กล่าวไปก่อนหน้านี้ได้

โปรดทราบว่าผู้มอบสิทธิ์ภายนอกคือการใช้งาน C++ ที่สอดคล้องกันของ delegate ในการเชื่อมโยง Tensorflow Lite Python ดังที่แสดงที่นี่ ดังนั้น ไลบรารีอะแดปเตอร์ที่มอบสิทธิ์ภายนอกแบบไดนามิกซึ่งสร้างที่นี่อาจใช้กับ Tensorflow Lite Python API ได้โดยตรง

แหล่งข้อมูล

ระบบปฏิบัติการ ที่เก็บถาวร BINARY_NAME
Linux x86_64
กลุ่ม
aarch64
Android กลุ่ม
aarch64