גרסאות אופרטור 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 עבור הגרסה החדשה של Google.

שינוי של מבני C והטמעת ליבה (kernel)

ב-LiteRT, הטמעת הליבה מופרדת מ-FlatBuffer להגדרה. הליבה קוראת את הפרמטר ממבני C שמוגדרים lite/c/builtin_op_data.h

פרמטר הקונבולוציה המקורי של עומק כך:

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) מספק כדי לרשום ליבות פעולה (kernel). הגרסה המינימלית והמקסימלית הן 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. במשפט הזה, הטמענו ליבה (kernel) חדשה של תפעול שיכולה לטפל ב-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

השלב הבא הוא לגרום ל-TFLite לאכלס את הגרסה המינימלית הנדרשת מבצעים את הפעולה בדוגמה הזו, המשמעות היא:

  • אכלוס גרסה=1 כשכל גורמי ההרחבה הם 1.
  • אכלוס גרסה=2, אחרת.

שינוי הפונקציה GetBuiltinOperatorVersion עבור האופרטור ב- lite/tools/versioning/op_version.cc על ידי הוספת הגרסה החדשה למקרה של 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;

עדכון מפת הגרסאות של המפעיל

השלב האחרון הוא להוסיף את פרטי הגרסה החדשה למפת הגרסאות של האופרטור. הזה בפעימה הזו, כי אנחנו צריכים ליצור את הגרסה של סביבת זמן הריצה על סמך מפת הגרסאות הזו.

לשם כך, עליך להוסיף רשומת מפה חדשה ב- lite/tools/versioning/runtime_version.cc

בדוגמה הזו, צריך להוסיף את הערך הבא ל-op_version_map:

{ {BuiltinOperator_DEPTHWISE_CONV_2D, 2}, %CURRENT_RUNTIME_VERSION%}

כאשר %CURRENT_RUNTIME_VERSION% תואם לגרסה הנוכחית של סביבת זמן הריצה מוגדר ב-tensorflow/core/public/version.h.

הטמעת הענקת גישה

פלטפורמת LiteRT מספקת API להענקת גישה שמאפשר הענקת פעולות ל- בקצה העורפי של החומרה. בפונקציה 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, לכן הענקת גישה יכולה לזהות אי-תאימות כאשר משיגים הפעלת גרסה גבוהה יותר.