In diesem Dokument wird das Schema für die Versionsverwaltung von LiteRT-Vorgängen beschrieben. Mit der Versionsverwaltung von Operationen können Entwickler vorhandenen Operationen neue Funktionen und Parameter hinzufügen. Außerdem wird Folgendes garantiert:
- Abwärtskompatibilität: Die neue LiteRT-Implementierung sollte eine alte Modelldatei verarbeiten können.
- Vorwärtskompatibilität: Die alte LiteRT-Implementierung sollte eine neue Modelldatei verarbeiten können, die von einer neuen Version des Konverters erstellt wurde, sofern keine neuen Funktionen verwendet werden.
- Erkennung von Inkompatibilitäten in der Zukunft: Wenn eine alte LiteRT-Implementierung ein neues Modell liest, das eine neue Version eines nicht unterstützten Vorgangs enthält, sollte ein Fehler gemeldet werden.
Beispiel: Dilation in die faltende Schicht einfügen
Im Rest dieses Dokuments wird die Versionsverwaltung von Operationen in TFLite erläutert. Dazu wird gezeigt, wie der Operation für die faltende Schicht mit Tiefeninformationen (depthwise convolution) Dilation-Parameter hinzugefügt werden.
Kenntnisse über die Dilation sind nicht erforderlich, um dieses Dokument zu verstehen. Hinweis:
- Es werden zwei neue Ganzzahlparameter hinzugefügt:
dilation_width_factorunddilation_height_factor. - Alte Faltungs-Kernels für die Tiefenberechnung, die keine Erweiterung unterstützen, entsprechen dem Festlegen der Erweiterungsfaktoren auf 1.
FlatBuffer-Schema ändern
Wenn Sie einem Vorgang neue Parameter hinzufügen möchten, ändern Sie die Optionstabelle in lite/schema/schema.fbs.
Die Tabelle mit den Optionen für die faltende Schicht sieht beispielsweise so aus:
table DepthwiseConv2DOptions {
padding:Padding;
stride_w:int;
stride_h:int;
depth_multiplier:int;
fused_activation_function:ActivationFunctionType;
}
Wenn Sie neue Parameter hinzufügen, gilt Folgendes:
- Fügen Sie Kommentare hinzu, in denen angegeben wird, welche Parameter von welcher Version unterstützt werden.
- Wenn die neue Implementierung die Standardwerte für neu hinzugefügte Parameter erhält, sollte sie genau wie die alte Implementierung funktionieren.
So sieht die Tabelle aus, nachdem die neuen Parameter hinzugefügt wurden:
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 das neue Schema neu generiert werden.
C-Strukturen und Kernel-Implementierung ändern
In LiteRT ist die Kernel-Implementierung von der FlatBuffer-Definition entkoppelt.
Die Parameter werden von den Kernels aus C-Strukturen gelesen, die in lite/c/builtin_op_data.h definiert sind.
Der ursprüngliche Parameter für die tiefenweise Faltung lautet:
typedef struct {
TfLitePadding padding;
int stride_width;
int stride_height;
int depth_multiplier;
TfLiteFusedActivation activation;
} TfLiteDepthwiseConvParams;
Fügen Sie wie beim FlatBuffer-Schema Kommentare hinzu, die angeben, welche Parameter ab welcher Version unterstützt werden. Das Ergebnis ist unten zu sehen:
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, um die neu hinzugefügten Parameter aus den C-Strukturen zu lesen. Die Details werden hier ausgelassen.
Code zum Lesen von FlatBuffers ändern
Die Logik zum Lesen von FlatBuffer und zum Erstellen der C-Struktur befindet sich in lite/core/api/flatbuffer_conversions.cc.
Aktualisieren Sie die Datei, um die neuen Parameter zu verarbeiten, wie unten gezeigt:
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;
}
Hier ist es nicht erforderlich, die Op-Version zu prüfen. Wenn bei der neuen Implementierung eine alte Modelldatei gelesen wird, in der keine Dilation-Faktoren vorhanden sind, wird 1 als Standardwert verwendet und der neue Kernel funktioniert konsistent mit dem alten Kernel.
Kernelregistrierung ändern
Der MutableOpResolver (definiert in lite/mutable_op_resolver.h) bietet einige Funktionen zum Registrieren von Op-Kernels. Die Mindest- und Höchstversion sind standardmäßig 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);
Die integrierten Vorgänge sind in lite/kernels/register.cc registriert. In diesem Beispiel haben wir einen neuen Op-Kernel implementiert, der Version 1 und 2 von DepthwiseConv2D verarbeiten kann. Daher 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-Op-Version ändern
Im nächsten Schritt muss TFLite die Mindestversion angeben, die zum Ausführen des Vorgangs erforderlich ist. In diesem Beispiel bedeutet das:
- Geben Sie „version=1“ an, wenn alle Dehnungsfaktoren 1 sind.
- Füllen Sie andernfalls „version=2“ aus.
Ändern Sie die Funktion GetBuiltinOperatorVersion für den Operator in lite/tools/versioning/op_version.cc, indem Sie die neue Version zum Fall von DepthwiseConv2D hinzufügen:
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 fügen Sie die Informationen zur neuen Version in die Operator-Versionszuordnung ein. Dieser Schritt ist erforderlich, da wir die für das Modell erforderliche Mindestlaufzeitversion auf Grundlage dieser Versionszuordnung generieren müssen.
Dazu müssen Sie in lite/tools/versioning/runtime_version.cc einen neuen Karteneintrag hinzufügen.
In diesem Beispiel müssen Sie den folgenden Eintrag in op_version_map hinzufügen:
{ {BuiltinOperator_DEPTHWISE_CONV_2D, 2}, %CURRENT_RUNTIME_VERSION%}
Dabei entspricht %CURRENT_RUNTIME_VERSION% der aktuellen Laufzeitversion, die in release_version.h definiert ist.
Implementierung der Delegierung
LiteRT bietet eine Delegierungs-API, mit der Vorgänge an Hardware-Backends delegiert werden können. Prüfen Sie in der Prepare-Funktion des Delegaten, ob die Version für jeden Knoten im Delegationscode unterstützt wird.
const int kMaxVersion = 1;
TfLiteNode* node;
TfLiteRegistration* registration = nullptr;
TF_LITE_ENSURE_STATUS(context->GetNodeAndRegistration(context, node_index, &node, ®istration));
if (registration->version > kMaxVersion) {
// Reject the node if the version isn't supported.
}
Dies ist auch dann erforderlich, wenn die Delegation nur Vorgänge der Version 1 unterstützt, damit die Delegation Inkompatibilitäten erkennen kann, wenn ein Vorgang einer höheren Version empfangen wird.