Dodawanie metadanych do modeli TensorFlow Lite

Metadane TensorFlow Lite stanowią standard dla opisów modeli. Metadane są ważnym źródłem wiedzy o działaniu modelu oraz jego danych wejściowych i wyjściowych. Metadane obejmują

Wszystkie modele obrazów opublikowane w TensorFlow Hub zostały wypełnione metadanymi.

Model z formatem metadanych

model_with_metadata
Rysunek 1. Model TFLite z metadanymi i powiązanymi plikami.

Metadane modelu są zdefiniowane w pliku metadata_schema.fbs FlatBuffer. Jak widać na Rysunku 1, jest on przechowywany w polu metadanych schematu modelu TTite pod nazwą "TFLITE_METADATA". Niektóre modele mogą mieć powiązane pliki, takie jak pliki etykiet klasyfikacji. Te pliki są łączone na końcu pierwotnego pliku modelu jako pliki ZIP w trybie „dołącz” (w trybie 'a'). Tłumacz TFLite może korzystać z nowego formatu pliku w taki sam sposób jak wcześniej. Więcej informacji znajdziesz w sekcji Pakowanie powiązanych plików.

Poniżej znajdziesz instrukcje wypełniania, wizualizacji i odczytywania metadanych.

Konfiguracja narzędzi do zarządzania metadanymi

Zanim dodasz metadane do modelu, musisz skonfigurować środowisko programistyczne Pythona na potrzeby uruchamiania TensorFlow. Szczegółowy przewodnik dotyczący konfiguracji znajdziesz tutaj.

Po skonfigurowaniu środowiska programowania Python musisz zainstalować dodatkowe narzędzia:

pip install tflite-support

Narzędzia do obsługi metadanych TensorFlow Lite obsługują Pythona 3.

Dodawanie metadanych za pomocą interfejsu Flatbuffers Pythons

Metadane modelu w schemacie składają się z 3 części:

  1. Informacje o modelu – ogólny opis modelu wraz z takimi elementami jak warunki licencji. Zobacz ModelMetadata.
  2. Informacje wejściowe – opis wymaganych danych wejściowych i przetwarzania wstępnego, np. normalizacji. Patrz SubGraphMetadata.input_tensor_metadata.
  3. Informacje wyjściowe – opis wymaganych danych wyjściowych i przetwarzania, np. mapowania na etykiety. Patrz SubGraphMetadata.output_tensor_metadata.

Ponieważ TensorFlow Lite obsługuje na razie tylko jeden podpunkt, generator kodu TensorFlow Lite i funkcja Android Studio ML Binding używają ModelMetadata.name i ModelMetadata.description zamiast SubGraphMetadata.name i SubGraphMetadata.description do wyświetlania metadanych i generowania kodu.

Obsługiwane typy danych wejściowych / wyjściowych

Metadane TensorFlow Lite dotyczące danych wejściowych i wyjściowych nie są projektowane z myślą o konkretnych typach modeli, ale raczej pod kątem typów danych wejściowych i wyjściowych. Nie ma znaczenia, jak działa model – jeśli tylko typy danych wejściowych i wyjściowych składają się z tych elementów lub ich kombinacji, są obsługiwane przez metadane TensorFlow Lite:

  • Cecha – liczby w formie nieoznaczonych liczb całkowitych lub liczby zmiennoprzecinkowej32.
  • Obraz – metadane obecnie obsługują obrazy RGB i w skali szarości.
  • Ramka ograniczająca – prostokątne ramki ograniczające kształty. Schemat obsługuje różne schematy numeracji.

Zapakuj powiązane pliki

Modele TensorFlow Lite mogą mieć różne powiązane pliki. Na przykład modele języka naturalnego zawierają zwykle pliki słów, które mapują fragmenty słów na identyfikatory słów. Modele klasyfikacji mogą mieć pliki etykiet wskazujące kategorie obiektów. Model bez powiązanych plików (jeśli są dostępne) nie będzie działał poprawnie.

Powiązane pliki można teraz połączyć z modelem za pomocą biblioteki metadanych w Pythonie. Nowy model TensorFlow Lite staje się plikiem ZIP zawierającym zarówno model, jak i powiązane z nim pliki. Można go rozpakować za pomocą popularnych narzędzi ZIP. Ten nowy format modelu nadal używa tego samego rozszerzenia pliku: .tflite. Jest zgodny z dotychczasową platformą i tłumaczeniem TFLite. Więcej informacji znajdziesz w sekcji Pakowanie metadanych i powiązanych plików do modelu.

