Dodawanie metadanych do modeli TensorFlow Lite

Metadane TensorFlow Lite zapewniają standard opisów modeli. Metadane są ważnym źródłem wiedzy o działaniu modelu oraz informacji wejściowych/wyjściowych. Metadane składają się z obu

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

Model z formatem metadanych

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

Metadane modelu są zdefiniowane w metadata_schema.fbs , pliku FlatBuffer . Jak pokazano na rysunku 1, jest on przechowywany w polu metadanych schematu modelu TFLite pod nazwą "TFLITE_METADATA" . Do niektórych modeli mogą być dołączone powiązane pliki, takie jak pliki etykiet klasyfikacyjnych . Pliki te są łączone na końcu oryginalnego pliku modelu w postaci pliku ZIP przy użyciu trybu „dołączania” pliku ZipFile (tryb 'a' ). Interpreter TFLite może korzystać z nowego formatu pliku w taki sam sposób jak poprzednio. Aby uzyskać więcej informacji, zobacz Spakowanie skojarzonych plików .

Zapoznaj się z poniższą instrukcją dotyczącą wypełniania, wizualizowania i odczytywania metadanych.

Skonfiguruj narzędzia metadanych

Przed dodaniem metadanych do modelu będziesz potrzebować konfiguracji środowiska programistycznego Python, aby uruchomić TensorFlow. Szczegółowy przewodnik dotyczący tego, jak to skonfigurować, znajduje się tutaj .

Po skonfigurowaniu środowiska programistycznego Python będziesz musiał zainstalować dodatkowe narzędzia:

pip install tflite-support

Narzędzia metadanych TensorFlow Lite obsługują język Python 3.

Dodawanie metadanych przy użyciu interfejsu API Flatbuffers Python

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

  1. Informacje o modelu — ogólny opis modelu oraz elementy, takie jak warunki licencji. Zobacz ModelMetadata .
  2. Informacje wejściowe — opis danych wejściowych i wymaganego przetwarzania wstępnego, takiego jak normalizacja. Zobacz SubGraphMetadata.input_tensor_metadata .
  3. Informacje wyjściowe — opis wyników i wymaganego przetwarzania końcowego, takiego jak mapowanie na etykiety. Zobacz SubGraphMetadata.output_tensor_metadata .

Ponieważ TensorFlow Lite obsługuje w tym momencie tylko pojedynczy podgraf, generator kodu TensorFlow Lite i funkcja Android Studio ML Binding będą używać ModelMetadata.name i ModelMetadata.description zamiast SubGraphMetadata.name i SubGraphMetadata.description podczas wyświetlania metadanych i generowania kodu.

Obsługiwane typy wejść/wyjść

Metadane TensorFlow Lite dla danych wejściowych i wyjściowych nie są projektowane z myślą o konkretnych typach modeli, ale raczej o typach danych wejściowych i wyjściowych. Nie ma znaczenia, co model funkcjonalnie robi, o ile typy danych wejściowych i wyjściowych składają się z następujących elementów lub ich kombinacji, jest on obsługiwany przez metadane TensorFlow Lite:

  • Cecha - Liczby będące liczbami całkowitymi bez znaku lub zmiennoprzecinkowe32.
  • Obraz — metadane obsługują obecnie obrazy RGB i w skali szarości.
  • Obwiednia — prostokątne obwiednie. Schemat obsługuje różne schematy numeracji .

Spakuj powiązane pliki

Modele TensorFlow Lite mogą zawierać różne powiązane pliki. Na przykład modele języka naturalnego zwykle zawierają pliki słowników, które odwzorowują fragmenty słów na identyfikatory słów; modele klasyfikacji mogą zawierać pliki etykiet wskazujące kategorie obiektów. Bez powiązanych plików (jeśli istnieją) model nie będzie działał dobrze.

Powiązane pliki można teraz dołączyć do modelu za pośrednictwem biblioteki metadanych języka Python. 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 kompatybilny z istniejącym frameworkiem i interpreterem TFLite. Aby uzyskać więcej informacji, zobacz Pakowanie metadanych i powiązanych plików do modelu .

Powiązane informacje o pliku można zapisać w metadanych. W zależności od typu pliku i miejsca, do którego plik jest dołączony (tj. ModelMetadata , SubGraphMetadata i TensorMetadata ), generator kodu TensorFlow Lite dla systemu Android może automatycznie zastosować odpowiednie przetwarzanie wstępne/końcowe dla obiektu. Aby uzyskać więcej informacji, zobacz sekcję <Użycie Codegen> każdego powiązanego typu pliku w schemacie.

Parametry normalizacyjne i kwantyzacyjne

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

Kwantyzacja modelu to technika, która pozwala na zmniejszoną precyzję reprezentacji wag i opcjonalnie aktywację zarówno do przechowywania, jak i obliczeń.

Jeśli chodzi o przetwarzanie wstępne i przetwarzanie końcowe, normalizacja i kwantyzacja to dwa niezależne etapy. Oto szczegóły.

Normalizacja Kwantyzacja

