Versi operator LiteRT

Dokumen ini menjelaskan skema pembuatan versi operasi LiteRT. Pembuatan versi operasi memungkinkan developer untuk menambahkan fungsionalitas dan parameter baru ke dalam operasi yang sudah ada. Selain itu, kebijakan ini menjamin hal-hal berikut:

  • Kompatibilitas mundur: Implementasi LiteRT baru harus menangani file model lama.
  • Kompatibilitas dengan versi baru: Implementasi LiteRT lama harus menangani file model baru yang dihasilkan oleh konverter versi baru, asalkan tidak ada fitur yang digunakan.
  • Teruskan deteksi kompatibilitas: Jika implementasi LiteRT lama membaca model baru yang berisi versi baru sebuah op yang tidak didukung, error tersebut akan dilaporkan.

Contoh: Menambahkan dilatasi ke konvolusi depthwise

Bagian selanjutnya dari dokumen ini akan menjelaskan pembuatan versi operasi di TFLite dengan menunjukkan cara untuk menambahkan parameter dilatasi ke operasi konvolusi {i>depthwise<i}.

Pengetahuan tentang dilatasi tidak diperlukan untuk memahami dokumen ini. Perhatikan bahwa:

  • 2 parameter bilangan bulat baru akan ditambahkan: dilation_width_factor dan dilation_height_factor.
  • Kernel konvolusi depthwise lama yang tidak mendukung dilatasi adalah sama untuk menyetel faktor dilatasi ke 1.

Mengubah skema FlatBuffer

Untuk menambahkan parameter baru ke dalam sebuah op, ubah tabel opsi di lite/schema/schema.fbs.

Misalnya, tabel opsi konvolusi depthwise terlihat seperti ini:

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

Saat menambahkan parameter baru:

  • Tambahkan komentar yang menunjukkan parameter yang didukung oleh versi tertentu.
  • Saat implementasi baru mendapatkan nilai default untuk yang baru ditambahkan parameter tersebut, fungsi ini harus bekerja sama persis dengan implementasi lama.

Tabel akan menjadi seperti ini setelah parameter baru ditambahkan:

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;
}

File lite/schema/schema_generated.h harus dibuat ulang untuk skema.

Mengubah struktur C dan implementasi {i>kernel<i}

Di LiteRT, implementasi kernel dipisahkan dari FlatBuffer definisi. {i>Kernel<i} membaca parameter dari struktur C yang ditentukan dalam lite/c/builtin_op_data.h.

Parameter konvolusi depthwise asli adalah sebagai berikut:

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

Seperti pada skema FlatBuffer, tambahkan komentar yang menunjukkan parameter mana didukung mulai dari versi mana. Hasilnya terlihat di bawah ini:

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;

Ubah juga implementasi kernel untuk membaca parameter yang baru ditambahkan dari struktur C. Detailnya dihilangkan di sini.

Mengubah kode pembacaan FlatBuffer

Logika untuk membaca FlatBuffer dan menghasilkan struktur C ada dalam lite/core/api/flatbuffer_conversions.cc.

Update file untuk menangani parameter baru, seperti yang ditunjukkan di bawah ini:

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;
}

Anda tidak perlu memeriksa versi operasi di sini. Kapan implementasi baru membaca file model lama yang faktor dilatasinya tidak ada, 1 akan digunakan sebagai nilai {i>default<i}-nya, dan {i>kernel<i} baru akan bekerja secara konsisten dengan {i>kernel<i} lama.

Mengubah pendaftaran kernel

MutableOpResolver (ditentukan dalam lite/mutable_op_resolver.h) menyediakan beberapa untuk mendaftarkan kernel op. Versi minimum dan maksimum adalah 1 kali default:

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

Operasi bawaan terdaftar di lite/kernels/register.cc. Dalam contoh ini, kami mengimplementasikan kernel op baru yang dapat menangani DepthwiseConv2D versi 1 dan 2, jadi kita perlu mengubah baris ini:

AddBuiltin(BuiltinOperator_DEPTHWISE_CONV_2D, Register_DEPTHWISE_CONV_2D());

menjadi:

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

Ubah versi operasi TFLite

Langkah selanjutnya adalah membuat TFLite mengisi versi minimum yang diperlukan untuk menjalankan operasi tersebut. Dalam contoh ini, artinya:

  • Isi version=1 jika faktor dilatasi semuanya 1.
  • Isi version=2 jika tidak.

Ubah fungsi GetBuiltinOperatorVersion untuk operator di lite/tools/versioning/op_version.cc dengan menambahkan versi baru ke kasus 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;

Memperbarui peta versi operator

Langkah terakhir adalah menambahkan info versi baru ke dalam peta versi operator. Ini diperlukan karena kita perlu membuat persyaratan minimum model versi runtime berdasarkan peta versi ini.

Untuk melakukan ini, Anda perlu menambahkan entri peta baru di lite/tools/versioning/runtime_version.cc.

Dalam contoh ini, Anda perlu menambahkan entri berikut ke op_version_map:

{ {BuiltinOperator_DEPTHWISE_CONV_2D, 2}, %CURRENT_RUNTIME_VERSION%}

dengan %CURRENT_RUNTIME_VERSION% sesuai dengan versi runtime saat ini yang ditentukan di tensorflow/core/public/version.h.

Penerapan delegasi

LiteRT menyediakan API delegasi yang memungkinkan pendelegasian operasi untuk backend perangkat keras. Dalam fungsi Prepare delegasi, periksa apakah versinya didukung untuk setiap node dalam kode Delegasi.

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.
}

Ini diperlukan bahkan jika delegasi hanya mendukung operasi versi 1, sehingga delegasi dapat mendeteksi inkompatibilitas ketika mendapatkan operasi versi yang lebih tinggi.