2021年1月31日日曜日

MetadataをTensorFlow Lite モデルに追加してTensorFlow Lite Support Task Libraryで推論する

 目的


TensorFlow Lite SupportTFLite Model Metadataを使ってTensorFlow Lite モデルにMetadataを追加する。Metadataを追加したモデルでTensorFlow Lite Support Task Libraryで推論してみる。


前回まで


前回のブログ「Raspberry PiでTensorFlow Lite Support Task Libraryをやってみる。」ではTensorFlow Lite Support Task Library (C++)をつかってRaspberry Pi 4で動くカメラキャプチャのサンプルを作った。

このときに使ったTensorFlow Lite モデル(TF-Lite モデル)はmetadataが追加されたモデルを使用した(TensorFlow Hubから入手したモデル)。

今回はTF-Lite モデルにmetadataを自分で追加して、TensorFlow Lite Support Task Library(TF Lite Support Task Library)で推論することをやってみる。


Metadataとは?


公式の「Adding metadata to TensorFlow Lite models」を参照。
既にあるTF-Lite モデルに補足の情報を追加することができる。


追加できる情報


以下の情報が追加できる。
  • モデル全体の説明(概要、著作権、作成者などの情報)
  • 入力の説明(画像フォーマット、正規化、入力幅の情報)
  • 出力の説明とラベル(説明やラベルとのマッピング)

追加した情報は「Netron」や「AndroidStudio」で参照できる。


Metaデータを追加する利点


  • 配布するモデルファイルの作成者、著作権を明示できる。
    TF-Lite モデルは端末にダウンロードが前提。
    公開・非公開に関わらず情報があるとありがたい。
  • TF Lite Support Task Libraryを使うと入力、出力情報から必要な前・後処理を行ってくれる。
    • 前処理: リサイズ、正規化、モデルのInput(Float, Int)
    • 後処理: ラベルとのマッピング
  • 推論コードのラッパーを自動生成してくれる。
    TensorFlowのドキュメント「メタデータを使用してモデルインターフェイスを生成する
    (まだよく理解していないので後で試す)


Medadataを追加するには?


TF Lite SupportのAPI(Python)を利用する。
Pythonのpipパッケージはtflite-supportが利用できる。


Object detectionモデルにmetadataを組み込む


TensorFlow 1 Detection Model Zooのpre-trainedモデル「ssd_mobiledet_cpu_coco」にMetadataを追加する。
このモデルはFloatモデルで、InputもFloat32である。

なお、Metadataを追加しない状態でTF Lite Support Task Libraryで推論するとエラーとなってしまう。
これは、TF Lite Support Task LibraryがInputの正規化するための情報が無いため。

$ ./bazel-bin/tensorflow_lite_support/examples/task/vision/pi/object_detector_capture \
    --model_path=/home/pi/ssdlite_mobiledet_cpu.tflite \
    --num_thread=4 --score_threshold=0.5
Detection failed: Input tensor has type kTfLiteFloat32: it requires specifying NormalizationOptions metadata to preprocess input images.

モデルにmetadataを追加して推論ができることまで確認する。


参考のコード・サンプル


Metadetaを追加するライブラリはここで実装。

MetadataはFlatBuffersのスキーマで定義。

よく使われるモデルのラッパーの定義。
(Image classification、Object detection、Image segmentation)
使い方はドキュメントはテストコードを参照。


作成したサンプル


Object detectionモデルにmetadataを書き込むサンプルを作成した。

前回のtflite-supportのforkリポジトリに追加。



実装


Pythonでの実装の概要を説明。

必要なimport
from tensorflow_lite_support.metadata import metadata_schema_py_generated as _metadata_fb
from tensorflow_lite_support.metadata.python.metadata_writers import metadata_info
from tensorflow_lite_support.metadata.python.metadata_writers import object_detector
from tensorflow_lite_support.metadata.python.metadata_writers import writer_utils

サンプルではMetadataPopulatorForObjectDetectorクラスの_create_metadataメソッドでmetadataを生成。