Przykład wartości parametrów obrazu wejściowego w MobileNet odpowiednio dla modelu zmiennoprzecinkowego i kwantowego.
Model pływający :
- średnia: 127,5
- std: 127,5
Model ilościowy :
- średnia: 127,5
- std: 127,5
Model pływający :
- Punkt zerowy: 0
- skala: 1,0
Model ilościowy :
- Punkt zerowy: 128,0
- skala: 0,0078125f




Kiedy wywołać?


Dane wejściowe : Jeśli dane wejściowe są znormalizowane podczas uczenia, dane wejściowe wnioskowania muszą zostać odpowiednio znormalizowane.
Wyjścia : dane wyjściowe nie będą ogólnie normalizowane.
Modele pływakowe nie wymagają kwantyzacji.
Model skwantowany może, ale nie musi, wymagać kwantyzacji w przetwarzaniu wstępnym/końcowym. Zależy to od typu danych tensorów wejścia/wyjścia.
- tensory float: nie jest wymagana kwantyzacja w przetwarzaniu wstępnym/końcowym. Quant op i dequant op są wstawiane do wykresu modelu.
- tensory int8/uint8: wymagają kwantyzacji w przetwarzaniu wstępnym/końcowym.


Formuła


normalized_input = (wejście - średnia) / std
Kwantyzacja dla wejść :
q = f / skala + punkt zerowy
Dekwantyzacja dla wyjść :
f = (q - punkt zerowy) * skala

Gdzie są parametry
Wypełniane przez twórcę modelu i przechowywane w metadanych modelu jako NormalizationOptions Wypełniane automatycznie przez konwerter TFLite i zapisywane w pliku modelu tflite.
Jak uzyskać parametry? Poprzez API MetadataExtractor [2] Poprzez API TFLite Tensor [1] lub poprzez API MetadataExtractor [2]
Czy modele zmiennoprzecinkowe i ilościowe mają tę samą wartość? Tak, modele zmiennoprzecinkowe i ilościowe mają te same parametry normalizacji Nie, model pływakowy nie wymaga kwantyzacji.
Czy generator kodu TFLite lub powiązanie Android Studio ML automatycznie generuje je podczas przetwarzania danych?
Tak

Tak

[1] Interfejs API języka Java TensorFlow Lite i interfejs API TensorFlow Lite C++ .
[2] Biblioteka ekstraktora metadanych

Podczas przetwarzania danych obrazu dla modeli uint8 czasami pomija się normalizację i kwantyzację. Można to zrobić, gdy wartości pikseli mieszczą się w zakresie [0, 255]. Ogólnie jednak należy zawsze przetwarzać dane zgodnie z parametrami normalizacji i kwantyzacji, jeśli ma to zastosowanie.

Biblioteka zadań TensorFlow Lite może obsłużyć normalizację, jeśli skonfigurujesz NormalizationOptions w metadanych. Przetwarzanie kwantyzacji i dekwantyzacji jest zawsze enkapsulowane.

Przykłady

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

Klasyfikacja obrazu

Pobierz tutaj skrypt, który zapełni metadane do pliku 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 dla innych modeli klasyfikacji obrazów, dodaj takie specyfikacje modelu do skryptu. W pozostałej części tego przewodnika wyróżnione zostaną niektóre kluczowe sekcje przykładu klasyfikacji obrazów, aby zilustrować kluczowe elementy.

Zagłęb się w przykład klasyfikacji obrazów

Informacje o modelu

Metadane rozpoczynają się od utworzenia informacji o nowym 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/wyjściowe

W tej sekcji pokazano, jak opisać sygnaturę wejściową i wyjściową modelu. Te metadane mogą być wykorzystywane przez automatyczne generatory kodu do tworzenia kodu przed i po przetwarzaniu. Aby utworzyć informację wejściową lub wyjściową dotyczącą tensora:

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

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

Wejście obrazu

Obraz jest typowym typem danych wejściowych w przypadku uczenia maszynowego. Metadane TensorFlow Lite obsługują takie informacje, jak przestrzeń kolorów i informacje dotyczące wstępnego przetwarzania, takie jak normalizacja. Wymiar obrazu nie wymaga ręcznego określenia, ponieważ jest już zapewniony przez kształt 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

Wyjście etykiety

Etykietę można zmapować do tensora wyjściowego poprzez powiązany plik przy użyciu 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]

Utwórz metadane Flatbuffers

Poniższy 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()

Spakuj metadane i powiązane pliki do modelu

Po utworzeniu metadanych Flatbuffers metadane i plik etykiety 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()

Możesz spakować do modelu dowolną liczbę powiązanych plików, korzystając z load_associated_files . Wymagane jest jednak spakowanie przynajmniej tych plików, które są udokumentowane w metadanych. W tym przykładzie spakowanie pliku etykiety jest obowiązkowe.

Wizualizuj metadane

Możesz użyć Netron do wizualizacji metadanych lub możesz 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 także wyświetlanie metadanych za pomocą funkcji Android Studio ML Binding .

Wersjonowanie metadanych

