Odpowiedz już dziś na lokalne wydarzenie TensorFlow Everywhere!
Ta strona została przetłumaczona przez Cloud Translation API.
Switch to English

Wersje operatora TensorFlow Lite

W tym dokumencie opisano schemat obsługi wersji TensorFlow Lite. Wersjonowanie operacji umożliwia programistom dodawanie nowych funkcji i parametrów do istniejących operacji. Ponadto gwarantuje:

  • Zgodność wsteczna: Nowa implementacja TensorFlow Lite powinna obsługiwać stary plik modelu.
  • Kompatybilność w przód: Stara implementacja TensorFlow Lite powinna obsługiwać nowy plik modelu utworzony przez nową wersję konwertera, o ile nie są używane żadne nowe funkcje.
  • Wykrywanie zgodności do przodu: jeśli stara implementacja TensorFlow Lite odczytuje nowy model, który zawiera nową wersję operacji, która nie jest obsługiwana, powinna zgłosić błąd.

Przykład: dodanie dylatacji do splotu wgłębnego

Pozostała część tego dokumentu wyjaśnia wersjonowanie operacji w TFLite, pokazując, jak dodać parametry dylatacji do operacji splotu wgłębnego.

Znajomość dylatacji nie jest wymagana do zrozumienia tego dokumentu. Zauważ, że:

  • Zostaną dodane 2 nowe parametry całkowite: dilation_width_factor i dilation_height_factor .
  • Stare jądra z głębokim splotem, które nie obsługują dylatacji, są równoważne ustawieniu współczynników dylatacji na 1.

Zmień schemat FlatBuffer

Aby dodać nowe parametry do operacji, zmień tabelę opcji w lite/schema/schema.fbs .

Na przykład tabela opcji splotu wgłębnego wygląda następująco:

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 daną wersję.
  • Gdy nowa implementacja otrzyma domyślne wartości dla nowo dodanych parametrów, powinna działać dokładnie tak samo, jak stara implementacja.

Tabela będzie wyglądać następująco po dodaniu nowych parametrów:

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 powinien zostać ponownie wygenerowany dla nowego schematu.

Zmień struktury C i implementację jądra

W TensorFlow Lite implementacja jądra jest oddzielona od definicji FlatBuffer. Jądra odczytują parametr ze struktur C zdefiniowanych w lite/c/builtin_op_data.h .

Oryginalny parametr splotu wgłębnego jest następujący:

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, począwszy od której wersji. Wynik widać poniżej:

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;

Proszę również zmienić implementację jądra, aby odczytać nowo dodane parametry ze struktur C. Szczegóły są tutaj pominięte.

Zmień kod odczytu FlatBuffer

Logika odczytywania FlatBuffer i tworzenia struktury C znajduje się w 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 jest wymagane sprawdzanie wersji op tutaj. Kiedy nowa implementacja czyta stary plik modelu, w którym brakuje współczynników dylatacji, użyje 1 jako wartości domyślnej, a nowe jądro będzie działało spójnie ze starym jądrem.

Zmień rejestrację jądra

MutableOpResolver (zdefiniowany w lite/mutable_op_resolver.h ) udostępnia kilka funkcji do rejestrowania jąder lite/mutable_op_resolver.h . Minimalna i maksymalna wersja to domyślnie 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 zaimplementowaliśmy nowe jądro op, które obsługuje DepthwiseConv2D wersji 1 i 2, więc musimy zmienić tę linię:

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ę op. TFLite

Następnym krokiem jest ustawienie w TFLite minimalnej wersji wymaganej do wykonania operacji. W tym przykładzie oznacza to:

  • Wypełnij wersję = 1, gdy wszystkie współczynniki dylatacji wynoszą 1.
  • W przeciwnym razie zapełnij wersję = 2.

Aby to zrobić, musisz najpierw dodać odpowiednie parametry do depthwise_conv_2d wewnątrz struktury OpSignature :

struct {
      int32_t dilation_w_factor;
      int32_t dilation_h_factor;
    } depthwise_conv_2d;

Następnie GetOpSignature te nowe parametry w funkcji GetOpSignature w lite/tools/versioning/op_version.cc :

case BuiltinOperator_DEPTHWISE_CONV_2D: {
      auto conv_option = op->builtin_options_as_DepthwiseConv2DOptions();
      if (conv_option) {
        op_sig.options.depthwise_conv_2d.dilation_w_factor =
            conv_option->dilation_w_factor();
        op_sig.options.depthwise_conv_2d.dilation_h_factor =
            conv_option->dilation_h_factor();
      }
    } break;

Pamiętaj, że jeśli dodajesz obsługę nowych typów, powyższe kroki nie są potrzebne. Typy danych wejściowych i wyjściowych są definiowane i wypełniane dla wszystkich OpSignature w OpSignature .

Na koniec zmodyfikuj funkcję GetBuiltinOperatorVersion dla operatora w lite/tools/versioning/op_version.cc , dodając nową wersję do przypadku DepthwiseConv2D :

case BuiltinOperator_DEPTHWISE_CONV_2D:
  if (op_sig.options.depthwise_conv_2d.dilation_w_factor != 1 ||
      op_sig.options.depthwise_conv_2d.dilation_h_factor != 1) {
    return 2;
  }
  return 1;

Zaktualizuj mapę 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 wykonawczego modelu na podstawie tej mapy wersji.

Aby to zrobić, musisz dodać nowy wpis mapy w lite/tools/versioning/runtime_version.cc .

W tym przykładzie musisz dodać następujący wpis do op_version_map :

{ {BuiltinOperator_DEPTHWISE_CONV_2D, 2}, kPendingReleaseOpVersion}

( kPendingReleaseOpVersion zostanie zastąpione odpowiednią wersją w następnej stabilnej wersji).

Realizacja delegacji

TensorFlow Lite udostępnia interfejs API delegowania, który umożliwia delegowanie operacji do zaplecza sprzętowego. W funkcji Prepare delegata sprawdź, czy wersja jest obsługiwana dla każdego węzła w kodzie delegowania.

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

Jest to wymagane, nawet jeśli delegowanie obsługuje tylko operacje w wersji 1, więc delegacja może wykryć niezgodność podczas pobierania operacji wyższej wersji.