将元数据添加到 LiteRT 模型

LiteRT 元数据为模型描述提供了标准。通过 元数据是了解模型用途及其 输入 / 输出信息。元数据包含

Kaggle 上发布的所有图片模型 模型 元数据。

采用元数据格式的模型

model_with_metadata
图 1.包含元数据和关联文件的 TFLite 模型。

模型元数据在 metadata_schema.fbs, 一 FlatBuffer 文件。如图 1 所示,它存储在 元数据 是 TFLite 模型的一个字段, schema、 (在名称“"TFLITE_METADATA"”下)有些模型可能附有相关文件 如分类标签 文件。 这些文件以 ZIP 的形式串联到原始模型文件的末尾。 使用 ZipFile “append” 模式'a' 模式)。TFLite 解释器可以像以前一样使用新文件格式。请参阅软件包 关联的文件

请参阅以下说明,了解如何填充、直观呈现和读取元数据。

设置元数据工具

在将元数据添加到模型之前,需要 Python 编程 运行 TensorFlow 所需的环境设置。关于如何 请在此处进行设置。

设置好 Python 编程环境后, 其他工具:

pip install tflite-support

LiteRT 元数据工具支持 Python 3。

使用 Flatbuffers Python API 添加元数据

数据集中的模型元数据由三个部分组成, schema

  1. 模型信息 - 模型及其内容的总体说明 例如许可条款请参阅 ModelMetadata
    1. 输入信息 - 输入和预处理的说明 例如标准化处理。请参阅 SubGraphMetadata.input_tensor_metadata
      1. 输出信息 - 输出的说明和 所需的后处理,例如映射到标签。请参阅 SubGraphMetadata.output_tensor_metadata

由于 LiteRT 目前仅支持一个子图, LiteRT 代码生成器Android Studio ML 绑定 功能 将使用 ModelMetadata.nameModelMetadata.description,而不是 SubGraphMetadata.nameSubGraphMetadata.description 元数据和生成代码的过程。

支持的输入 / 输出类型

用于输入和输出的 LiteRT 元数据未采用特定的 模型类型,而是要考虑输入和输出类型。这并不重要 只要输入和输出类型包含 以下各项或以下几项的组合,则受 TensorFlow 支持 精简版元数据:

  • 特征 - 无符号整数或 float32 的数字。
  • 图片 - 元数据目前支持 RGB 和灰度图片。
  • 边界框 - 矩形边界框。该架构支持 多种编号 架构

打包关联文件

LiteRT 模型可能附带不同的关联文件。例如: 自然语言模型通常具有将词段映射到单词的词汇表文件 ID;分类模型可能具有指示对象类别的标签文件。 如果没有关联文件(如果有),模型将无法正常工作。

关联文件现在可以通过元数据与模型捆绑在一起 Python 库。新的 LiteRT 模型会生成一个 ZIP 文件,其中包含 模型和关联文件。可以使用普通 ZIP 文件进行解压缩 工具。这种新的模型格式会保持相同的文件扩展名 .tflite。它 与现有 TFLite 框架和解释器兼容。请参阅软件包元数据 和关联的文件 model

关联的文件信息可以记录在元数据中。取决于 文件类型以及文件的附加位置(即 ModelMetadataSubGraphMetadataTensorMetadata),LiteRT Android 代码 生成器可能适用于相应的前后对比分析 自动处理对象。请参阅 <Codegen 用法>部分 每个关联文件 类型 了解更多详细信息。

归一化和量化参数

归一化是机器学习中常用的数据预处理技术。通过 归一化的目标是将数值更改为通用范围, 失真。

模型量化是一种技术, 可以降低权重的精度表示,还可以选择性地 。

在预处理和后期处理方面,归一化和量化 是两个独立的步骤。详情如下。

规范化 量化

示例 我们将使用 输入图片, 适用于浮动和 量化模型 。
浮点模型
- 平均值:127.5
- 标准:127.5
量化模型
- 平均值:127.5
- 标准:127.5
浮点模型
-zeroPoint:0
- 比例:1.0
量化模型
-zeroPoint:128.0
- scale:0.0078125f