Wersjonowanie schematu metadanych odbywa się zarówno na podstawie numeru wersji Semantic, który śledzi zmiany w pliku schematu, jak i na podstawie identyfikacji pliku Flatbuffers, która wskazuje prawdziwą zgodność wersji.

Numer wersji semantycznej

Wersja schematu metadanych jest oparta na numerze wersji semantycznej , takim jak MAJOR.MINOR.PATCH. Śledzi zmiany schematu zgodnie z zasadami tutaj . Zobacz historię pól dodanych po wersji 1.0.0 .

Identyfikacja pliku Flatbuffers

Wersjonowanie semantyczne gwarantuje zgodność, jeśli przestrzega się zasad, ale nie oznacza prawdziwej niezgodności. Podbicie numeru MAJOR nie musi oznaczać, że kompatybilność wsteczna jest zepsuta. Dlatego używamy identyfikacji pliku Flatbuffers , identyfikator_pliku , aby wskazać prawdziwą zgodność schematu metadanych. Identyfikator pliku ma dokładnie 4 znaki. Jest on przypisany do określonego schematu metadanych i nie podlega zmianom przez użytkowników. Jeśli z jakiegoś powodu wsteczna kompatybilność schematu metadanych musi zostać przerwana, identyfikator pliku zostanie podniesiony, na przykład z „M001” na „M002”. Oczekuje się, że identyfikator pliku będzie zmieniany znacznie rzadziej niż wersja metadata_version.

Minimalna niezbędna wersja analizatora metadanych

Minimalna niezbędna wersja analizatora metadanych to minimalna wersja analizatora metadanych (kod wygenerowany przez Flatbuffers), która może w całości odczytać Flatbuffers metadanych. Wersja to w rzeczywistości największy numer wersji spośród wersji we wszystkich wypełnionych polach i najmniejsza kompatybilna wersja wskazywana przez identyfikator pliku. Minimalna niezbędna wersja analizatora metadanych jest automatycznie wypełniana przez MetadataPopulator podczas wypełniania metadanych do modelu TFLite. Więcej informacji o tym, jak używana jest minimalna niezbędna wersja analizatora metadanych, można znaleźć w ekstraktorze metadanych .

Przeczytaj metadane z modeli

Biblioteka Metadata Extractor 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 zbudować własne narzędzie do wyodrębniania metadanych w innych językach, korzystając z biblioteki Flatbuffers.

Przeczytaj metadane w Javie

Aby korzystać z biblioteki Metadata Extractor w aplikacji na Androida, zalecamy użycie TensorFlow Lite Metadata AAR 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 następujący sposób:

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

Aby korzystać z nocnych migawek, upewnij się, że dodałeś repozytorium migawek Sonatype .

Możesz zainicjować obiekt MetadataExtractor za pomocą ByteBuffer wskazującego model:

public MetadataExtractor(ByteBuffer buffer);

ByteBuffer musi pozostać niezmieniona przez cały okres istnienia obiektu MetadataExtractor . Inicjalizacja może się nie powieść, jeśli identyfikator pliku Flatbuffers metadanych modelu nie jest zgodny z identyfikatorem analizatora metadanych. Aby uzyskać więcej informacji, zobacz wersjonowanie metadanych .

Dzięki pasującym identyfikatorom plików ekstraktor metadanych pomyślnie odczyta metadane wygenerowane ze wszystkich przeszłych i przyszłych schematów dzięki mechanizmowi kompatybilności do przodu i wstecz Flatbufferów. Jednak starsze moduły wyodrębniania metadanych nie mogą wyodrębniać pól z przyszłych schematów. Minimalna niezbędna wersja metadanych parsera wskazuje minimalną wersję parsera metadanych, która może w całości odczytać płaskie bufory metadanych. Możesz użyć następującej metody, aby sprawdzić, czy spełniony jest warunek minimalnej niezbędnej wersji analizatora składni:

public final boolean isMinimumParserVersionSatisfied();

Dozwolone jest przekazywanie modelu bez metadanych. Jednak wywoływanie metod odczytujących metadane spowoduje błędy w czasie wykonywania. Możesz sprawdzić, czy model posiada metadane, wywołując metodę hasMetadata :

public boolean hasMetadata();

MetadataExtractor zapewnia wygodne funkcje umożliwiające uzyskanie metadanych tensorów wejścia/wyjścia. Na 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);

Chociaż schemat modelu TensorFlow Lite obsługuje wiele podgrafów, interpreter TFLite obsługuje obecnie tylko jeden podgraf. Dlatego MetadataExtractor pomija indeks podgrafu jako argument wejściowy w swoich metodach.

Przeczytaj powiązane pliki z modeli

Model TensorFlow Lite z metadanymi i powiązanymi plikami to zasadniczo plik ZIP, który można rozpakować za pomocą typowych narzędzi ZIP, aby uzyskać powiązane pliki. Na przykład możesz rozpakować mobilenet_v1_0.75_160_quantized i wyodrębnić plik etykiety w modelu w następujący 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

Możesz także odczytać powiązane pliki za pomocą biblioteki Metadata Extractor.

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;