Powiązane informacje o pliku można rejestrować w metadanych. W zależności od typu pliku i miejsca, do którego został dołączony plik (np. ModelMetadata, SubGraphMetadata i TensorMetadata) generator kodów na Androida TensorFlow Lite może automatycznie stosować do obiektu odpowiednie przetwarzanie przed i po. Więcej informacji znajdziesz w sekcji o <korzystaniu z Codegen> w przypadku każdego typu pliku powiązanego z kodem w schemacie.

Parametry normalizacji i kwantyzacji

Normalizacja jest powszechną techniką wstępnego przetwarzania danych w systemach uczących się. Celem normalizacji jest zmiana wartości na wspólną skalę bez zniekształcania różnic w zakresach wartości.

Kwantyfikacja modelu to technika, która umożliwia z mniejszą precyzją przedstawiania wag, a opcjonalnie także aktywacji na potrzeby pamięci masowej i obliczenia.

Normalizacja i kwantyfikacja to 2 niezależne etapy przetwarzania wstępnego i ponownego. Oto szczegóły:

Normalizacja Kwantyzacja

Przykład wartości parametrów obrazu wejściowego w MobileNet dla modeli zmiennoprzecinkowych i kwotowych.
Model zmiennoprzecinkowy:
– średnia: 127,5
– std: 127,5
Model kwantystyczny:
– średnia: 127,5
– std: 127,5
Model zmiennoprzecinkowy:
– zeroPoint: 0
– skala: 1,0
Model ilorazowy:
– zeroPoint: 128,0
– skala:0.0078125f




Kiedy wywoływać?


Dane wejściowe: jeśli dane wejściowe są znormalizowane podczas trenowania, dane wejściowe wnioskowania muszą zostać odpowiednio znormalizowane.
Dane wyjściowe: dane wyjściowe nie będą ogólnie znormalizowane.
Modele zmiennoprzecinkowe nie wymagają kwantyzacji.
Model kwantyzowany może nie wymagać kwantyzacji w procesie przed przetwarzaniem i po nim. Jest uzależniona od typu danych tensorów wejściowych i wyjściowych.
– tensory zmiennoprzecinkowe: nie jest potrzebna kwantyfikacja w przetwarzaniu przed i po. Operacje kwanty i operacje zależne są zapisywane na wykresie modelu.
– tensory int8/uint8: wymagają kwantyzacji w procesie przetwarzania przed i po.


Wzór


normalized_input = (input – średnia) / std
Kwantyzuj dla danych wejściowych:
q = f / skala + zeroPoint
Dekwantyfikuj dla danych wyjściowych:
f = (q - zeroPoint) * skala

Gdzie są parametry
Wypełnione przez twórcę modelu i zapisane w metadanych modelu jako NormalizationOptions Wypełniane automatycznie przez konwerter TFLite i zapisane w pliku modelu tflite.
Jak pobrać parametry? Za pomocą interfejsu MetadataExtractor API [2] Przez interfejs TFLite Tensor API [1] lub MetadataExtractor API[2]
Czy modele zmiennoprzecinkowe i kwantowe mają tę samą wartość? Tak. Modele zmiennoprzecinkowe i kwantowe mają te same parametry normalizacji, Nie. Model zmiennoprzecinkowy nie wymaga kwantyzacji.
Czy generator kodu TFLite lub powiązanie ML Studio na Androidzie generuje go automatycznie podczas przetwarzania danych?
Tak

Tak

[1] TensorFlow Lite Java API i TensorFlow Lite C++ API.
[2] Biblioteka modułu wyodrębniania metadanych

Podczas przetwarzania danych obrazu na potrzeby modeli uint8 normalizacja i kwantyfikacja są czasem pomijane. Jest to dozwolone, gdy wartości w pikselach mieszczą się w zakresie [0, 255]. Ogólnie jednak należy zawsze przetwarzać dane zgodnie z parametrami normalizacji i kwantyzacji (w stosownych przypadkach).

Przykłady

Przykłady wypełniania metadanych w przypadku różnych typów modeli znajdziesz tutaj:

Klasyfikacja obrazów

Pobierz skrypt tutaj, który uzupełnia metadane w formacie mobilenet_v1_0.75_160_quantized.tflite. Uruchom skrypt w ten sposób:

python ./metadata_writer_for_image_classifier.py \
    --model_file=./model_without_metadata/mobilenet_v1_0.75_160_quantized.tflite \
    --label_file=./model_without_metadata/labels.txt \
    --export_directory=model_with_metadata

Aby wypełnić metadane na potrzeby innych modeli klasyfikacji obrazów, dodaj do skryptu odpowiednie specyfikacje modelu, np. this. W pozostałej części tego przewodnika omówimy najważniejsze sekcje w przykładzie klasyfikacji obrazów, aby zilustrować kluczowe elementy.

Szczegółowe omówienie przykładu klasyfikacji obrazów

Informacje o modelu

Metadane rozpoczynają się od utworzenia nowych informacji o modelu:

from tflite_support import flatbuffers
from tflite_support import metadata as _metadata
from tflite_support import metadata_schema_py_generated as _metadata_fb

""" ... """
"""Creates the metadata for an image classifier."""

# Creates model info.
model_meta = _metadata_fb.ModelMetadataT()
model_meta.name = "MobileNetV1 image classifier"
model_meta.description = ("Identify the most prominent object in the "
                          "image from a set of 1,001 categories such as "
                          "trees, animals, food, vehicles, person etc.")
model_meta.version = "v1"
model_meta.author = "TensorFlow"
model_meta.license = ("Apache License. Version 2.0 "
                      "http://www.apache.org/licenses/LICENSE-2.0.")

Informacje wejściowe i wyjściowe

W tej sekcji możesz opisać podpis wejściowy i wyjściowy modelu. Te metadane mogą być używane przez automatyczne generatory kodu do tworzenia kodu przed przetwarzaniem i po nim. Aby utworzyć dane wejściowe lub wyjściowe dotyczące tensora:

# Creates input info.
input_meta = _metadata_fb.TensorMetadataT()

# Creates output info.
output_meta = _metadata_fb.TensorMetadataT()

Dane wejściowe obrazu

Obraz to typowy typ danych wejściowych systemów uczących się. Metadane TensorFlow Lite obsługują takie informacje jak przestrzeń kolorów oraz informacje o wstępnym przetwarzaniu, np. normalizacji. Wymiary obrazu nie wymagają ręcznego określania wymiarów, ponieważ jest on już określony na podstawie kształtu tensora wejściowego i można go wywnioskować automatycznie.

input_meta.name = "image"
input_meta.description = (
    "Input image to be classified. The expected image is {0} x {1}, with "
    "three channels (red, blue, and green) per pixel. Each value in the "
    "tensor is a single byte between 0 and 255.".format(160, 160))
input_meta.content = _metadata_fb.ContentT()
input_meta.content.contentProperties = _metadata_fb.ImagePropertiesT()
input_meta.content.contentProperties.colorSpace = (
    _metadata_fb.ColorSpaceType.RGB)
input_meta.content.contentPropertiesType = (
    _metadata_fb.ContentProperties.ImageProperties)
input_normalization = _metadata_fb.ProcessUnitT()
input_normalization.optionsType = (
    _metadata_fb.ProcessUnitOptions.NormalizationOptions)
input_normalization.options = _metadata_fb.NormalizationOptionsT()
input_normalization.options.mean = [127.5]
input_normalization.options.std = [127.5]
input_meta.processUnits = [input_normalization]
input_stats = _metadata_fb.StatsT()
input_stats.max = [255]
input_stats.min = [0]
input_meta.stats = input_stats

Dane wyjściowe etykiet

Etykieta można zmapować na tensor wyjściowy za pomocą powiązanego pliku za pomocą funkcji TENSOR_AXIS_LABELS.

# Creates output info.
output_meta = _metadata_fb.TensorMetadataT()
output_meta.name = "probability"
output_meta.description = "Probabilities of the 1001 labels respectively."
output_meta.content = _metadata_fb.ContentT()
output_meta.content.content_properties = _metadata_fb.FeaturePropertiesT()
output_meta.content.contentPropertiesType = (
    _metadata_fb.ContentProperties.FeatureProperties)
output_stats = _metadata_fb.StatsT()
output_stats.max = [1.0]
output_stats.min = [0.0]
output_meta.stats = output_stats
label_file = _metadata_fb.AssociatedFileT()
label_file.name = os.path.basename("your_path_to_label_file")
label_file.description = "Labels for objects that the model can recognize."
label_file.type = _metadata_fb.AssociatedFileType.TENSOR_AXIS_LABELS
output_meta.associatedFiles = [label_file]

