Phiên bản toán tử LiteRT

Tài liệu này mô tả giản đồ lập phiên bản op của LiteRT. Tính năng quản lý phiên bản của thao tác cho phép nhà phát triển thêm các chức năng và tham số mới vào các thao tác hiện có. Ngoài ra, tính năng này còn đảm bảo những điều sau:

  • Khả năng tương thích ngược: Phương thức triển khai LiteRT mới sẽ xử lý một tệp mô hình cũ.
  • Khả năng tương thích về sau: Phương thức triển khai LiteRT cũ sẽ xử lý tệp mô hình mới do phiên bản mới của trình chuyển đổi tạo ra, miễn là không có tính năng mới nào được sử dụng.
  • Phát hiện sự không tương thích trong tương lai: Nếu một quy trình triển khai LiteRT cũ đọc một mô hình mới chứa phiên bản mới của một thao tác không được hỗ trợ, thì quy trình này sẽ báo cáo lỗi.

Ví dụ: Thêm độ giãn nở vào phép tích chập theo chiều sâu

Phần còn lại của tài liệu này giải thích cách tạo phiên bản op trong TFLite bằng cách cho biết cách thêm các tham số giãn nở vào thao tác tích chập theo chiều sâu.

Bạn không cần có kiến thức về phép giãn nở để hiểu tài liệu này. Lưu ý:

  • 2 tham số số nguyên mới sẽ được thêm vào: dilation_width_factordilation_height_factor.
  • Các nhân tích chập theo chiều sâu cũ không hỗ trợ giãn nở tương đương với việc đặt hệ số giãn nở thành 1.

Thay đổi giản đồ FlatBuffer

Để thêm các tham số mới vào một thao tác, hãy thay đổi bảng lựa chọn trong lite/schema/schema.fbs.

Ví dụ: bảng tuỳ chọn của phép tích chập theo chiều sâu có dạng như sau:

table DepthwiseConv2DOptions {
  padding:Padding;
  stride_w:int;
  stride_h:int;
  depth_multiplier:int;
  fused_activation_function:ActivationFunctionType;
}

Khi thêm thông số mới:

  • Thêm nhận xét cho biết phiên bản nào hỗ trợ tham số nào.
  • Khi quá trình triển khai mới nhận được các giá trị mặc định cho các thông số mới được thêm, thì quá trình này sẽ hoạt động giống hệt như quá trình triển khai cũ.

Sau khi bạn thêm các tham số mới, bảng sẽ có dạng như sau:

table DepthwiseConv2DOptions {
  // Parameters for DepthwiseConv version 1 or above.
  padding:Padding;
  stride_w:int;
  stride_h:int;
  depth_multiplier:int;
  fused_activation_function:ActivationFunctionType;
  // Parameters for DepthwiseConv version 2 or above.
  dilation_w_factor:int = 1;
  dilation_h_factor:int = 1;
}

Bạn nên tạo lại tệp lite/schema/schema_generated.h cho giản đồ mới.

Thay đổi cấu trúc C và việc triển khai nhân

Trong LiteRT, việc triển khai nhân hệ điều hành tách biệt với định nghĩa FlatBuffer. Các nhân đọc tham số từ các cấu trúc C được xác định trong lite/c/builtin_op_data.h.

Tham số tích chập theo chiều sâu ban đầu như sau:

typedef struct {
  TfLitePadding padding;
  int stride_width;
  int stride_height;
  int depth_multiplier;
  TfLiteFusedActivation activation;
} TfLiteDepthwiseConvParams;

Giống như với giản đồ FlatBuffer, hãy thêm nhận xét cho biết những tham số nào được hỗ trợ bắt đầu từ phiên bản nào. Kết quả như sau:

typedef struct {
  // Parameters for DepthwiseConv version 1 or above.
  TfLitePadding padding;
  int stride_width;
  int stride_height;
  int depth_multiplier;
  TfLiteFusedActivation activation;
  // Parameters for DepthwiseConv version 2 or above.
  int dilation_width_factor;
  int dilation_height_factor;
} TfLiteDepthwiseConvParams;

Vui lòng thay đổi việc triển khai kernel để đọc các tham số mới được thêm từ cấu trúc C. Thông tin chi tiết sẽ không được đề cập ở đây.

Thay đổi mã đọc FlatBuffer

Logic để đọc FlatBuffer và tạo cấu trúc C nằm trong lite/core/api/flatbuffer_conversions.cc.

Cập nhật tệp để xử lý các tham số mới, như minh hoạ bên dưới:

