Dodawanie metadanych do modeli TensorFlow Lite

Metadane TensorFlow Lite zapewnia standard opisów modeli. Metadane to ważne źródło wiedzy o tym, jak działa model oraz o danych wejściowych i wyjściowych. Metadane składają się

Wszystkie modele obrazów opublikowane w modelach Kaggle zostały wypełnione metadanymi.

Model w formacie 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 ilustracji 1, jest on przechowywany w polu metadata (metadane) schematu modelu TFLite pod nazwą "TFLITE_METADATA". Niektóre modele mogą mieć powiązane pliki, takie jak pliki etykiet klasyfikacji. Pliki te są dołączone na końcu oryginalnego pliku modelu jako plik ZIP w trybie „append” (tryb 'a') ZipFile. TFLite Tłumacz może przetwarzać nowy format plików 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 obsługi metadanych

Przed dodaniem metadanych do modelu musisz skonfigurować środowisko programistyczne Pythona do uruchomienia TensorFlow. Tutaj znajdziesz szczegółowy przewodnik, jak to zrobić.

Po skonfigurowaniu środowiska programistycznego w Pythonie musisz zainstalować dodatkowe narzędzia:

pip install tflite-support

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

Dodawanie metadanych za pomocą interfejsu Flatbuffers Python API

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

  1. Informacje o modelu – ogólny opis modelu, jak również informacje takie jak warunki licencji. Zobacz ModelMetadata.
    1. Informacje wejściowe – opis danych wejściowych i wymaganego przetwarzania wstępnego, np. normalizacji. Zobacz SubGraphMetadata.input_tensor_metadata.
      1. Informacje wyjściowe – opis danych wyjściowych i wymaganego przetwarzania końcowego, np. mapowania na etykiety. Zobacz SubGraphMetadata.output_tensor_metadata.

Ponieważ TensorFlow Lite obsługuje na tym etapie tylko 1 podgraf, generator kodu TensorFlow Lite i funkcja powiązań ML w Android Studio przy wyświetlaniu metadanych i generowaniu kodu będą używać ModelMetadata.name i ModelMetadata.description zamiast SubGraphMetadata.name i SubGraphMetadata.description.

Obsługiwane typy wejścia / wyjścia

Metadane TensorFlow Lite dotyczące danych wejściowych i wyjściowych nie zostały zaprojektowane z myślą o konkretnych typach modeli, lecz o typach danych wejściowych i wyjściowych. Nie ma znaczenia, jak funkcjonuje model, o ile typy danych wejściowych i wyjściowych składają się z poniższych elementów lub ich kombinacji, są one obsługiwane przez metadane TensorFlow Lite:

  • Cecha – liczby, które są liczbami całkowitymi bez znaku lub liczbą zmiennoprzecinkową zmiennoprzecinkową.
  • Obraz – metadane obecnie obsługują obrazy w kolorze RGB i w skali szarości.
  • Ramka ograniczająca – prostokątne ramki ograniczające kształtu. Schemat obsługuje różne schematy numerowania.

Spakuj 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. Bez powiązanych plików (jeśli istnieją) model nie będzie działał poprawnie.

Powiązane pliki można teraz grupować z modelem za pomocą biblioteki metadanych w Pythonie. Nowy model TensorFlow Lite stanie się plikiem ZIP zawierającym zarówno model, jak i powiązane z nim pliki. Można go rozpakować za pomocą popularnych narzędzi do pakowania. W nowym formacie modelu nadal jest używane to samo rozszerzenie pliku: .tflite. Jest on zgodny z dotychczasową platformą TFLite i funkcją tłumaczenia rozmowy. Więcej informacji znajdziesz w sekcji Dodawanie metadanych i powiązanych plików do modelu.

Informacje o powiązanych plikach można zarejestrować w metadanych. W zależności od typu pliku i miejsca, do którego jest on dołączony (np. ModelMetadata, SubGraphMetadata i TensorMetadata), generator kodu TensorFlow Lite na Androida może automatycznie zastosować odpowiednie wstępne i końcowe przetwarzanie do obiektu. Więcej informacji znajdziesz w sekcji dotyczącej użycia kodu Codegen> każdego powiązanego typu pliku w schemacie.

Parametry normalizacji i kwantyzacji

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

Kwantyzacja modelu to technika, która umożliwia mniej precyzyjne reprezentacje wag i opcjonalnie aktywacje zarówno pamięci masowej, jak i obliczeń.

