LiteRT 元数据为模型说明提供了一个标准。元数据是了解模型功能及其输入 / 输出信息的重要知识来源。元数据包括
- 人类可读的部分,用于传达使用模型时的最佳实践,以及
- 可供代码生成器(例如 LiteRT Android 代码生成器和 Android Studio ML Binding 功能)使用的机器可读部分。
Kaggle Models 上发布的所有图片模型都已填充元数据。
具有元数据格式的模型

模型元数据在 metadata_schema.fbs(一个 FlatBuffer 文件)中定义。如图 1 所示,它存储在 TFLite 模型架构的 metadata 字段中,名称为 "TFLITE_METADATA"。某些模型可能随附相关文件,例如分类标签文件。
这些文件会使用 ZipFile “追加”模式('a' 模式)连接到原始模型文件的末尾,并以 ZIP 格式存储。TFLite 解释器可以像以前一样使用新文件格式。如需了解详情,请参阅打包关联文件。
请参阅下文,了解如何填充、直观呈现和读取元数据。
设置元数据工具
在向模型添加元数据之前,您需要设置一个 Python 编程环境,以便运行 TensorFlow。如需详细了解如何进行此设置,请点击此处。
设置 Python 编程环境后,您需要安装其他工具:
pip install tflite-support
LiteRT 元数据工具支持 Python 3。
使用 Flatbuffers Python API 添加元数据
架构中的模型元数据包含三个部分:
- 模型信息 - 模型的总体说明以及许可条款等项目。请参阅 ModelMetadata。 2. 输入信息 - 对所需输入和预处理(例如归一化)的说明。请参阅 SubGraphMetadata.input_tensor_metadata。 3. 输出信息 - 输出的说明以及所需的后处理(例如映射到标签)。请参阅 SubGraphMetadata.output_tensor_metadata。
由于 LiteRT 目前仅支持单个子图,因此 LiteRT 代码生成器和 Android Studio ML Binding 功能在显示元数据和生成代码时将使用 ModelMetadata.name 和 ModelMetadata.description,而不是 SubGraphMetadata.name 和 SubGraphMetadata.description。
支持的输入 / 输出类型
输入和输出的 LiteRT 元数据并非针对特定模型类型设计,而是针对输入和输出类型设计。无论模型在功能上做什么,只要输入和输出类型包含以下类型或以下类型的组合,TensorFlow Lite 元数据就支持该模型:
- 特征 - 无符号整数或 float32 类型的数字。
- 图片 - 元数据目前支持 RGB 和灰度图片。
- 边界框 - 矩形边界框。该架构支持多种编号方案。
打包关联文件
LiteRT 模型可能附带不同的关联文件。例如,自然语言模型通常具有将字块映射到字 ID 的词汇文件;分类模型可能具有指示对象类别的标签文件。如果没有关联的文件(如果有),模型将无法正常运行。
现在,可以通过元数据 Python 库将关联的文件与模型捆绑在一起。新的 LiteRT 模型会成为一个 zip 文件,其中包含模型和关联文件。可以使用常见的 ZIP 工具解压缩。这种新模型格式仍使用相同的文件扩展名 .tflite。它与现有的 TFLite 框架和 Interpreter 兼容。如需了解详情,请参阅将元数据和关联文件打包到模型中。
关联的文件信息可以记录在元数据中。根据文件类型以及文件附加到的位置(即 ModelMetadata、SubGraphMetadata 和 TensorMetadata),LiteRT Android 代码生成器可能会自动对对象应用相应的预处理/后处理。如需了解详情,请参阅架构中每种关联文件类型的 <Codegen usage> 部分。
归一化和量化参数
归一化是机器学习中常见的数据预处理技术。归一化的目的是将值更改为通用比例,而不会扭曲值范围的差异。
模型量化是一种技术,可降低权重(以及可选的激活)的精度,以便进行存储和计算。
在预处理和后处理方面,归一化和量化是两个独立的步骤。以下是详细信息。
| 规范化 | 量化 | |
|---|---|---|
MobileNet 中浮点模型和量化模型的输入图片参数值示例。 |
浮点模型: - 平均值:127.5 - 标准差:127.5 量化模型: - 平均值:127.5 - 标准差:127.5 |
浮点模型: - zeroPoint: 0 - scale: 1.0 量化模型: - zeroPoint: 128.0 - scale:0.0078125f |
何时调用? |
输入:如果输入数据在训练时已归一化,则推理的输入数据也需要相应地进行归一化。 输出:输出数据一般不会进行归一化处理。 |
浮点模型不需要量化。 量化模型可能需要也可能不需要在预处理/后处理中进行量化。这取决于输入/输出张量的数据类型。 - float 张量:无需在预处理/后处理中进行量化。量化操作和反量化操作已纳入模型图。 - int8/uint8 张量:需要在预处理/后处理中进行量化。 |
公式 |
normalized_input = (input - mean) / std |
输入量化:
q = f / scale + zeroPoint 输出反量化: f = (q - zeroPoint) * scale |
参数位于何处 |
由模型创建者填写,并以 NormalizationOptions 形式存储在模型元数据中 |
由 TFLite 转换器自动填充,并存储在 tflite 模型文件中。 |
| 如何获取参数? | 通过 MetadataExtractor API
[2]
|
通过 TFLite Tensor API [1] 或通过 MetadataExtractor API [2] |
| 浮点模型和量化模型是否共享相同的值? | 可以,浮点模型和量化模型具有相同的归一化参数 | 不需要,浮点模型不需要量化。 |
| TFLite 代码生成器或 Android Studio ML 绑定是否会在数据处理中自动生成它? | 是 |
是 |
[1] LiteRT Java API 和 LiteRT 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 直观呈现元数据,也可以使用 MetadataDisplayer 将 LiteRT 模型中的元数据读取为 JSON 格式:
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 文件标识
如果遵循语义化版本控制规则,则可保证兼容性,但并不意味着真正的不兼容。提高主要版本号并不一定意味着向后兼容性被打破。因此,我们使用 Flatbuffers 文件标识符 file_identifier 来表示元数据架构的真实兼容性。文件标识符的长度正好为 4 个字符。它固定为某个元数据架构,不会因用户而发生变化。如果出于某种原因必须打破元数据架构的向后兼容性,file_identifier 将会递增,例如从“M001”递增到“M002”。与 metadata_version 相比,file_identifier 的更改频率预计会低得多。
最低必需的元数据解析器版本
必需的最低元数据解析器版本是指能够完整读取元数据 Flatbuffers 的最低元数据解析器(Flatbuffers 生成的代码)版本。该版本实际上是所有已填充字段的版本中最大的版本号,也是文件标识符指示的最小兼容版本。当元数据填充到 TFLite 模型中时,MetadataPopulator 会自动填充最低必需的元数据解析器版本。如需详细了解如何使用最低必需的元数据解析器版本,请参阅元数据提取器。
从模型中读取元数据
元数据提取器库是一种便捷的工具,可用于从不同平台的模型中读取元数据和关联文件(请参阅 Java 版本和 C++ 版本)。您可以使用 Flatbuffers 库以其他语言构建自己的元数据提取器工具。
在 Java 中读取元数据
如需在 Android 应用中使用元数据提取器库,我们建议使用 MavenCentral 上托管的 LiteRT 元数据 AAR。它包含 MetadataExtractor 类,以及 元数据架构和模型架构的 FlatBuffers Java 绑定。
您可以在 build.gradle 依赖项中按如下方式指定此内容:
dependencies {
implementation 'org.tensorflow:tensorflow-lite-metadata:0.1.0'
}
如需使用每晚快照,请确保您已添加 Sonatype 快照代码库。
您可以使用指向模型的 ByteBuffer 初始化 MetadataExtractor 对象:
public MetadataExtractor(ByteBuffer buffer);
在 MetadataExtractor 对象的整个生命周期内,ByteBuffer 必须保持不变。如果模型元数据的 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
您还可以通过元数据提取器库读取关联的文件。
在 Java 中,将文件名传递给 MetadataExtractor.getAssociatedFile 方法:
public InputStream getAssociatedFile(String fileName);
同样,在 C++ 中,可以使用方法 ModelMetadataExtractor::GetAssociatedFile 来实现此目的:
tflite::support::StatusOr<absl::string_view> GetAssociatedFile(
const std::string& filename) const;