metadata_info.GeneralMdでモデル全体の情報を生成。
    # Creates model info.
    self.general_md = metadata_info.GeneralMd(
      name=self.model_info.name,
      version=self.model_info.version,
      description=("Identify which of a known set of objects might be present "
                   "and provide information about their positions within the "
                   "given image or a video stream."),
      author="Test",
      licenses=("Apache License. Version 2.0 "
                 "http://www.apache.org/licenses/LICENSE-2.0.")
    )

metadata_info.InputImageTensorMdでモデルの入力を生成。
  • norm_mean、norm_stdで正規化のパラメータを指定。
  • tensor_typeは入力モデルから取得(writer_utils.get_input_tensor_types)。
  • モデルはtensorflow.python.platform.resource_loaderを使って読み込み。
    # Load model to buffer.
    self.model_buffer = self._load_file(self.model_file_path)

    # Creates input info.
    self.input_md = metadata_info.InputImageTensorMd(
      name="normalized_input_image_tensor",
      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 {2} and {3}.".format(
                     self.model_info.image_width, self.model_info.image_height,
                     self.model_info.image_min, self.model_info.image_max)),
        norm_mean=self.model_info.mean,
        norm_std=self.model_info.std,
        color_space_type=_metadata_fb.ColorSpaceType.RGB,
        tensor_type=writer_utils.get_input_tensor_types(self.model_buffer)[0])

metadata_info.CategoryTensorMdでモデルの出力を生成。
  • ラベルファイルのパスを渡してあげると、ラベルも一緒に追加してくれる。
    # Creates output info.
    self.output_category_md = metadata_info.CategoryTensorMd(
        name="category",
        description="The categories of the detected boxes.",
        label_files=[
            metadata_info.LabelFileMd(file_path=file_path)
            for file_path in self.label_file_path
        ])

object_detector.MetadataWriter.create_from_metadata_infoで生成したMetadataをモデルに追加する。
戻り値のMetadataWriterのpopulateメソッドを呼び出すことでMetadataを追加したモデルを得ることができる。あとはバイナリファイルに書き込むだけ。
    self.writer = object_detector.MetadataWriter.create_from_metadata_info(
        model_buffer=self.model_buffer, general_md=self.general_md,
        input_md=self.input_md, output_category_md=self.output_category_md)
    model_with_metadata = self.writer.populate()

    with open(self.export_model_path, "wb") as f:
      f.write(model_with_metadata)

ラッパーを使っているので簡単にmetadataを追加できる。

また、MetadataWriterのget_metadata_jsonメソッドで追加したMetadataをJSONフォーマットで得ることができる。
  def get_metadata_json(self):
    return self.writer.get_metadata_json()


環境の準備


ホストPCでMetadataを書き込む(Windows、LinuxどちらもOK)。
今回はWindowsで実施。

TF Lite Support Task LibraryのPythonパッケージは0.1.0が公開されている(2021.01.30時点)。
しかし、metadata_writersのラッパーは0.1.0には含まれていないため、tflite-support-nightlyを使う。
TensorFlowはCPUのみで問題ない。

$ pip install tflite-support-nightly
$ pip install tensorflow


Metadataを組み込むモデル、ラベルファイル


モデル


ssd_mobiledet_cpu_coco」をダウンロードして目的のTF-Liteモデルを得る。
model.tfliteをssdlite_mobiledet_cpu.tfliteにリネームして使用。
$ wget http://download.tensorflow.org/models/object_detection/ssdlite_mobiledet_cpu_320x320_coco_2020_05_19.tar.gz
$ tar xf ssdlite_mobiledet_cpu_320x320_coco_2020_05_19.tar.gz
$ mv ssdlite_mobiledet_cpu_320x320_coco_2020_05_19/model.tflite ssdlite_mobiledet_cpu.tflite

ラベル


coco datasetsのラベルを記載したファイル(labelmap.txt)を用意する。
今回はTF Lite Supportのテストデータを使用する。


Metadataを組み込む


リポジトリをclone後、サンプルのスクリプトを実行する。
引数は追加するモデル、ラベルファイル、出力ディレクトリ。
$ git clone https://github.com/NobuoTsukamoto/tflite-support.git
$ cd tensorflow_lite_support\examples\metadata
$ mkdir model_with_metadata
$ python metadata_writer_for_object_detection.py \
    --model_file=PATH_TO\ssdlite_mobiledet_cpu.tflite \
    --label_file=PATH_TO\labelmap.txt \
    --export_directory=.\model_with_metadata

