Добавление метаданных в модели LiteRT

Метаданные LiteRT предоставляют стандарт для описаний моделей. Метаданные являются важным источником знаний о том, что делает модель, и ее входной/выходной информации. Метаданные состоят из обоих

Все модели изображений, опубликованные на Kaggle Models, заполнены метаданными.

Модель с форматом метаданных

модель_с_метаданными
Рисунок 1. Модель TFLite с метаданными и связанными файлами.

Метаданные модели определяются в файле Metadata_schema.fbs , FlatBuffer . Как показано на рисунке 1, он хранится в поле метаданных схемы модели TFLite под именем "TFLITE_METADATA" . Некоторые модели могут поставляться со связанными файлами, например файлами классификационных меток . Эти файлы объединяются в конец исходного файла модели в виде ZIP-файла с использованием режима добавления ZipFile (режим 'a' ). TFLite Interpreter может использовать новый формат файла так же, как и раньше. Дополнительную информацию см. в разделе Упаковка связанных файлов .

См. инструкцию ниже о том, как заполнять, визуализировать и читать метаданные.

Настройка инструментов метаданных

Прежде чем добавлять метаданные в вашу модель, вам потребуется настроить среду программирования Python для запуска TensorFlow. Подробное руководство о том, как это настроить, есть здесь .

После настройки среды программирования Python вам потребуется установить дополнительные инструменты:

pip install tflite-support

Инструмент метаданных LiteRT поддерживает Python 3.

Добавление метаданных с помощью API Flatbuffers Python

Метаданные модели в схеме состоят из трех частей:

  1. Информация о модели — общее описание модели, а также такие элементы, как условия лицензии. См. МодельМетаданные .
    1. Входная информация — описание входных данных и необходимой предварительной обработки, например нормализации. См. SubGraphMetadata.input_tensor_metadata .
      1. Выходная информация — описание вывода и необходимой постобработки, например сопоставление с метками. См. SubGraphMetadata.output_tensor_metadata .

Поскольку на данный момент LiteRT поддерживает только один подграф, генератор кода LiteRT и функция привязки Android Studio ML будут использовать ModelMetadata.name и ModelMetadata.description вместо SubGraphMetadata.name и SubGraphMetadata.description при отображении метаданных и создании кода.

Поддерживаемые типы ввода/вывода

Метаданные LiteRT для ввода и вывода разрабатываются не с учетом конкретных типов моделей, а с учетом типов ввода и вывода. Не имеет значения, что функционально делает модель, если типы ввода и вывода состоят из следующих элементов или их комбинации, они поддерживаются метаданными TensorFlow Lite:

  • Функция — числа, которые являются целыми числами без знака или числами с плавающей запятой32.
  • Изображение. Метаданные в настоящее время поддерживают изображения RGB и оттенки серого.
  • Ограничительная рамка — ограничивающие рамки прямоугольной формы. Схема поддерживает различные схемы нумерации .

Упакуйте связанные файлы

Модели LiteRT могут иметь разные связанные файлы. Например, модели естественного языка обычно содержат файлы словаря, которые сопоставляют части слов с идентификаторами слов; Модели классификации могут иметь файлы меток, обозначающие категории объектов. Без связанных файлов (если они есть) модель не будет работать должным образом.

Связанные файлы теперь можно объединить с моделью через библиотеку метаданных Python. Новая модель LiteRT становится zip-файлом, содержащим как саму модель, так и связанные с ней файлы. Его можно распаковать обычными zip-инструментами. Этот новый формат модели продолжает использовать то же расширение файла — .tflite . Он совместим с существующей платформой TFLite и интерпретатором. Дополнительные сведения см. в разделе Упаковка метаданных и связанных файлов в модель .

Соответствующая информация о файле может быть записана в метаданных. В зависимости от типа файла и места, к которому он прикреплен (т. е. ModelMetadata , SubGraphMetadata и TensorMetadata ), генератор кода LiteRT Android может автоматически применять к объекту соответствующую пред-/постобработку. Дополнительные сведения см. в разделе <Использование кода> для каждого связанного типа файла в схеме.

Параметры нормализации и квантования

Нормализация — это распространенный метод предварительной обработки данных в машинном обучении. Цель нормализации – привести значения к единой шкале, не искажая различий в диапазонах значений.

Квантование модели — это метод, который позволяет снизить точность представления весов и, при необходимости, активировать как хранение, так и вычисления.

С точки зрения предварительной и постобработки нормализация и квантование представляют собой два независимых этапа. Вот подробности.

Нормализация Квантование

