การใช้ตัวแทนแบบกำหนดเอง

ผู้รับมอบสิทธิ์ TensorFlow Lite คืออะไร

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

แผนภาพด้านล่างสรุปผู้ร่วมประชุม รายละเอียดเพิ่มเติมในส่วนด้านล่าง

TFLite Delegates

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

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

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

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

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

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

Original graph

หลังจากใช้ “MyDelegate” นี้ กราฟ TensorFlow Lite ดั้งเดิมจะได้รับการอัปเดตในลักษณะต่อไปนี้:

Graph with delegate

จะได้กราฟด้านบนเมื่อ TensorFlow Lite แยกกราฟต้นฉบับตามกฎสองข้อ:

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

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

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

การใช้ผู้รับมอบสิทธิ์แบบกำหนดเองของคุณเอง

วิธีที่ต้องการเพิ่มผู้รับมอบสิทธิ์คือการใช้ SimpleDelegate API

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

1 - SimpleDelegateInterface

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

2 - SimpleDelegateKernelInterface

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

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

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

ตัวอย่าง

ในตัวอย่างนี้ คุณจะสร้างผู้รับมอบสิทธิ์แบบธรรมดาที่สามารถรองรับการดำเนินการได้เพียง 2 ประเภท (ADD) และ (SUB) ด้วยเทนเซอร์ float32 เท่านั้น

// 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 สร้างอินพุตแบบสุ่ม จากนั้นรันโมเดลซ้ำๆ ตามจำนวนการรันที่ระบุ โดยจะพิมพ์สถิติเวลาแฝงรวมในตอนท้าย
  • Inference Diff Tool : สำหรับโมเดลที่กำหนด เครื่องมือจะสร้างข้อมูล Gaussian แบบสุ่มและส่งผ่านตัวแปล TFLite สองตัว ตัวหนึ่งทำงานเคอร์เนล CPU แบบเธรดเดี่ยว และอีกตัวหนึ่งใช้ข้อมูลจำเพาะที่ผู้ใช้กำหนด โดยจะวัดความแตกต่างสัมบูรณ์ระหว่างเอาท์พุตเทนเซอร์จากล่ามแต่ละตัว ในแต่ละองค์ประกอบ เครื่องมือนี้ยังมีประโยชน์สำหรับการแก้ไขข้อบกพร่องด้านความแม่นยำอีกด้วย
  • นอกจากนี้ยังมีเครื่องมือประเมินเฉพาะงานสำหรับการจำแนกภาพและการตรวจจับวัตถุ เครื่องมือเหล่านี้สามารถพบได้ ที่นี่

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

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

การเลือกแนวทางที่ดีที่สุด

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

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

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

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

สมมติว่าคุณมีผู้รับมอบสิทธิ์ที่ใช้ SimpleDelegate API และ 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 ทั้งสองข้อนี้ลงในไฟล์ 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 ++ ที่สอดคล้องกันของ ผู้รับมอบสิทธิ์ ในการผูก Tensorflow Lite Python ดังที่แสดงไว้ ที่นี่ ดังนั้น ไลบรารีอะแดปเตอร์ผู้ร่วมประชุมภายนอกแบบไดนามิกที่สร้างขึ้นที่นี่จึงสามารถใช้กับ Tensorflow Lite Python API ได้โดยตรง

ทรัพยากร

ระบบปฏิบัติการ โค้ง BINARY_NAME
ลินุกซ์ x86_64
แขน
อาร์ค64
หุ่นยนต์ แขน
อาร์ค64