model_with_metadataディレクトリに以下が生成される。
  • ssdlite_mobiledet_cpu.tflite 👈 metadataを追加したモデル
  • ssdlite_mobiledet_cpu.json 👈 追加したmetadataのJSONファイル


Metadataを確認


Netronと生成したJSONファイルを確認。

Netron


Input(normalized_input_image_tensor)を選択すると追加したmetadataの情報が表示される。
Metadataを追加したモデル

追加前と比べるとよくわかる。
Metadataを追加する前のオリジナルのモデル



JSON


出力したmetadataのJSON。
Netronでは表示できない内容も確認できる。
{
  "name": "SSDLite with MobileDet-CPU",
  "description": "Identify which of a known set of objects might be present and provide information about their positions within the given image or a video stream.",
  "version": "v1",
  "subgraph_metadata": [
    {
      "input_tensor_metadata": [
        {
          "name": "normalized_input_image_tensor",
          "description": "Input image to be classified. The expected image is 320 x 320, with three channels (red, blue, and green) per pixel. Each value in the tensor is a single byte between 0 and 255.",
          "content": {
            "content_properties_type": "ImageProperties",
            "content_properties": {
              "color_space": "RGB"
            }
          },
          "process_units": [
            {
              "options_type": "NormalizationOptions",
              "options": {
                "mean": [
                  127.5
                ],
                "std": [
                  127.5
                ]
              }
            }
          ],
          "stats": {
            "max": [
              1.0
            ],
            "min": [
              -1.0
            ]
          }
        }
      ],
      "output_tensor_metadata": [
        {
          "name": "location",
          "description": "The locations of the detected boxes.",
          "content": {
            "content_properties_type": "BoundingBoxProperties",
            "content_properties": {
              "index": [
                1,
                0,
                3,
                2
              ],
              "type": "BOUNDARIES"
            },
            "range": {
              "min": 2,
              "max": 2
            }
          },
          "stats": {
          }
        },
        {
          "name": "category",
          "description": "The categories of the detected boxes.",
          "content": {
            "content_properties_type": "FeatureProperties",
            "content_properties": {
            },
            "range": {
              "min": 2,
              "max": 2
            }
          },
          "stats": {
          },
          "associated_files": [
            {
              "name": "labelmap.txt",
              "description": "Labels for categories that the model can recognize.",
              "type": "TENSOR_VALUE_LABELS"
            }
          ]
        },
        {
          "name": "score",
          "description": "The scores of the detected boxes.",
          "content": {
            "content_properties_type": "FeatureProperties",
            "content_properties": {
            },
            "range": {
              "min": 2,
              "max": 2
            }
          },
          "stats": {
          }
        },
        {
          "name": "number of detections",
          "description": "The number of the detected boxes.",
          "content": {
            "content_properties_type": "FeatureProperties",
            "content_properties": {
            }
          },
          "stats": {
          }
        }
      ],
      "output_tensor_groups": [
        {
          "name": "detection_result",
          "tensor_names": [
            "location",
            "category",
            "score"
          ]
        }
      ]
    }
  ],
  "author": "Test",
  "license": "Apache License. Version 2.0 http://www.apache.org/licenses/LICENSE-2.0."
}

Metadataを組み込んだモデルを使ってみる


前回のブログで使ったサンプルプログラム(Object Detector)を使って推論してみる。
モデルをRaspberry Pi 4に転送して推論する。
$ ./bazel-bin/tensorflow_lite_support/examples/task/vision/pi/object_detector_capture \
    --model_path=/home/pi/ssdlite_mobiledet_cpu.tflite \
    --num_thread=4 --score_threshold=0.5
Detection failed: Input tensor has type kTfLiteFloat32: it requires specifying NormalizationOptions metadata to preprocess input images.

今度はエラーがなく、推論できる👍


感想


推論のコードが変更せずに利用できることはとても良いことだと思う。
(ただ、個人で使う場合、metadataを追加するコストを考えるとあまりメリットがない気も、、、Input, Outputを統一してしまえばいいし、、、)
次は「メタデータを使用してモデルインターフェイスを生成する」を使ってAndroidアプリを試してみよう。

0 件のコメント:

コメントを投稿