LiteRT ऑपरेटर के वर्शन

इस दस्तावेज़ में, LiteRT के ओप वर्शनिंग स्कीमा के बारे में बताया गया है. ऑप वर्शनिंग की मदद से, डेवलपर मौजूदा ऑप में नई सुविधाएं और पैरामीटर जोड़ सकते हैं. इसके अलावा, यह इन बातों की गारंटी देता है:

  • पिछले वर्शन के साथ काम करने की सुविधा: LiteRT के नए वर्शन को पुराने मॉडल फ़ाइल को हैंडल करना चाहिए.
  • फ़ॉरवर्ड कंपैटिबिलिटी: LiteRT के पुराने वर्शन को, कनवर्टर के नए वर्शन से बनाई गई नई मॉडल फ़ाइल को हैंडल करना चाहिए. हालांकि, ऐसा तब ही होगा, जब कोई नई सुविधा इस्तेमाल न की गई हो.
  • आगे की ओर काम न करने की समस्या का पता लगाना: अगर LiteRT का पुराना वर्शन, ऐसे नए मॉडल को पढ़ता है जिसमें किसी ऐसे ऑप का नया वर्शन शामिल है जो काम नहीं करता, तो उसे गड़बड़ी की सूचना देनी चाहिए.

उदाहरण: डेप्थवाइज़ कनवोल्यूशन में डाइलटेशन जोड़ना

इस दस्तावेज़ के बाकी हिस्से में, TFLite में ऑपरेटर वर्शनिंग के बारे में बताया गया है. इसमें यह दिखाया गया है कि डेप्थवाइज़ कनवोल्यूशन ऑपरेशन में डाइलटेशन पैरामीटर कैसे जोड़े जाते हैं.

इस दस्तावेज़ को समझने के लिए, डाइलेशन के बारे में जानकारी होना ज़रूरी नहीं है. ध्यान दें कि:

  • दो नए पूर्णांक पैरामीटर जोड़े जाएंगे: dilation_width_factor और dilation_height_factor.
  • डाइलेशन की सुविधा के साथ काम न करने वाले पुराने डेप्थवाइज़ कनवोल्यूशन कर्नल, डाइलेशन फ़ैक्टर को 1 पर सेट करने के बराबर होते हैं.

FlatBuffer स्कीमा बदलना

किसी ऑपरेशन में नए पैरामीटर जोड़ने के लिए, lite/schema/schema.fbs में मौजूद विकल्पों की टेबल में बदलाव करें.

उदाहरण के लिए, डेप्थवाइज़ कनवोल्यूशन की विकल्प टेबल इस तरह दिखती है:

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

नए पैरामीटर जोड़ते समय:

  • टिप्पणियां जोड़कर बताएं कि कौनसे पैरामीटर, किस वर्शन के साथ काम करते हैं.
  • जब नए पैरामीटर के लिए डिफ़ॉल्ट वैल्यू सेट की जाती हैं, तब नया वर्शन पुराने वर्शन की तरह ही काम करता है.

नए पैरामीटर जोड़ने के बाद, टेबल इस तरह दिखेगी:

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

नई स्कीमा के लिए, lite/schema/schema_generated.h फ़ाइल को फिर से जनरेट किया जाना चाहिए.

C स्ट्रक्चर और कर्नल के लागू करने के तरीके में बदलाव करना

LiteRT में, कर्नेल को लागू करने की प्रोसेस को FlatBuffer की परिभाषा से अलग किया जाता है. कर्नल, lite/c/builtin_op_data.h में तय किए गए C स्ट्रक्चर से पैरामीटर पढ़ते हैं.

डेप्थवाइज़ कनवोल्यूशन का ओरिजनल पैरामीटर यह है:

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

FlatBuffer स्कीमा की तरह, टिप्पणियां जोड़ें. इनमें यह बताएं कि कौनसे पैरामीटर, किस वर्शन से काम करते हैं. नतीजा यहां दिया गया है:

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;

कृपया कर्नल के लागू करने के तरीके में भी बदलाव करें, ताकि C स्ट्रक्चर से नए जोड़े गए पैरामीटर पढ़े जा सकें. यहाँ जानकारी नहीं दी गई है.