W przypadku wstępnego i ponownego przetwarzania danych normalizacja i kwantyzacja to 2 niezależne kroki. Oto szczegóły:

Normalizacja Kwantyfikacja

Przykład wartości parametrów obrazu wejściowego w MobileNet na potrzeby modeli zmiennoprzecinkowych i kwantowych.
Model swobodny:
– średnia: 127,5
– standardowy: 127,5
Model ilościowy:
– średnia: 127,5
– standardowy: 127,5
Model zmiennoprzecinkowy:
- zeroPoint: 0
- skala: 1,0
Model ilościowy:
- zeroPoint: 128,0
- skala:0,0078125f




Kiedy wywoływać?


Dane wejściowe: jeśli dane wejściowe są znormalizowane podczas trenowania, należy odpowiednio znormalizować dane wejściowe wnioskowania.
Dane wyjściowe: ogólne dane wyjściowe nie będą normalizowane.
Modele pływające nie wymagają kwantyzacji.
Model kwantyzowany może lub nie wymagać kwantyzacji w ramach przetwarzania wstępnego i po nim. Zależy to od typu danych tensorów wejściowych/wyjściowych.
– tensory zmiennoprzecinkowe: nie trzeba kwantyzować procesu wstępnego i postaciowego. Na wykresie modelu są zawarte wartości kwantowe i zerowe.
– tensory int8/uint8: wymaga kwantyzacji przed przetwarzaniem i po nim.


Formuła


normalized_input = (dane wejściowe – średnia) / std
Kwantyfikacja danych wejściowych:
q = f / skala + zeroPoint
Dekwantyzowanie danych wyjściowych:
f = (q - zeroPoint) * skala

