גרסאות של מפעילי TensorFlow Lite

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

  • תאימות לאחור: ההטמעה החדשה של TensorFlow Lite אמורה לטפל בקובץ מודל ישן.
  • תאימות קדימה: ההטמעה של ישן TensorFlow Lite צריכה לטפל בקובץ מודל חדש שנוצר על ידי גרסה חדשה של הממיר, כל עוד לא משתמשים בתכונות חדשות.
  • זיהוי העברה תואמת: אם בהטמעה ישנה של TensorFlow Lite מופיע מודל חדש שמכיל גרסה חדשה של פעולה שלא נתמכת, צריך לדווח על השגיאה.

דוגמה: הוספת הרחבות לניתוח מעמיק יותר

בהמשך המסמך הזה מוסבר על גרסאות תפעול ב-TFLite על ידי הוספת פרמטרים של הרחבה לפעולת הקונבולוציה של העומק.

לא נדרש ידע בהרחבה כדי להבין את המסמך הזה. הערה:

  • יתווספו 2 פרמטרים חדשים מסוג מספרים שלמים: 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 והטמעת ליבה

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

השלב הבא הוא לגרום ל-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.

איך מטמיעים גישה

TensorFlow Lite מספק 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, כדי שהענקת גישה תוכל לזהות חוסר תאימות כשמקבלים גרסה מתקדמת יותר.