FlatBuffer को पढ़ने वाले कोड में बदलाव करना

FlatBuffer को पढ़ने और C स्ट्रक्चर बनाने का लॉजिक, lite/core/api/flatbuffer_conversions.cc में होता है.

नीचे दिए गए तरीके से, नई फ़ाइल को अपडेट करें, ताकि नए पैरामीटर को मैनेज किया जा सके:

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

यहां ओएस के वर्शन की जांच करने की ज़रूरत नहीं है. जब नया मॉडल, पुरानी मॉडल फ़ाइल को पढ़ता है और उसमें डाइलटेशन फ़ैक्टर मौजूद नहीं होते हैं, तो यह डिफ़ॉल्ट वैल्यू के तौर पर 1 का इस्तेमाल करेगा. साथ ही, नया कर्नल पुराने कर्नल के साथ लगातार काम करेगा.

कर्नेल रजिस्ट्रेशन बदलना

MutableOpResolver (lite/mutable_op_resolver.h में तय किया गया) op कर्नल रजिस्टर करने के लिए कुछ फ़ंक्शन उपलब्ध कराता है. कम से कम और ज़्यादा से ज़्यादा वर्शन, डिफ़ॉल्ट रूप से 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);

पहले से मौजूद ऑप्स, lite/kernels/register.cc में रजिस्टर होते हैं. इस उदाहरण में, हमने एक नया ऑप कर्नल लागू किया है, जो DepthwiseConv2D के वर्शन 1 और 2 को हैंडल कर सकता है. इसलिए, हमें इस लाइन को बदलना होगा:

AddBuiltin(BuiltinOperator_DEPTHWISE_CONV_2D, Register_DEPTHWISE_CONV_2D());

इससे बदलें:

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

TFLite op वर्शन बदलना

अगला चरण, TFLite को उस कम से कम वर्शन के बारे में बताना है जो op को चलाने के लिए ज़रूरी है. इस उदाहरण में इसका मतलब है:

  • जब सभी डाइलटेशन फ़ैक्टर 1 हों, तब version=1 भरें.
  • अन्य मामलों में, version=2 को पॉप्युलेट करें.

DepthwiseConv2D के मामले में नया वर्शन जोड़कर, lite/tools/versioning/op_version.cc में ऑपरेटर के लिए GetBuiltinOperatorVersion फ़ंक्शन में बदलाव करें:

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;

ऑपरेटर के वर्शन का मैप अपडेट करना

आखिरी चरण में, ऑपरेटर वर्शन के मैप में नए वर्शन की जानकारी जोड़नी होती है. यह चरण ज़रूरी है, क्योंकि हमें इस वर्शन मैप के आधार पर, मॉडल के लिए कम से कम ज़रूरी रनटाइम वर्शन जनरेट करना होता है.

इसके लिए, आपको lite/tools/versioning/runtime_version.cc में नई मैप एंट्री जोड़नी होगी.

इस उदाहरण में, आपको op_version_map में यह एंट्री जोड़नी होगी:

{ {BuiltinOperator_DEPTHWISE_CONV_2D, 2}, %CURRENT_RUNTIME_VERSION%}

यहां %CURRENT_RUNTIME_VERSION%, release_version.h में तय किए गए मौजूदा रनटाइम वर्शन से मेल खाता है.

डेलिगेशन की सुविधा लागू करना

LiteRT, डेलिगेशन एपीआई उपलब्ध कराता है. इससे हार्डवेयर बैकएंड को ऑपरेशंस डेलिगेट किए जा सकते हैं. डेलिगेट के Prepare फ़ंक्शन में, देखें कि डेलिगेशन कोड में मौजूद हर नोड के लिए वर्शन काम करता है या नहीं.

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

अगर डेलिगेशन सिर्फ़ वर्शन 1 के ऑपरेशंस के साथ काम करता है, तो भी इसकी ज़रूरत होती है. इससे डेलिगेशन को यह पता चल पाता है कि ज़्यादा नए वर्शन का ऑपरेशन इस्तेमाल नहीं किया जा सकता.