Gdzie są parametry
Dane są wypełniane przez twórcę modelu i zapisane w metadanych modelu jako NormalizationOptions Wypełnione automatycznie przez konwerter TFLite i zapisane w pliku modelu tflite.
Skąd wziąć parametry? Za pomocą interfejsu API MetadataExtractor [2] Za pomocą TFLite API Tensor[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 w Android Studio generuje go automatycznie podczas przetwarzania danych?
Tak

Tak

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

Podczas przetwarzania danych obrazu w modelach uint8 czasami normalizacja i kwantyzacja są pomijane. Jest to dozwolone, gdy wartości pikseli mieszczą się w zakresie [0, 255]. Ogólnie rzecz biorąc, dane należy zawsze przetwarzać zgodnie z parametrami normalizacji i kwantyzacji (w odpowiednich przypadkach).

Przykłady

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

Klasyfikacja obrazów

Pobierz ten skrypt tutaj, który uzupełnia 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łniać metadane innych modeli klasyfikacji obrazów, dodaj do skryptu specyfikacje modelu tutaj. W pozostałej części tego przewodnika omawiamy niektóre kluczowe sekcje przykładu klasyfikacji obrazów, aby zilustrować kluczowe elementy.

Szczegółowa analiza przykładu klasyfikacji obrazów

Informacje o modelu

Metadane zaczynają 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 / wyjściowe

Ta sekcja pokazuje, jak opisać podpis wejściowy i wyjściowy modelu. Automatyczne generatory kodu mogą używać tych metadanych 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 i dane dotyczące wstępnego przetwarzania danych, takie jak normalizacja. Wymiary obrazu nie wymagają ręcznego podawania danych, ponieważ są już określone przez kształt tensora wejściowego i można je określić 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 etykiety

Etykieta można zmapować na tensor wyjściowy za pomocą powiązanego pliku za pomocą 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 metadanych Flatbuffers

Ten kod łączy informacje o modelu z danymi 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 do modelu metadane i powiązane pliki

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()

Za pomocą load_associated_files możesz zapakować do modelu dowolną liczbę powiązanych plików. Konieczne jest jednak zapakowanie przynajmniej plików udokumentowanych w metadanych. W tym przykładzie spakowanie pliku z etykietami jest obowiązkowe.

Wizualizacja metadanych

Możesz użyć narzędzia Netron do wizualizacji metadanych lub odczytać je 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 też wyświetlanie metadanych za pomocą funkcji powiązań ML w Android Studio.

Obsługa wersji metadanych

Schemat metadanych jest obsługiwany przez wersje semantycznego numeru wersji, który śledzi zmiany w pliku schematu, oraz identyfikatora pliku Flatbuffers, który wskazuje prawdziwą zgodność wersji.

Numer semantycznej obsługi wersji

Schemat metadanych jest objęty numerem semantycznej obsługi wersji, na przykład GŁÓWNA.PODRZĘDNA.POPRAWKA. Śledzi zmiany schematu zgodnie z regułami opisanymi tutaj. Sprawdź historię pól dodaną po wersji 1.0.0.

Identyfikacja pliku Flatbuffers

Semantyczna obsługa wersji gwarantuje zgodność, jeśli jest zgodna z regułami, ale nie sugeruje rzeczywistej niezgodności. Zwiększenie wartości GŁÓWNA nie musi oznaczać, że zgodność wsteczna została przerwana. Dlatego jako identyfikator plików Flatbuffers używamy atrybutu file_identifier, aby wskazać prawdziwą zgodność schematu metadanych. Identyfikator pliku ma dokładnie 4 znaki. Jest on ustalony na podstawie określonego schematu metadanych i nie podlega zmianom przez użytkowników. Jeśli z jakiegoś powodu wymagana jest zgodność wsteczna schematu metadanych, parametr file_identifier będzie się podbijać np. z „M001” do „M002”. Wartość File_identifier będzie zmieniana znacznie rzadziej niż parametr metadata_version.

Minimalna wymagana wersja parsera metadanych

Minimalna niezbędna wersja parsera metadanych to minimalna wersja parsera metadanych (wygenerowanego kodu Flatbuffers), który może w pełni odczytać elementy Flatbuffers do odczytu w całości. Jest to w praktyce największy numer 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, gdy metadane zostaną wprowadzone do modelu TFLite. Więcej informacji o tym, jak używać minimalnej wymaganej wersji parsera metadanych, znajdziesz w module wyodrębniania metadanych.

Odczytywanie metadanych z modeli

Biblioteka wyodrębniania metadanych to wygodne narzędzie do odczytu 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 Flatbuffers.

Odczytywanie metadanych w Javie

Jeśli chcesz używać biblioteki wyodrębniania metadanych w aplikacji na Androida, zalecamy użycie biblioteki AAR TensorFlow Lite Metadata AAR hostowanej w MavenCentral. Zawiera klasę MetadataExtractor oraz 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 korzystać z nocnych zrzutów, musisz dodać repozytorium zrzutów Sonatype.

Możesz zainicjować obiekt MetadataExtractor obiektem ByteBuffer wskazującym model:

public MetadataExtractor(ByteBuffer buffer);

ByteBuffer musi pozostać niezmienione przez cały okres istnienia obiektu MetadataExtractor. Zainicjowanie może się nie udać, jeśli identyfikator pliku Flatbuffers metadanych modelu nie jest zgodny z identyfikatorem parsera metadanych. Więcej informacji znajdziesz w sekcji na temat obsługi wersji metadanych.

Dzięki pasującym identyfikatorami plików moduł wyodrębniania metadanych będzie odczytywać metadane wygenerowane na podstawie wszystkich przeszłych i przyszłych schematów dzięki mechanizmowi zgodności progowej i wcześniejszej Flatbuffers. Starsze moduły wyodrębniania metadanych nie mogą jednak wyodrębnić pól z przyszłych schematów. Minimalna wymagana wersja parsera metadanych wskazuje minimalną wersję parsera metadanych, która może w pełni odczytać metadane płaskie. Aby sprawdzić, czy spełniony jest minimalny warunek wersji parsera:

public final boolean isMinimumParserVersionSatisfied();

Dozwolone jest przekazywanie modelu bez metadanych. Jednak wywoływanie metod, które odczytują metadane, powoduje jednak błędy podczas działania. Aby sprawdzić, czy model ma metadane, wywołaj metodę hasMetadata:

public boolean hasMetadata();

MetadataExtractor udostępnia wygodne funkcje pobierania metadanych tensorów wejściowych/wyjściowych. 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, TFLite Interpreter obsługuje obecnie tylko jeden podgraf. Dlatego funkcja MetadataExtractor pomija indeks podgrafu jako argumentu wejściowego w swoich metodach.

Odczytywanie powiązanych plików z modeli

Model TensorFlow Lite z metadanymi i powiązanymi plikami to zasadniczo plik ZIP, który można rozpakować za pomocą popularnych narzędzi do spakowania, 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:

$ 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 ekstraktora metadanych.

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

public InputStream getAssociatedFile(String fileName);

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

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