In diesem Dokument wird das Schema der Betriebsversionsverwaltung von TensorFlow Lite beschrieben. Die Op-Versionsverwaltung ermöglicht es Entwicklern, neue Funktionen und Parameter in bestehende Vorgänge aufzunehmen. Außerdem wird Folgendes garantiert:
- Abwärtskompatibilität: Die neue TensorFlow Lite-Implementierung sollte eine alte Modelldatei verarbeiten.
- Aufwärtskompatibilität: Die alte TensorFlow Lite-Implementierung sollte eine neue Modelldatei verarbeiten, die von der neuen Version des Converters erzeugt wird, solange keine neuen Features verwendet werden.
- Erkennung von Vorwärtsinkompatibilität: Wenn eine alte TensorFlow Lite-Implementierung ein neues Modell liest, das eine neue Version eines nicht unterstützten Vorgangs enthält, sollte sie den Fehler melden.
Beispiel: Dilatation in tiefen Faltung hinzufügen
Im Rest dieses Dokuments wird die Op-Versionierung in TFLite erläutert. Dazu wird gezeigt, wie Sie Dilatationsparameter zum tiefenklären Faltungsvorgang hinzufügen.
Zum Verständnis dieses Dokuments sind keine Kenntnisse der Dilatation erforderlich. Hinweis:
- Es werden zwei neue Ganzzahlparameter hinzugefügt:
dilation_width_factor
unddilation_height_factor
. - Alte tiefen Faltungskerne, die die Dilatation nicht unterstützen, entsprechen dem Einstellen der Dilatationsfaktoren 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 Optionstabelle für die tiefenweise 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 erhält, sollte sie genau wie die alte Implementierung funktionieren.
Nach dem Hinzufügen der neuen Parameter sieht die Tabelle 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 das neue Schema neu generiert werden.
C-Strukturen und Kernel-Implementierung ändern
In TensorFlow Lite ist die Kernelimplementierung von der FlatBuffer-Definition entkoppelt. Die Kernel lesen den Parameter aus den C-Strukturen, die in lite/c/builtin_op_data.h
definiert sind.
Der ursprüngliche tiefenfaltbare Parameter lautet wie folgt:
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 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 Kernelimplementierung, um die neu hinzugefügten Parameter aus den C-Strukturen zu lesen. Die Details werden hier ausgelassen.
Lesecode für FlatBuffer ändern
Die Logik zum Lesen von FlatBuffer und zum Erzeugen der C-Struktur befindet sich in lite/core/api/flatbuffer_conversions.cc
.
Aktualisieren Sie die Datei so, dass sie die neuen Parameter verarbeitet:
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 Vorgangsversion hier zu prüfen. Wenn die neue Implementierung eine alte Modelldatei liest, in der Dilatationsfaktoren fehlen, wird 1 als Standardwert verwendet und der neue Kernel funktioniert konsistent mit dem alten Kernel.
Kernelregistrierung ändern
Der MutableOpResolver (in lite/mutable_op_resolver.h
definiert) bietet einige Funktionen zum Registrieren von Op-Kernels. Die Mindest- und Höchstversion ist 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-Vorgangsversion ändern
Im nächsten Schritt muss TFLite die Mindestversion ausfüllen, die zum Ausführen des Vorgangs erforderlich ist. In diesem Beispiel bedeutet das:
- Geben Sie version=1 ein, wenn die Dilatationsfaktoren alle 1 sind.
- Andernfalls geben Sie version=2 ein.
Ändern Sie die Funktion GetBuiltinOperatorVersion
für den Operator in lite/tools/versioning/op_version.cc
, indem Sie die neue Version im 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;
Operator-Versionszuordnung aktualisieren
Im letzten Schritt fügen Sie der Operator-Versionskarte die neuen Versionsinformationen hinzu. Dieser Schritt ist erforderlich, da wir die erforderliche Mindestlaufzeitversion des Modells anhand 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
einfügen:
{ {BuiltinOperator_DEPTHWISE_CONV_2D, 2}, %CURRENT_RUNTIME_VERSION%}
Dabei entspricht %CURRENT_RUNTIME_VERSION%
der aktuellen Laufzeitversion, die in tensorflow/core/public/version.h definiert ist.
Implementierung der Delegierung
TensorFlow Lite bietet eine Delegations-API, die das Delegieren von Vorgängen an Hardware-Back-Ends ermöglicht. Prüfen Sie in der Funktion Prepare
des Delegaten, ob die Version für jeden Knoten im Delegierungscode 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 erforderlich, wenn die Delegierung nur Vorgänge der Version 1 unterstützt. Die Delegierung kann also Inkompatibilitäten erkennen, wenn sie eine höhere Versionsversion abrufen.