最近書いた以下の記事に登場する「TFJS Task API(TensorFlow Lite のモデルを JavaScript で扱える仕組み)」による画像分類の処理を行っていた際に、Teachable Machine や Lobe の TensorFlow Lite用のモデルエクスポートを組み合わせようとして、その時にすんなり進められなかったのがあり、この記事を書きました。
- TensorFlow Lite のモデルを Web で扱えるという話についてのざっくりなメモ【Google I/O 2021】 - Qiita
- 30行未満の HTML+JavaScript でカスタムモデルを使った画像分類を試す(TFJS Task API を利用)【Google I/O 2021】 - Qiita
- TensorFlow Hub の TensorFlow Lite用モデルを HTML+JavaScript から使って画像分類(TFJS Task API を利用) - Qiita
要点から書くと、問題が出た原因はメタデータの扱いというか、出力ファイルにメタデータがどのような形で含まれるか、という話です。
(ちなみに、TFJS Task API を利用しない方向でやるなら、TensorFlow.js向けの機械学習モデルをエクスポートするやり方をとることで、すんなり進められます)
TensorFlow Lite用の機械学習モデルのファイル構成
Teachable Machine の画像プロジェクトの場合
以下の記事にも書いたのですが、Teachable Machine の画像プロジェクト(画像分類を行うための仕組み)で TensorFlow Lite用のモデルをダウンロードすると、「model.tflite(または、model_unquant.tflite)」と「labels.txt」の 2つのファイルを得ることができます。
●Teachable Machine の機械学習モデル出力に関するメモ 〜画像プロジェクト編〜【2021年5月版】 - Qiita
https://qiita.com/youtoy/items/685d97e0acc5cfc184c0
画像分類で、分類したいクラスをいくつか設定するステップがありますが、その設定をしたクラスの名称の情報は「labels.txt」に書かれて出てきます。
TFJS Task API で .tflite のファイルを直接使った場合の問題
TFJS Task API のデフォルト設定では、.tflite のファイル内にクラスの情報も書かれている前提となっているようで、Teachable Machine の画像プロジェクトで出力した TensorFlow Lite用のモデルについて、.tflite のファイルを TFJS Task API でそのまま使うと、以下のように推論結果での ClassName が空になるということが起こります。
Teachable Machine の TensorFlow Lite用モデル、
— you (@youtoy) July 3, 2021
TFJS Task API でそのまま実行できた!
...けど、「labels.txt」として別に分かれているラベルの情報というかクラス名の情報が抜けてる。
Lobe はメタデータ用データ一式が抜け落ちた形だったと思われるけど、こちらはクラス名のみなんとかすれば... https://t.co/uvaAurEeGg pic.twitter.com/IRwcSCThaE
Lobe の場合
Lobe で TensorFlow Lite用のモデルをエクスポートした場合、「saved_model.tflite」・「labels.txt」と「signature.json」の 3つを、関連するファイルとして得られます。
Teachable Machine の画像プロジェクトの時と比べると、「signature.json」というファイルが 1つ多いです。
中を見てみると、以下のような内容が書かれたファイルです。
機械学習モデルでのパラメータ的なものやその他の情報が書かれていそうな感じです。
TFJS Task API で .tflite のファイルを直接使った場合の問題
Lobe からのエクスポートで得られた 3つのファイルのうち、.tflite のファイルを TFJS Task API で使おうとすると、以下のようにエラーが発生します。
なんとなくは予想していたのだけど(Lobe が出力するファイルのセットと、TFJS Task API で呼び出しているモデルの内容的に)、
— you (@youtoy) July 3, 2021
やはりエラーが出たw pic.twitter.com/MmKVSr6Jj3
この結果を、Teachable Machine の画像プロジェクトの時の結果と比べて推測すると、おそらく Teachable Machine の .tflite のファイルでは Lobe の signature.json に書かれていた情報が .tflite のファイル内に含まれていたのではないかと思われます。
TensorFlow Lite のモデルとメタデータ
TensorFlow Lite用のモデルのメタデータに関するツール
上記の問題やエラーの解決方法について調べてみたところ、以下のページに関係する情報がありそうでした。
●TensorFlowLiteモデルへのメタデータの追加 | TensorFlow Lite
https://www.tensorflow.org/lite/convert/metadata
例えばファイルのフォーマット関連で、以下の内容が正に関連していそうな部分のうちの 1つです。
そして、クラス名が書かれた「labels.txt」が「.tflite のファイル」に含まれる場合の構成は、以下のように書かれています。
「labels.txt」を含むモデルを ZIPファイルとして解凍してみる
上記の labels.txt に関する話をここで確かめてみようと思います。
手軽にできそうなやり方の 1つとして、TensorFlow Hub から labels.txt を含んだモデルをダウンロードしてきて解凍処理を行ってみる、というのが良さそうです。
そこで、実際に以下の画像で示した内容のモデルを使って試していきます。
このモデルの出力形式がいくつかある中で、以下からファイルをダウンロードすると「lite-model_imagenet_mobilenet_v3_large_100_224_classification_5_metadata_1.tflite」というファイルが得られます。
そのファイルについて、Mac だと unzip 【対象ファイル】
というコマンドを実行することで解凍を実行できます。
実際にそのコマンドを実行した結果は以下の通りです。
$ unzip lite-model_imagenet_mobilenet_v3_large_100_224_classification_5_metadata_1.tflite
Archive: lite-model_imagenet_mobilenet_v3_large_100_224_classification_5_metadata_1.tflite
extracting: labels.txt
labels.txt が抽出されるのを確認できました。
ちなみに、「TFLite (v5, metadata)」ではなく「TFLite (v5, default)」のほうのタブを選び、そこからファイルをダウンロードして unzip を実行すると、以下のようになります。
$ unzip lite-model_imagenet_mobilenet_v3_large_100_224_classification_5_default_1.tflite
Archive: lite-model_imagenet_mobilenet_v3_large_100_224_classification_5_default_1.tflite
End-of-central-directory signature not found. Either this file is not
a zipfile, or it constitutes one disk of a multi-part archive. In the
latter case the central directory and zipfile comment will be found on
the last disk(s) of this archive.
unzip: cannot find zipfile directory in one of lite-model_imagenet_mobilenet_v3_large_100_224_classification_5_default_1.tflite or
lite-model_imagenet_mobilenet_v3_large_100_224_classification_5_default_1.tflite.zip, and cannot find lite-model_imagenet_mobilenet_v3_large_100_224_classification_5_default_1.tflite.ZIP, period.
こちらは、「labels.txt」を含んでないファイルだと思われるので、ファイルフォーマット的に ZIP の形をとっていなくて上記のエラーがでる、という状況だと思われます。
.tflite のファイル と label.txt を統合する
上で書いていた話のうち、Teachable Machine のほうは、比較的簡単に対応ができます。
以下の「TensorFlowLiteメタデータライターAPI」を使うと、.tflite のファイル と label.txt を統合することができます。
●TensorFlowLiteメタデータライターAPI | TensorFlow Lite
https://www.tensorflow.org/lite/convert/metadata_writer_tutorial#image_classifiers
統合する手順
TensorFlowLiteメタデータライターAPI のページに書かれている「TensorFlow Lite SupportPypiパッケージのインストール」を行います。
$ pip install tflite-support-nightly
そして、TensorFlowLiteメタデータライターAPI のページに書かれているのと同じような Python のプログラムを作成して実行します。
from tflite_support.metadata_writers import image_classifier
from tflite_support.metadata_writers import writer_utils
ImageClassifierWriter = image_classifier.MetadataWriter
_MODEL_PATH = "model_unquant.tflite"
_LABEL_FILE = "labels.txt"
_SAVE_TO_PATH = "model_unquant_metadata.tflite"
_INPUT_NORM_MEAN = 127.5
_INPUT_NORM_STD = 127.5
writer = ImageClassifierWriter.create_for_inference(
writer_utils.load_file(_MODEL_PATH), [_INPUT_NORM_MEAN], [_INPUT_NORM_STD],
[_LABEL_FILE])
print(writer.get_metadata_json())
writer_utils.save_file(writer.populate(), _SAVE_TO_PATH)
そうすると、以下のように TFJS Task API での画像分類(推論)を実行した際に、ClassName が表示されるようになりました。
(以下の例は、学習させた画像と推論の対象の画像もマッチしていないし、label.txt に含まれた数字の情報がそのまま統合されていて、イマイチな感じになっているのはありますが...)
#TeachableMachine で #TensorFlowLite 用に出力した機械学習モデルのデータ、
— you (@youtoy) July 3, 2021
TensorFlowLiteメタデータライターAPI を使って labels.txt の中身を .tflite ファイルにメタデータとして付与。
無事、そのモデルと TFJS Task API を使って出した処理結果に、クラス名も一緒に表示される形になった! https://t.co/RM6k9wgu6U pic.twitter.com/4cbFHH4LCp
Lobe の事例のほうは「signature.json」の中身も統合する必要がありそうで、上でも記載していた「TensorFlowLiteモデルへのメタデータの追加」のページに書いてあることを利用すれば良さそうなのかもしれないですが、詳細は調査できてない状況です。
終わりに
Teachable Machine と Lobe で TensorFlow Lite用のモデルをエクスポートし、TFJS Task API で使おうとしたらすんなりとは進められなかった...、という状況から TensorFlow Lite用の機械学習モデルのファイルの構成について調査したりしてみました。
出力した機械学習モデル関連のファイルが、単体・2つのファイル・3つのファイルのパターンがあり、それらを変換する方法を調べて試そうとしましたが、2つのファイルを 1つに統合するというところは実現できました(TensorFlowLiteメタデータライターAPI を利用)。
3つのファイルに分かれているパターン(Lobe の出力するファイル)を 1つのファイルに統合するのは、まだ調査が必要な状況です。
TensorFlow Lite用モデルを HTML+JavaScript から使って画像分類などを簡単に行える TFJS Task API は、いろいろ活用してみたいところなので、今回未解決の部分も引き続き調査してみようと思います。