何时调用?


输入源:如果输入源 将数据归一化为 训练、输入 推理需求数据 进行归一化处理, 。
输出:output 数据将 通常已经标准化。
浮点模型 不需要量化。
量化模型 或者可能不需要 前/后量化 处理。视情况而定 关于 输入/输出张量。
- float 张量:no 前/后量化 需要处理。Quant op 和 dequant op 纳入模型 图表。
- int8/uint8 张量: 需要量化 前/后处理。


配方


标准化输入 = (输入 - 平均值)/标准
输入量化
q = f / scale + zeroPoint
反量化 输出
f = (q - zeroPoint) * 体重秤

哪里去了 参数
由模型创建者填充 并存储在 例如 NormalizationOptions 自动填充 TFLite 转换器,以及 存储在 tflite 模型中 文件。
如何获取 参数? 通过 MetadataExtractor API [2] 通过 TFLite Tensor API [1] 或 通过 MetadataExtractor API [2]
执行浮点数和量化 这些模型 值? 是的,浮点数和量化数 模型都拥有 归一化 参数 不,浮点模型没有 不需要量化。
执行 TFLite 代码 生成器或 Android Studio ML 绑定 自动生成 该怎么办?

[1] LiteRT Java APILiteRT C++ API
[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]

创建元数据 Flatbuffers

以下代码将模型信息与输入和输出相结合 信息:

# 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 之后,元数据和标签文件 通过 populate 方法写入 TFLite 文件中:

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 显示元数据 绑定 功能

元数据版本控制

元数据 架构 均由语义版本号进行版本控制,语义版本号可跟踪 以及通过 Flatbuffers 文件标识来指示 真正的版本兼容性。

语义版本号

元数据架构采用语义版本控制 数值、 例如 MAJOR.MINOR.PATCH。它会根据规则跟踪架构更改 此处。 查看历史 字段 在版本 1.0.0 之后添加。

Flatbuffers 文件标识

如果遵循相应规则,语义版本控制可确保兼容性, 并不表示存在真正的不兼容问题。向上移动 MAJOR 编号时, 并不一定意味着向后兼容性已损坏。因此,我们 请使用 Flatbuffers 文件 身份识别file_identifier, 来表示元数据架构的真正兼容性。文件标识符为 长度正好 4 个字符。它固定在特定的元数据架构上,而不是 可能会受到用户更改如果元数据架构的向后兼容性 因为文件因某种原因而被破坏,file_identifier 就会增加,例如: 从“M001”更改为“M002”。File_identifier 的更改预计会大幅减少 频率高于 metadata_version。

所需的最低元数据解析器版本

所需的最低元数据解析器 版本 是元数据解析器(Flatbuffer 生成的代码)的最低版本, 可以完整读取元数据 Flatbuffers。该版本实际上是 从所有已填充字段的版本中选择最大版本号, 文件标识符指明的最小兼容版本。最低 会自动填充必要的元数据解析器版本 在元数据填充到 TFLite 模型中时,返回 MetadataPopulator。请参阅 元数据提取器,详细了解 使用所需的最低元数据解析器版本。

从模型中读取元数据

Metadata Extractor 库是读取元数据和 关联文件(请参阅 Java 版本C++ 版本)。 您可以使用 Flatbuffers 库。

读取 Java 中的元数据

如需在 Android 应用中使用 Metadata Extractor 库,我们建议您使用 LiteRT 元数据 AAR 托管在 MavenCentral. 它包含 MetadataExtractor 类以及 FlatBuffers Java 元数据绑定 架构模型 架构

您可以在 build.gradle 依赖项中进行指定,如下所示:

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

要使用夜间快照,请确保您已添加 Sonatype 快照 代码库

您可以使用指向 ByteBufferMetadataExtractor 对象初始化 传递给模型:

public MetadataExtractor(ByteBuffer buffer);

ByteBuffer 必须在 MetadataExtractor 对象。如果 Flatbuffers 文件 模型元数据的标识符与元数据解析器的标识符不匹配。请参阅 元数据版本控制

有了匹配的文件标识符,元数据提取器将成功读取 由于 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;