在 LiteRT 模型中新增中繼資料

LiteRT 中繼資料提供模型說明的標準。中繼資料是模型功能和輸入 / 輸出資訊的重要知識來源。中繼資料包含

Kaggle 模型上發布的所有圖像模型都已填入中繼資料。

含有中繼資料格式的模型

model_with_metadata
圖 1. 含有中繼資料和相關聯檔案的 TFLite 模型。

模型中繼資料定義於 metadata_schema.fbs,這是 FlatBuffer 檔案。如圖 1 所示,這項資料會儲存在 TFLite 模型結構定義中繼資料欄位中,名稱為 "TFLITE_METADATA"。部分模型可能附有相關聯的檔案,例如分類標籤檔案。這些檔案會以 ZIP 格式串連至原始模型檔案的結尾,並使用 ZipFile「append」模式 ('a' 模式) 。TFLite 解譯器可以像以往一樣使用新檔案格式。詳情請參閱「封裝相關聯的檔案」。

請參閱下方的說明,瞭解如何填入、顯示及讀取中繼資料。

設定中繼資料工具

為模型新增中繼資料前,您需要設定 Python 程式設計環境,才能執行 TensorFlow。如需詳細設定指南,請參閱這篇文章

設定 Python 程式設計環境後,您需要安裝其他工具:

pip install tflite-support

LiteRT 中繼資料工具支援 Python 3。

使用 Flatbuffers Python API 新增中繼資料

結構定義中的模型中繼資料包含三個部分:

  1. 模型資訊:模型的整體說明,以及授權條款等項目。請參閱 ModelMetadata。 2. 輸入資訊 - 輸入內容的說明和必要的前置處理,例如正規化。請參閱 SubGraphMetadata.input_tensor_metadata。 3. 輸出資訊 - 輸出內容的說明和所需的後續處理作業,例如對應至標籤。請參閱 SubGraphMetadata.output_tensor_metadata

由於 LiteRT 目前僅支援單一子圖,因此 LiteRT 程式碼產生器Android Studio ML 繫結功能會使用 ModelMetadata.nameModelMetadata.description,而非 SubGraphMetadata.nameSubGraphMetadata.description,顯示中繼資料並產生程式碼。

支援的輸入 / 輸出類型

輸入和輸出的 LiteRT 中繼資料並非專為特定模型類型設計,而是針對輸入和輸出類型。只要輸入和輸出類型包含下列類型或下列類型的組合,TensorFlow Lite 中繼資料就支援該模型,模型的功能為何並不重要:

  • 特徵 - 無正負號整數或 float32。
  • 圖片 - 中繼資料目前支援 RGB 和灰階圖片。
  • 定界框 - 矩形定界框。這個結構定義支援各種編號方式

封裝相關聯的檔案

LiteRT 模型可能附有不同的相關聯檔案。舉例來說,自然語言模型通常會有將字詞片段對應至字詞 ID 的字彙檔案;分類模型則可能有指出物件類別的標籤檔案。如果沒有相關聯的檔案 (如有),模型就無法正常運作。

現在可透過中繼資料 Python 程式庫,將相關聯的檔案與模型一併封裝。新的 LiteRT 模型會變成 ZIP 檔案,內含模型和相關聯的檔案。您可以使用常見的 ZIP 工具解壓縮。這個新模型格式仍使用相同的檔案副檔名 .tflite。與現有的 TFLite 架構和解譯器相容。詳情請參閱將中繼資料和相關聯的檔案封裝到模型中

相關檔案資訊可以記錄在中繼資料中。視檔案類型和附加檔案的位置 (即 ModelMetadataSubGraphMetadataTensorMetadata) 而定,LiteRT Android 程式碼產生器可能會自動對物件套用對應的前/後處理程序。詳情請參閱結構定義中每個關聯檔案類型的「<Codegen usage>」部分

正規化和量化參數

正規化是機器學習中常見的資料預先處理技術。正規化的目標是將值變更為通用比例,但不會扭曲值範圍的差異。

模型量化技術可降低權重的精確度,並視需要降低儲存和運算時的啟用次數。

就預先處理和後續處理而言,正規化和量化是兩個獨立的步驟。以下是問題的詳細說明:

正規化 量化

MobileNet 中輸入圖片的參數值範例,分別適用於浮點和量化模型。
浮點模型
- 平均值:127.5
- 標準差:127.5
量化模型
- 平均值:127.5
- 標準差:127.5
浮點模型
- zeroPoint:0
- scale:1.0
量化模型
- zeroPoint:128.0
- scale:0.0078125f




何時叫用?


輸入:如果訓練資料已正規化,推論資料也必須相應正規化。
輸出內容:輸出資料一般不會經過正規化。
浮點模型不需要量化。
量化模型可能需要或不需要在預先/後續處理中量化。這取決於輸入/輸出張量的資料類型。
- 浮點張量:前/後處理不需要量化。量化作業和去量化作業會併入模型圖表。
- 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 機器學習繫結是否會在資料處理時自動產生這項資訊?

[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 將中繼資料視覺化,也可以使用 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 檔案識別

如果遵循規則,語意版本控管可確保相容性,但這並不代表真正的不相容。調升 MAJOR 編號時,不一定表示回溯相容性會中斷。因此,我們會使用 Flatbuffers 檔案識別資訊 (file_identifier),表示中繼資料結構定義的實際相容性。檔案 ID 長度必須為 4 個字元。這項設定會固定為特定中繼資料結構定義,使用者無法變更。如果因故必須打破中繼資料架構的回溯相容性,file_identifier 就會增加,例如從「M001」變成「M002」。預期 file_identifier 的變更頻率會遠低於 metadata_version。

中繼資料剖析器最低必要版本

必要的中繼資料剖析器最低版本是指可完整讀取中繼資料 Flatbuffers 的中繼資料剖析器 (Flatbuffers 生成的程式碼) 最低版本。這個版本實際上是所有填入欄位版本中最大的版本號碼,以及檔案 ID 指出的最小相容版本。將中繼資料填入 TFLite 模型時,MetadataPopulator會自動填入必要的中繼資料剖析器最低版本。如要進一步瞭解如何使用最低必要的中繼資料剖析器版本,請參閱中繼資料擷取器

讀取模型中的中繼資料

中繼資料擷取器程式庫是便利的工具,可從不同平台上的模型讀取中繼資料和相關聯的檔案 (請參閱 Java 版本C++ 版本)。您可以使用 Flatbuffers 程式庫,以其他語言建構自己的中繼資料擷取工具。

在 Java 中讀取中繼資料

如要在 Android 應用程式中使用 Metadata Extractor 程式庫,建議使用 MavenCentral 託管的 LiteRT Metadata AAR。其中包含 MetadataExtractor 類別,以及 中繼資料結構定義模型結構定義的 FlatBuffers Java 繫結。

您可以在 build.gradle 依附元件中指定此項目,如下所示:

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

如要使用每晚快照,請務必新增 Sonatype 快照存放區

您可以使用指向模型的 ByteBuffer 初始化 MetadataExtractor 物件:

public MetadataExtractor(ByteBuffer buffer);

ByteBuffer 在整個 MetadataExtractor 物件的生命週期內都不得變更。如果模型中繼資料的 Flatbuffers 檔案 ID 與中繼資料剖析器不符,初始化作業可能會失敗。詳情請參閱中繼資料版本管理

有了相符的檔案 ID,中繼資料擷取器就能透過 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;