Tworzenie płaskich buforów metadanych

Ten kod łączy informacje o modelu z informacjami wejściowymi i wyjściowymi:

# Creates subgraph info.
subgraph = _metadata_fb.SubGraphMetadataT()
subgraph.inputTensorMetadata = [input_meta]
subgraph.outputTensorMetadata = [output_meta]
model_meta.subgraphMetadata = [subgraph]

b = flatbuffers.Builder(0)
b.Finish(
    model_meta.Pack(b),
    _metadata.MetadataPopulator.METADATA_FILE_IDENTIFIER)
metadata_buf = b.Output()

Zapakuj metadane i powiązane pliki do modelu

Po utworzeniu płaskich buforów metadanych metadane i plik etykiet są zapisywane w pliku TFLite za pomocą metody populate:

populator = _metadata.MetadataPopulator.with_model_file(model_file)
populator.load_metadata_buffer(metadata_buf)
populator.load_associated_files(["your_path_to_label_file"])
populator.populate()

Za pomocą funkcji load_associated_files możesz spakować do modelu dowolną liczbę powiązanych plików. Konieczne jest jednak spakowanie przynajmniej tych plików, które są wymienione w metadanych. W tym przykładzie spakowanie pliku etykiet jest obowiązkowe.

Wizualizacja metadanych

Możesz wizualizować metadane za pomocą Netron lub odczytać metadane z modelu TensorFlow Lite do formatu JSON za pomocą MetadataDisplayer:

displayer = _metadata.MetadataDisplayer.with_model_file(export_model_path)
export_json_file = os.path.join(FLAGS.export_directory,
                                os.path.splitext(model_basename)[0] + ".json")
json_file = displayer.get_metadata_json()
# Optional: write out the metadata as a json file
with open(export_json_file, "w") as f:
  f.write(json_file)

Android Studio obsługuje również wyświetlanie metadanych za pomocą funkcji powiązania ML w Android Studio.

Obsługa wersji metadanych

Wersja schematu metadanych jest przechowywana na podstawie numeru obsługi wersji semantycznej, który śledzi zmiany pliku schematu, oraz identyfikatora pliku Flatbuffers, który wskazuje prawdziwą zgodność wersji.

Numer semantycznej obsługi wersji

Schemat metadanych jest obsługiwany przez numer obsługi wersji semantycznych, np. GŁÓWNA.PODRZĘDNA.POPRAWKA. Śledzi zmiany schematu zgodnie z regułami, które obowiązują tutaj. Zobacz historię pól dodanych po wersji 1.0.0.

Identyfikacja pliku Flatbuffers

Semantyczna obsługa wersji gwarantuje zgodność w przypadku zgodności z regułami, ale nie sugeruje prawdziwej niezgodności. Zwiększenie wartości dla wartości MAJOR nie musi oznaczać, że zgodność wsteczna jest zerwana. Dlatego w celu określenia rzeczywistej zgodności schematu metadanych używamy identyfikatora pliku płaskiego bufora (file_identifier). Identyfikator pliku ma dokładnie 4 znaki. Jest ono stałe na podstawie określonego schematu metadanych i użytkownicy nie mogą go zmienić. Jeśli z jakiegoś powodu zgodność wsteczna schematu metadanych zostanie uszkodzona, parametr file_identifier wzrosnie, np. z „M001” na „M002”. Wartość atrybutu „file_identifier” będzie się prawdopodobnie zmieniać znacznie rzadziej niż w przypadku parametrumetadata_version.

Minimalna wymagana wersja parsera metadanych

Minimalna niezbędna wersja parsera metadanych to minimalna wersja parsera metadanych (generowany kod Flatbuffers), który może w pełni odczytywać płaskie bufory metadanych. Wersja jest w rzeczywistości najwyższym numerem wersji spośród wszystkich wypełnionych pól i najmniejsza zgodna wersja wskazana przez identyfikator pliku. Minimalna wymagana wersja parsera metadanych jest automatycznie wypełniana przez MetadataPopulator podczas wypełniania modelu TFLite. Więcej informacji o stosowaniu minimalnej wymaganej wersji parsera metadanych znajdziesz w artykule o wyodrębnianiu metadanych.

Odczytywanie metadanych z modeli

