Versionen des LiteRT-Operators

In diesem Dokument wird das operative Versionsverwaltungsschema von LiteRT beschrieben. Op-Versionsverwaltung ermöglicht es Entwicklern, neue Funktionen und Parameter in vorhandene Abläufe zu integrieren. Darüber hinaus wird Folgendes garantiert:

  • Abwärtskompatibilität: Die neue LiteRT-Implementierung sollte eine alten Modelldatei.
  • Aufwärtskompatibilität: Die alte LiteRT-Implementierung sollte eine Neue Modelldatei, die von der neuen Version des Konverters erstellt wird, solange keine neuen Funktionen verwendet werden.
  • Erkennung von Inkompatibilitäten bei Weiterleitung: Wenn eine alte LiteRT-Implementierung ein neues Modell liest, das eine neue Version eines Vorgangs enthält, unterstützt, sollte der Fehler gemeldet werden.

Beispiel: Dilation zur tiefen Faltung hinzufügen

Im weiteren Verlauf dieses Dokuments wird die Versionsverwaltung in TFLite erläutert, indem gezeigt wird, wie um Dilatationsparameter zur tiefenweisen Faltung hinzuzufügen.

Zum Verständnis dieses Dokuments sind keine Kenntnisse der Dilatation erforderlich. Hinweis:

  • Zwei neue Ganzzahlparameter werden hinzugefügt: dilation_width_factor und dilation_height_factor.
  • Alte Kernel mit tiefgehender Faltung, die keine Dilatation unterstützen, sind äquivalent Dilatationsfaktoren auf 1 zu setzen.

FlatBuffer-Schema ändern

Um einem Vorgang neue Parameter hinzuzufügen, ändern Sie die Optionstabelle in lite/schema/schema.fbs

Die Optionstabelle für die tiefe Faltung sieht beispielsweise so aus:

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

Beim Hinzufügen neuer Parameter:

  • Fügen Sie Kommentare hinzu, die angeben, welche Parameter von welcher Version unterstützt werden.
  • Wenn die neue Implementierung die Standardwerte für neu hinzugefügte Parameter verwenden, sollte sie genauso funktionieren wie die alte Implementierung.

Die Tabelle sieht nach dem Hinzufügen der neuen Parameter so aus:

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

Die Datei lite/schema/schema_generated.h sollte für die neue Schema.

C-Strukturen und Kernel-Implementierung ändern

In LiteRT ist die Kernel-Implementierung von FlatBuffer entkoppelt Definition. Die Kernel lesen den Parameter aus den C-Strukturen, die in lite/c/builtin_op_data.h

Der ursprüngliche Parameter für die tiefeseitige Faltung lautet wie folgt:

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

Wie beim FlatBuffer-Schema können Sie auch hier Kommentare hinzufügen, die angeben, welche Parameter ab welcher Version unterstützt wird. Das Ergebnis sieht so aus:

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;

Ändern Sie auch die Kernel-Implementierung, damit die neu hinzugefügten Parameter gelesen werden. aus den C-Strukturen. Die Details werden hier weggelassen.

FlatBuffer-Lesecode ändern

Die Logik zum Lesen von FlatBuffer und Erstellen der C-Struktur befindet sich in lite/core/api/flatbuffer_conversions.cc

Aktualisieren Sie die Datei so, dass die neuen Parameter verarbeitet werden:

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

Es ist nicht erforderlich, die Op-Version hier zu prüfen. Wenn die neue Implementierung eine alte Modelldatei liest, bei der Dilatationsfaktoren fehlen, wird 1 als Der neue Kernel funktioniert dann einheitlich mit dem alten.

Kernel-Registrierung ändern

Der MutableOpResolver (definiert in lite/mutable_op_resolver.h) bietet einige zum Registrieren von Vorgangs-Kerneln. Die Mindest- und Höchstversion sind 1 zu Standardeinstellung:

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

Die integrierten Vorgänge sind in lite/kernels/register.cc registriert. In diesem Beispiel Wir haben einen neuen Operations-Kernel implementiert, der DepthwiseConv2D Version 1 und 2, also müssen wir diese Zeile ändern:

AddBuiltin(BuiltinOperator_DEPTHWISE_CONV_2D, Register_DEPTHWISE_CONV_2D());

in

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

TFLite-Vorgangsversion ändern

Im nächsten Schritt legen Sie fest, dass TFLite die Mindestversion ausfüllt, die für die Ausführung den Vorgang ausführen. In diesem Beispiel bedeutet das:

  • Füllen Sie Version=1 aus, wenn die Dilatationsfaktoren alle 1 sind.
  • Andernfalls füllen Sie Version 2 aus.

Funktion GetBuiltinOperatorVersion für den Operator ändern in lite/tools/versioning/op_version.cc, indem Sie die neue Version zum Fall von 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;

Operatorversionszuordnung aktualisieren

Im letzten Schritt werden die Informationen zur neuen Version zur Versionszuordnung des Operators hinzugefügt. Dieses Schritt ist erforderlich, da wir das Minimum für das Modell generieren müssen, Laufzeitversion basierend auf dieser Versionszuordnung.

Dazu müssen Sie in lite/tools/versioning/runtime_version.cc

In diesem Beispiel müssen Sie op_version_map den folgenden Eintrag hinzufügen:

{ {BuiltinOperator_DEPTHWISE_CONV_2D, 2}, %CURRENT_RUNTIME_VERSION%}

wobei %CURRENT_RUNTIME_VERSION% der aktuellen Laufzeitversion entspricht definiert in tensorflow/core/public/version.h.

Implementierung der Delegierung

LiteRT bietet ein Delegations-API, das das Delegieren von Vorgängen an Hardware-Back-Ends. Prüfen Sie in der Funktion Prepare des Bevollmächtigten, ob die Version wird für jeden Knoten im Delegationscode unterstützt.

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

Dies ist auch dann erforderlich, wenn die Delegierung nur Vorgänge der Version 1 unterstützt, sodass der Die Delegierung kann Inkompatibilität erkennen, wenn ein höherer Versionsvorgang abgerufen wird.