Пример значений параметров входного изображения в MobileNet для моделей с плавающей запятой и квантовой модели соответственно.
Поплавковая модель :
- среднее: 127,5
- стандарт: 127,5
Квантовая модель :
- среднее: 127,5
- стандарт: 127,5
Поплавковая модель :
- нулевая точка: 0
- масштаб: 1,0
Квантовая модель :
- нулевая точка: 128,0
- масштаб: 0,0078125f




Когда вызывать?


Входные данные : если входные данные нормализуются при обучении, входные данные вывода необходимо нормализовать соответствующим образом.
Выходы : выходные данные в целом не будут нормализованы.
Модели с плавающей запятой не нуждаются в квантовании.
Квантованная модель может нуждаться в квантовании, а может и не нуждаться в нем при предварительной/постобработке. Это зависит от типа данных тензоров ввода/вывода.
- Тензоры с плавающей запятой: квантование в предварительной/постобработке не требуется. Квантовые и деквантные операции встроены в граф модели.
- Тензоры int8/uint8: требуют квантования при предварительной/постобработке.


Формула


нормализованный_вход = (ввод — среднее) / стандартное
Квантование входных данных :
q = f / масштаб + нулевая точка
Деквантование для выходов :
f = (q – нулевая точка) * масштаб

Где параметры
Заполняется создателем модели и сохраняется в метаданных модели как NormalizationOptions . Заполняется автоматически конвертером TFLite и сохраняется в файле модели tflite.
Как получить параметры? Через API MetadataExtractor [2] Через TFLite Tensor API [1] или через API MetadataExtractor [2]
Имеют ли модели с плавающей запятой и квантовой величиной одно и то же значение? Да, модели с плавающей запятой и квантовые модели имеют одинаковые параметры нормализации. Нет, модель с плавающей запятой не нуждается в квантовании.
Генератор кода TFLite или привязка Android Studio ML автоматически генерируют его при обработке данных?
Да

Да

[1] API LiteRT Java и API LiteRT C++ .
[2] Библиотека экстрактора метаданных

При обработке данных изображения для моделей uint8 нормализация и квантование иногда пропускаются. Это можно сделать, когда значения пикселей находятся в диапазоне [0, 255]. Но в целом вам всегда следует обрабатывать данные в соответствии с параметрами нормализации и квантования, если это применимо.

Примеры

Здесь вы можете найти примеры заполнения метаданных для разных типов моделей:

Классификация изображений

Загрузите здесь скрипт, который заполняет метаданные в mobilenet_v1_0.75_160_quantized.tflite . Запустите скрипт следующим образом:

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

Чтобы заполнить метаданные для других моделей классификации изображений, добавьте в скрипт такие спецификации модели. В оставшейся части этого руководства будут выделены некоторые ключевые разделы примера классификации изображений, чтобы проиллюстрировать ключевые элементы.

Подробное изучение примера классификации изображений.

Информация о модели

Метаданные начинаются с создания новой информации о модели:

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.")

Входная/выходная информация

В этом разделе показано, как описать входную и выходную сигнатуру вашей модели. Эти метаданные могут использоваться автоматическими генераторами кода для создания кода предварительной и постобработки. Чтобы создать входную или выходную информацию о тензоре:

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

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

Ввод изображения

Изображение — это распространенный тип входных данных для машинного обучения. Метаданные LiteRT поддерживают такую ​​информацию, как цветовое пространство, и информацию предварительной обработки, например нормализацию. Размер изображения не требует указания вручную, поскольку он уже задан формой входного тензора и может быть выведен автоматически.

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

Вывод этикетки

Метку можно сопоставить с выходным тензором через связанный файл с помощью 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]

Создайте плоские буферы метаданных

Следующий код объединяет информацию о модели с входной и выходной информацией:

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

Упакуйте метаданные и связанные файлы в модель.

После создания Flatbuffers метаданных метаданные и файл метки записываются в файл TFLite с помощью метода 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()

Вы можете упаковать в модель столько связанных файлов, сколько захотите, с помощью load_associated_files . Однако необходимо упаковать как минимум те файлы, которые указаны в метаданных. В этом примере упаковка файла этикетки обязательна.

Визуализируйте метаданные

Вы можете использовать Netron для визуализации ваших метаданных или прочитать метаданные из модели LiteRT в формат json с помощью 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 также поддерживает отображение метаданных с помощью функции Android Studio ML Binding .

Управление версиями метаданных

Версия схемы метаданных определяется как семантическим номером версии, который отслеживает изменения файла схемы, так и идентификатором файла Flatbuffers, который указывает на истинную совместимость версий.

Семантический номер версии

