Ten dokument opisuje schemat wersji opcji LiteRT. Wersje operacji umożliwiają deweloperom dodawanie nowych funkcji i parametrów do istniejących operacji. Dodatkowo gwarantuje:
- Zgodność wsteczna: nowa implementacja LiteRT powinna obsługiwać stary plik modelu.
- Zgodność wsteczna: stara implementacja LiteRT powinna obsługiwać nowy plik modelu wygenerowany przez nową wersję konwertera, o ile nie są używane żadne nowe funkcje.
- Wykrywanie niezgodności w przyszłości: jeśli stara implementacja LiteRT odczyta nowy model, który zawiera nową wersję op, która nie jest obsługiwana, powinna zgłosić błąd.
Przykład: dodawanie rozszerzenia do konwolucji głębokształtowej
W pozostałej części tego dokumentu wyjaśniamy wersjonowanie operacji w TFLite, pokazując, jak dodać parametry rozszerzania do operacji konwulsji głębokształtnej.
Aby zrozumieć ten dokument, nie musisz znać pojęcia „rozszerzenia”. Uwaga:
- Dodamy 2 nowe parametry liczbowe:
dilation_width_factor
idilation_height_factor
. - Stare jądra skonwulowanych operacji konwolucji, które nie obsługują rozszerzania, są równoważne ustawieniu współczynników rozszerzania na 1.
Zmiana schematu FlatBuffer
Aby dodać nowe parametry do op, zmień tabelę opcji w pliku lite/schema/schema.fbs
.
Na przykład tabela opcji konwekcji głębokształtującej wygląda tak:
table DepthwiseConv2DOptions {
padding:Padding;
stride_w:int;
stride_h:int;
depth_multiplier:int;
fused_activation_function:ActivationFunctionType;
}
Podczas dodawania nowych parametrów:
- Dodaj komentarze wskazujące, które parametry są obsługiwane przez którą wersję.
- Gdy nowa implementacja otrzyma domyślne wartości nowo dodanych parametrów, powinna działać dokładnie tak samo jak stara implementacja.
Po dodaniu nowych parametrów tabela będzie wyglądać tak:
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;
}
Plik lite/schema/schema_generated.h
należy wygenerować ponownie dla nowego schematu.
Zmiana struktur C i wdrażania jądra
W LiteRT implementacja jądra jest odłączona od definicji FlatBuffer. Kernely odczytują parametr ze struktur C zdefiniowanych w pliku lite/c/builtin_op_data.h
.
Pierwotny parametr konwekcji w głębi ma postać:
typedef struct {
TfLitePadding padding;
int stride_width;
int stride_height;
int depth_multiplier;
TfLiteFusedActivation activation;
} TfLiteDepthwiseConvParams;
Podobnie jak w przypadku schematu FlatBuffer, dodaj komentarze wskazujące, które parametry są obsługiwane od której wersji. Wynik:
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;
Zmień też implementację jądra, aby odczytywać nowo dodane parametry ze struktur C. Szczegóły zostały pominięte.
Zmiana kodu odczytu FlatBuffer
Logika odczytu FlatBuffer i tworzenia struktury C znajduje się w pliku lite/core/api/flatbuffer_conversions.cc
.
Zaktualizuj plik, aby obsługiwał nowe parametry, jak pokazano poniżej:
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;
}
Nie musisz sprawdzać wersji op. Gdy nowa implementacja odczyta stary plik modelu, w którym brakuje czynników rozszerzenia, użyje wartości 1 jako wartości domyślnej, a nowe jądro będzie działać zgodnie ze starym jądrem.
Zmiana rejestracji jądra
MutableOpResolver (zdefiniowany w lite/mutable_op_resolver.h
) udostępnia kilka funkcji do rejestrowania jąder operacji. Domyślnie minimalna i maksymalna wersja to 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);
Wbudowane operacje są zarejestrowane w lite/kernels/register.cc
. W tym przykładzie wdrożyliśmy nowe jądro op, które obsługuje DepthwiseConv2D
wersję 1 i 2, więc musimy zmienić ten wiersz:
AddBuiltin(BuiltinOperator_DEPTHWISE_CONV_2D, Register_DEPTHWISE_CONV_2D());
do:
AddBuiltin(BuiltinOperator_DEPTHWISE_CONV_2D, Register_DEPTHWISE_CONV_2D(),
/* min_version = */ 1,
/* max_version = */ 2);
Zmień wersję operacji TFLite
Kolejnym krokiem jest wypełnienie przez TFLite minimalnej wersji wymaganej do wykonania operacji. W tym przykładzie oznacza to:
- Wypełnij wersję=1, gdy wszystkie współczynniki rozszerzania mają wartość 1.
- W przeciwnym razie wypełnij wersję=2.
Zmodyfikuj funkcję GetBuiltinOperatorVersion
dla operatora w funkcji lite/tools/versioning/op_version.cc
, dodając nową wersję do przypadku 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;
Aktualizowanie mapy wersji operatora
Ostatnim krokiem jest dodanie informacji o nowej wersji do mapy wersji operatora. Ten krok jest wymagany, ponieważ musimy wygenerować minimalną wymaganą wersję środowiska uruchomieniowego na podstawie tej mapy wersji.
Aby to zrobić, musisz dodać nowy wpis mapy w sekcji lite/tools/versioning/runtime_version.cc
.
W tym przykładzie musisz dodać do elementu op_version_map
ten wpis:
{ {BuiltinOperator_DEPTHWISE_CONV_2D, 2}, %CURRENT_RUNTIME_VERSION%}
gdzie %CURRENT_RUNTIME_VERSION%
odpowiada bieżącej wersji środowiska uruchomieniowego zdefiniowanej w pliku release_version.h.
Implementacja delegowania
LiteRT udostępnia interfejs API do delegowania, który umożliwia delegowanie operacji do backendów sprzętowych. W funkcji Prepare
podrzędnego sprawdź, czy wersja jest obsługiwana dla każdego węzła w kodzie delegacji.
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.
}
Jest to wymagane, nawet jeśli delegacja obsługuje tylko operacje wersji 1, aby delegacja mogła wykryć niezgodność podczas uzyskiwania operacji w wersji 2.