TfLiteStatus ParseDepthwiseConv2D(const Operator* op,
                                  ErrorReporter* error_reporter,
                                  BuiltinDataAllocator* allocator,
                                  void** builtin_data) {
  CheckParsePointerParams(op, error_reporter, allocator, builtin_data);

  SafeBuiltinDataAllocator safe_allocator(allocator);

  std::unique_ptr<TfLiteDepthwiseConvParams,
                  SafeBuiltinDataAllocator::BuiltinDataDeleter>
      params = safe_allocator.Allocate<TfLiteDepthwiseConvParams>();
  TF_LITE_ENSURE(error_reporter, params != nullptr);

  const DepthwiseConv2DOptions* schema_params =
      op->builtin_options_as_DepthwiseConv2DOptions();

  if (schema_params != nullptr) {
    params->padding = ConvertPadding(schema_params->padding());
    params->stride_width = schema_params->stride_w();
    params->stride_height = schema_params->stride_h();
    params->depth_multiplier = schema_params->depth_multiplier();
    params->activation =
        ConvertActivation(schema_params->fused_activation_function());

    params->dilation_width_factor = schema_params->dilation_w_factor();
    params->dilation_height_factor = schema_params->dilation_h_factor();
  }

  *builtin_data = params.release();
  return kTfLiteOk;
}

Bạn không bắt buộc phải kiểm tra phiên bản op tại đây. Khi triển khai mới đọc một tệp mô hình cũ mà không có hệ số giãn nở, tệp đó sẽ dùng 1 làm giá trị mặc định và nhân mới sẽ hoạt động nhất quán với nhân cũ.

Thay đổi thông tin đăng ký nhân

MutableOpResolver (được xác định trong lite/mutable_op_resolver.h) cung cấp một số hàm để đăng ký các hạt nhân op. Theo mặc định, phiên bản tối thiểu và tối đa là 1:

void AddBuiltin(tflite::BuiltinOperator op, TfLiteRegistration* registration,
                int min_version = 1, int max_version = 1);
void AddCustom(const char* name, TfLiteRegistration* registration,
               int min_version = 1, int max_version = 1);

Các thao tác tích hợp sẵn được đăng ký trong lite/kernels/register.cc. Trong ví dụ này, chúng ta đã triển khai một hạt nhân op mới có thể xử lý DepthwiseConv2D phiên bản 1 và 2, vì vậy, chúng ta cần thay đổi dòng này:

AddBuiltin(BuiltinOperator_DEPTHWISE_CONV_2D, Register_DEPTHWISE_CONV_2D());

tới:

AddBuiltin(BuiltinOperator_DEPTHWISE_CONV_2D, Register_DEPTHWISE_CONV_2D(),
             /* min_version = */ 1,
             /* max_version = */ 2);

Thay đổi phiên bản op TFLite

Bước tiếp theo là để TFLite điền phiên bản tối thiểu cần thiết để thực thi thao tác. Trong ví dụ này, điều đó có nghĩa là:

  • Điền phiên bản=1 khi tất cả các hệ số giãn nở đều là 1.
  • Nếu không, hãy điền phiên bản=2.

Sửa đổi hàm GetBuiltinOperatorVersion cho toán tử trong lite/tools/versioning/op_version.cc bằng cách thêm phiên bản mới vào trường hợp DepthwiseConv2D:

case BuiltinOperator_DEPTHWISE_CONV_2D:
  auto depthwise_conv_params =
      reinterpret_cast<TfLiteDepthwiseConvParams*>(op_sig.builtin_data);
  TFLITE_DCHECK(depthwise_conv_params != nullptr);
  if (depthwise_conv_params->dilation_width_factor != 1 ||
       depthwise_conv_params->dilation_height_factor != 1) {
    return 2;
  }
  return 1;

Cập nhật bản đồ phiên bản của toán tử

Bước cuối cùng là thêm thông tin phiên bản mới vào bản đồ phiên bản của toán tử. Đây là bước bắt buộc vì chúng ta cần tạo phiên bản thời gian chạy tối thiểu bắt buộc của mô hình dựa trên bản đồ phiên bản này.

Để làm như vậy, bạn cần thêm một mục nhập bản đồ mới trong lite/tools/versioning/runtime_version.cc.

Trong ví dụ này, bạn cần thêm mục sau vào op_version_map:

{ {BuiltinOperator_DEPTHWISE_CONV_2D, 2}, %CURRENT_RUNTIME_VERSION%}

trong đó %CURRENT_RUNTIME_VERSION% tương ứng với phiên bản thời gian chạy hiện tại được xác định trong release_version.h.

Triển khai uỷ quyền

LiteRT cung cấp một API uỷ quyền cho phép uỷ quyền các hoạt động cho các phần phụ trợ phần cứng. Trong hàm Prepare của uỷ quyền, hãy kiểm tra xem phiên bản có được hỗ trợ cho mọi nút trong mã Uỷ quyền hay không.

const int kMaxVersion = 1;
TfLiteNode* node;
TfLiteRegistration* registration = nullptr;
TF_LITE_ENSURE_STATUS(context->GetNodeAndRegistration(context, node_index, &node, &registration));

if (registration->version > kMaxVersion) {
  // Reject the node if the version isn't supported.
}

Điều này là bắt buộc ngay cả khi việc uỷ quyền chỉ hỗ trợ các thao tác phiên bản 1, vì vậy, việc uỷ quyền có thể phát hiện sự không tương thích khi nhận được một thao tác phiên bản cao hơn.