Версия схемы метаданных определяется семантическим номером версии , например MAJOR.MINOR.PATCH. Он отслеживает изменения схемы согласно правилам здесь . Смотрите историю полей, добавленных после версии 1.0.0 .

Идентификация файла Flatbuffers

Семантическое управление версиями гарантирует совместимость при соблюдении правил, но не подразумевает настоящую несовместимость. Увеличение числа MAJOR не обязательно означает нарушение обратной совместимости. Поэтому мы используем идентификатор файла Flatbuffers , file_identifier , чтобы обозначить истинную совместимость схемы метаданных. Идентификатор файла имеет длину ровно 4 символа. Он привязан к определенной схеме метаданных и не подлежит изменению пользователями. Если по какой-то причине необходимо нарушить обратную совместимость схемы метаданных, file_identifier увеличится, например, с «M001» на «M002». Ожидается, что File_identifier будет меняться гораздо реже, чем метаданные_версия.

Минимально необходимая версия парсера метаданных

Минимально необходимая версия парсера метаданных — это минимальная версия парсера метаданных (код, сгенерированный Flatbuffers), который может полностью прочитать метаданные Flatbuffers. Версия фактически представляет собой наибольший номер версии среди версий всех заполненных полей и наименьшую совместимую версию, указанную идентификатором файла. Минимально необходимая версия анализатора метаданных автоматически заполняется MetadataPopulator , когда метаданные заполняются в модель TFLite. См. экстрактор метаданных для получения дополнительной информации о том, как используется минимально необходимая версия анализатора метаданных.

Чтение метаданных из моделей

Библиотека Metadata Extractor — удобный инструмент для чтения метаданных и связанных файлов из моделей на разных платформах (см. версию Java и версию C++ ). Вы можете создать свой собственный инструмент извлечения метаданных на других языках, используя библиотеку Flatbuffers.

Чтение метаданных в Java

Чтобы использовать библиотеку Metadata Extractor в вашем приложении для Android, мы рекомендуем использовать LiteRT Metadata AAR, размещенный на MavenCentral . Он содержит класс MetadataExtractor , а также Java-привязки FlatBuffers для схемы метаданных и схемы модели .

Вы можете указать это в зависимостях build.gradle следующим образом:

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

Чтобы использовать ночные снимки, убедитесь, что вы добавили репозиторий снимков Sonatype .

Вы можете инициализировать объект MetadataExtractor с помощью ByteBuffer , указывающего на модель:

public MetadataExtractor(ByteBuffer buffer);

ByteBuffer должен оставаться неизменным в течение всего времени существования объекта MetadataExtractor . Инициализация может завершиться неудачно, если идентификатор файла метаданных модели Flatbuffers не совпадает с идентификатором анализатора метаданных. Дополнительную информацию см. в разделе Управление версиями метаданных .

При совпадении идентификаторов файлов средство извлечения метаданных успешно прочитает метаданные, сгенерированные из всех прошлых и будущих схем, благодаря механизму прямой и обратной совместимости Flatbuffers. Однако поля из будущих схем невозможно извлечь с помощью старых экстракторов метаданных. Минимально необходимая версия анализатора метаданных указывает минимальную версию анализатора метаданных, которая может полностью прочитать плоские буферы метаданных. Вы можете использовать следующий метод, чтобы проверить, выполняется ли минимально необходимое условие версии парсера:

public final boolean isMinimumParserVersionSatisfied();

Разрешена передача модели без метаданных. Однако вызов методов, считывающих метаданные, приведет к ошибкам во время выполнения. Вы можете проверить, есть ли у модели метаданные, вызвав метод hasMetadata :

public boolean hasMetadata();

MetadataExtractor предоставляет удобные функции для получения метаданных тензоров ввода/вывода. Например,

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

Хотя схема модели LiteRT поддерживает несколько подграфов, интерпретатор TFLite в настоящее время поддерживает только один подграф. Поэтому MetadataExtractor опускает индекс подграфа в качестве входного аргумента в своих методах.

Чтение связанных файлов из моделей

Модель LiteRT с метаданными и связанными файлами по сути представляет собой zip-файл, который можно распаковать с помощью обычных инструментов zip, чтобы получить связанные файлы. Например, вы можете разархивировать mobilenet_v1_0.75_160_quantized и извлечь файл метки в модели следующим образом:

$ 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

Вы также можете прочитать связанные файлы с помощью библиотеки Metadata Extractor.

В Java передайте имя файла в метод MetadataExtractor.getAssociatedFile :

public InputStream getAssociatedFile(String fileName);

Аналогично в C++ это можно сделать с помощью метода ModelMetadataExtractor::GetAssociatedFile :

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