Biblioteka wyodrębniania metadanych to wygodne narzędzie do odczytywania metadanych i powiązanych plików z modeli na różnych platformach (zobacz wersję Java i wersję C++). Możesz utworzyć własne narzędzie do wyodrębniania metadanych w innych językach, korzystając z biblioteki płaskich buforów.

Odczytywanie metadanych w Javie

Jeśli chcesz użyć biblioteki ekstraktora metadanych w aplikacji na Androida, zalecamy użycie AAR metadanych TensorFlow Lite hostowanego w MavenCentral. Zawiera klasę MetadataExtractor, a także powiązania Java FlatBuffers dla schematu metadanych i schematu modelu.

Możesz to określić w zależnościach build.gradle w ten sposób:

dependencies {
    implementation 'org.tensorflow:tensorflow-lite-metadata:0.1.0'
}

Aby używać nocnych zrzutów, musisz dodać repozytorium zrzutów Sonatype.

Obiekt MetadataExtractor możesz zainicjować za pomocą obiektu ByteBuffer, który wskazuje model:

public MetadataExtractor(ByteBuffer buffer);

Wartość ByteBuffer musi pozostać niezmieniona przez cały czas istnienia obiektu MetadataExtractor. Inicjowanie może się nie udać, jeśli identyfikator pliku Flatbuffers metadanych modelu nie pasuje do identyfikatora parsera metadanych. Więcej informacji znajdziesz w artykule na temat obsługi wersji metadanych.

Dzięki dopasowaniu identyfikatorów plików moduł wyodrębniania metadanych z powodzeniem odczytuje metadane wygenerowane na podstawie wszystkich przeszłych i przyszłych schematów dzięki mechanizmowi zgodności wstecznej i przekierowania obiektu Flatbuffers. Starsze moduły wyodrębniania metadanych nie będą jednak mogły wyodrębnić pól z przyszłych schematów. Minimalna niezbędna wersja parsera metadanych wskazuje minimalną wersję parsera metadanych, który może w pełni odczytywać płaskie bufory metadanych. Aby sprawdzić, czy spełniony jest minimalny wymagany warunek wersji parsera, możesz użyć tej metody:

public final boolean isMinimumParserVersionSatisfied();

Przekazywanie modelu bez metadanych jest dozwolone. Wywoływanie metod odczytujących metadane z metadanych powoduje jednak błędy w czasie działania. Możesz sprawdzić, czy model zawiera metadane, wywołując metodę hasMetadata:

public boolean hasMetadata();

MetadataExtractor udostępnia wygodne funkcje do pobierania metadanych tensorów wejściowych i wyjściowych. Przykład:

public int getInputTensorCount();
public TensorMetadata getInputTensorMetadata(int inputIndex);
public QuantizationParams getInputTensorQuantizationParams(int inputIndex);
public int[] getInputTensorShape(int inputIndex);
public int getoutputTensorCount();
public TensorMetadata getoutputTensorMetadata(int inputIndex);
public QuantizationParams getoutputTensorQuantizationParams(int inputIndex);
public int[] getoutputTensorShape(int inputIndex);

Choć schemat modelu TensorFlow Lite obsługuje wiele podtytułów, tłumacz TFLite obecnie obsługuje tylko 1 podgraf. Dlatego w metodach MetadataExtractor pomijany indeks podpunktu jako argumentu wejściowego.

Odczytywanie powiązanych plików z modeli

Model TensorFlow Lite z metadanymi i powiązanymi plikami to w zasadzie plik ZIP, który można rozpakować za pomocą popularnych narzędzi do kompresji danych, aby pobrać powiązane pliki. Możesz na przykład rozpakować plik mobilenet_v1_0.75_160_quantized i wyodrębnić plik etykiety w modelu w ten sposób:

$ unzip mobilenet_v1_0.75_160_quantized_1_metadata_1.tflite
Archive:  mobilenet_v1_0.75_160_quantized_1_metadata_1.tflite
 extracting: labels.txt

Powiązane pliki możesz też odczytywać za pomocą biblioteki wyodrębniania metadanych.

W Javie przekaż nazwę pliku do metody MetadataExtractor.getAssociatedFile:

public InputStream getAssociatedFile(String fileName);

Podobnie w C++ można to zrobić za pomocą metody ModelMetadataExtractor::GetAssociatedFile:

tflite::support::StatusOr<absl::string_view> GetAssociatedFile(
      const std::string& filename) const;