TensorFlow Object Detection APIでGoogle Cloud MLのモデルサービス(モデルのデプロイ)をするためのsaved_model作成方法を書きます。調査が結構面倒でプログラムまで作成しました。
※Google Cloud MLでなければだめだった方法2 - チェックポイントファイルからエクスポートで大丈夫っぽいです。
関連記事
- TensorFlow Object Detection APIをUbuntuにインストール
- Object Detection APIを使って堀川君を検知
- Access Tokenを取得してGoogle Cloud MLでオンライン予測
- Object Detectionのためのアノテーション(ラベリング)
環境
種類 | バージョン | 内容 |
---|---|---|
OS | Ubuntu18.04.01 LTS | Windows8.1からOracle VirtualBoxを使って動かしています |
Python | 3.5.6 | Python3.5.6をpyenvで動かしています。Google Cloud MLのランタイム1.10を使うのでPythonは3.7を使いません |
TensorFlow | 1.10 | Google Cloud MLのランタイム1.10を使うのでTensorFlowは1.12を使いません |
※後で気になって調べたのですが、2019/1/5時点でもTensorFlow1.12が使えました。日本語の情報だと1.10までしかなかったのですが、英語で見ると1.12も使えました。失敗したー。 |
前提
- pyenvはインストール済
- GCPのアカウントを持っていること
- Google Cloud SDKインストール済
- TensorFlow Object Detection API インストール済
- 訓練済みチェックポイントファイル作成済
※記事「Object Detection APIを使って堀川君を検知」で方法を書いていますが、Google Cloud ML Engineで訓練しています。ローカルPCや他サービスを使っていてもチェックポイントファイルさえあれば大丈夫です。
手順
1. チェックポイントファイル準備
チェックポイントファイルをGoogle Cloud StorageからエクスポートされたモデルをPCにダウンロードします。model.ckpt-XXXXの3ファイルとcheckpointファイルが必要です(他は不要です)。
gsutil cp -r gs://cl-ml01/train_output/model ~/Documents/od/export/checkpoint
2. saved_model出力プログラム作成
"How to deploy an Object Detection Model with TensorFlow serving"という記事を参考にexporter.pyをコピーして変更しています。
ソース全体はGitHubに置いておきます。
改変ポイントを解説していきます。
2.1. 関数write_saved_modelのパラメータ
関数write_saved_modelのパラメータのfrozen_graph_defをtrained_checkpoint_prefixに変えます。
def write_saved_model(saved_model_path,
# Start of change - replace trained_checkpoint_prefix from frozen_graph_def 2019/1/13
#frozen_graph_def,
trained_checkpoint_prefix,
# End of change
inputs,
outputs):
2.2. 関数write_saved_modelのセッションの開始方法
関数write_saved_modelのセッションの開始方法をfrozen_graph_defからtrained_checkpoint_prefixを使ったresotreに変更。
# Start of change - 2019/1/13
# with tf.Graph().as_default():
# with session.Session() as sess:
#
# tf.import_graph_def(frozen_graph_def, name='')
saver = tf.train.Saver()
with session.Session() as sess:
# restore from checkpoint
saver.restore(sess, trained_checkpoint_prefix)
# End of change
2.3. 関数write_saved_modelでのsignature追加
関数write_saved_modelでのsignatureを追加。
builder.add_meta_graph_and_variables(
sess, [tf.saved_model.tag_constants.SERVING],
signature_def_map={
# Start of Insertion - 2019/1/20
'detection_signature':
detection_signature,
# End of insertion
signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY:
detection_signature,
},
2.4. 関数_export_inference_graphでの関数write_saved_model呼出パラメータ変更
1.と対応していますが、関数write_saved_modelの呼出パラメータをfrozen_graph_defからtrained_checkpoint_prefixに変えます。
# Start of change - pass trained_checkpoint_prefix 2019/1/13
# write_saved_model(saved_model_path, frozen_graph_def,
# placeholder_tensor, outputs)
write_saved_model(saved_model_path, trained_checkpoint_prefix,
placeholder_tensor, outputs)
# End of change
2.5. 呼出メイン部分追加
単体で呼び出せるようにメイン部分を追加。ファイルパスはパラメータにできればいいのですが、面倒なのでハードコードしています。使いまわしたい場合は適宜変更してください。
# Start of insertion - add for export 2019/1/18
from object_detection.utils.config_util import create_pipeline_proto_from_configs
from object_detection.utils.config_util import get_configs_from_pipeline_file
def main():
# Configuration for model to be exported
config_pathname = '/home/i348221/Documents/od/input/faster_rcnn_resnet101_coco.config'
latest_filename = 'model.ckpt-1000'
# Input checkpoint for the model to be exported
# Path to the directory which consists of the saved model on disk (see above)
trained_model_dir = '/home/i348221/Documents/od/export/checkpoint'
# Create proto from model confguration
configs = get_configs_from_pipeline_file(config_pathname)
pipeline_proto = create_pipeline_proto_from_configs(configs=configs)
# Read .ckpt and .meta files from model directory
# checkpoint = tf.train.get_checkpoint_state(trained_model_dir, latest_filename)
checkpoint = tf.train.get_checkpoint_state(trained_model_dir)
input_checkpoint = checkpoint.model_checkpoint_path
# Model Version
model_version_id = 1
# Output Directory
output_directory = '/home/i348221/Documents/od/model/export/' + str(model_version_id)
export_inference_graph(input_type='image_tensor', pipeline_config=pipeline_proto,
trained_checkpoint_prefix=input_checkpoint, output_directory=output_directory)
# main関数呼び出し
if __name__ == "__main__":
main()
# End of insertion
3. saved_model出力
作成したプログラムをmodels/research/object_detectionフォルダに置いて実行します。
# From tensorflow/models/research/
$ export PYTHONPATH=$PYTHONPATH:`pwd`:`pwd`/slim
$ python object_detection/exporter_clml.py
これでsaved_modelとvariablesが出来ました!saved_model_cliを使って見てみます。
※saved_model_cliについては、CLI to inspect and execute SavedModel参照
# From ~/Documents/od/model/export/1/saved_model
$ saved_model_cli show --dir ./ --all
etaGraphDef with tag-set: 'serve' contains the following SignatureDefs:
signature_def['detection_signature']:
The given SavedModel SignatureDef contains the following input(s):
inputs['inputs'] tensor_info:
dtype: DT_UINT8
shape: (-1, -1, -1, 3)
name: image_tensor:0
The given SavedModel SignatureDef contains the following output(s):
outputs['detection_boxes'] tensor_info:
dtype: DT_FLOAT
shape: (-1, 300, 4)
name: detection_boxes:0
outputs['detection_classes'] tensor_info:
dtype: DT_FLOAT
shape: (-1, 300)
name: detection_classes:0
outputs['detection_scores'] tensor_info:
dtype: DT_FLOAT
shape: (-1, 300)
name: detection_scores:0
outputs['num_detections'] tensor_info:
dtype: DT_FLOAT
shape: (-1)
name: num_detections:0
Method name is: tensorflow/serving/predict
signature_def['serving_default']:
The given SavedModel SignatureDef contains the following input(s):
inputs['inputs'] tensor_info:
dtype: DT_UINT8
shape: (-1, -1, -1, 3)
name: image_tensor:0
The given SavedModel SignatureDef contains the following output(s):
outputs['detection_boxes'] tensor_info:
dtype: DT_FLOAT
shape: (-1, 300, 4)
name: detection_boxes:0
outputs['detection_classes'] tensor_info:
dtype: DT_FLOAT
shape: (-1, 300)
name: detection_classes:0
outputs['detection_scores'] tensor_info:
dtype: DT_FLOAT
shape: (-1, 300)
name: detection_scores:0
outputs['num_detections'] tensor_info:
dtype: DT_FLOAT
shape: (-1)
name: num_detections:0
Method name is: tensorflow/serving/predict
NG集
よく陥るNG集です。
だめだった方法1 - エクスポートされていたものをそのまま使う
事前に有効化venvの仮想環境を有効化し、環境変数の設定もします。
source cl-od/bin/activate
# From tensorflow/models/research/
export PYTHONPATH=$PYTHONPATH:`pwd`:`pwd`/slim
saved_model_cliを使ってダウンロードしたsaved_modelを見てみます。
$ saved_model_cli show --dir ./ --all
MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs:
signature_def['serving_default']:
The given SavedModel SignatureDef contains the following input(s):
inputs['serialized_example'] tensor_info:
dtype: DT_STRING
shape: ()
name: tf_example:0
The given SavedModel SignatureDef contains the following output(s):
outputs['detection_boxes'] tensor_info:
dtype: DT_FLOAT
shape: (1, 300, 4)
name: detection_boxes:0
outputs['detection_classes'] tensor_info:
dtype: DT_FLOAT
shape: (1, 300)
name: detection_classes:0
outputs['detection_scores'] tensor_info:
dtype: DT_FLOAT
shape: (1, 300)
name: detection_scores:0
outputs['num_detections'] tensor_info:
dtype: DT_FLOAT
shape: (1)
name: num_detections:0
Method name is: tensorflow/serving/predict
signature_def['tensorflow/serving/predict']:
The given SavedModel SignatureDef contains the following input(s):
inputs['serialized_example'] tensor_info:
dtype: DT_STRING
shape: ()
name: tf_example:0
The given SavedModel SignatureDef contains the following output(s):
outputs['detection_boxes'] tensor_info:
dtype: DT_FLOAT
shape: (1, 300, 4)
name: detection_boxes:0
outputs['detection_classes'] tensor_info:
dtype: DT_FLOAT
shape: (1, 300)
name: detection_classes:0
outputs['detection_scores'] tensor_info:
dtype: DT_FLOAT
shape: (1, 300)
name: detection_scores:0
outputs['num_detections'] tensor_info:
dtype: DT_FLOAT
shape: (1)
name: num_detections:0
Method name is: tensorflow/serving/predict
残念ながらこのsaved_modelはGoogle Cloud MLで使えませんでした(筆者の技術力不足かもしれません)。
ブログ記事「Performing prediction with TensorFlow object detection models on Google Cloud Machine Learning Engine」に下記のように書かれていて、shapeがマッチしないのでこのままだとうまくいきません
"Note that the first dimensions of the input and output tensors should be -1 as this is required by the Cloud Machine Learning engine to support batched-inputs."
GitHub issueにも同様の記述があります。どちらも少し古いのですが、状況は変わっていないと思います。
The current object detection example only supports reading one image at a time when doing inference. However, Cloud ML engine prediction service requires the inference graph be able to accept arbitrary number of input instances.The shape of the input tensor(s) must have None as its first dimension.
だめだった方法2 - チェックポイントファイルからエクスポート
チェックポイントのファイルをダウンロードします。
gsutil cp gs://cl-ml01/train_output/model/model.ckpt-1000* ~/Documents/od/model
Exporting a trained model for inferenceに従ってsaved_modelをエクスポート。
INPUT_TYPE=image_tensor
PIPELINE_CONFIG_PATH=~/Documents/od/input/faster_rcnn_resnet101_coco.config
TRAINED_CKPT_PREFIX=~/Documents/od/model/model.ckpt-1000
EXPORT_DIR=~/Documents/od/model
python object_detection/export_inference_graph.py \
--input_type=${INPUT_TYPE} \
--pipeline_config_path=${PIPELINE_CONFIG_PATH} \
--trained_checkpoint_prefix=${TRAINED_CKPT_PREFIX} \
--output_directory=${EXPORT_DIR}
$ saved_model_cli show --dir ./saved_model --all
MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs:
signature_def['serving_default']:
The given SavedModel SignatureDef contains the following input(s):
inputs['inputs'] tensor_info:
dtype: DT_UINT8
shape: (-1, -1, -1, 3)
name: image_tensor:0
The given SavedModel SignatureDef contains the following output(s):
outputs['detection_boxes'] tensor_info:
dtype: DT_FLOAT
shape: (-1, 300, 4)
name: detection_boxes:0
outputs['detection_classes'] tensor_info:
dtype: DT_FLOAT
shape: (-1, 300)
name: detection_classes:0
outputs['detection_scores'] tensor_info:
dtype: DT_FLOAT
shape: (-1, 300)
name: detection_scores:0
outputs['num_detections'] tensor_info:
dtype: DT_FLOAT
shape: (-1)
name: num_detections:0
Method name is: tensorflow/serving/predict
今度はshapeは良さそうなのですが、variablesフォルダが空です。
GitHub Issueに以下のように書かれていて、変数はすべて固定値になってしまっているようです。うーん・・・
This is working as intended. The exporter script converts all variables into constants when exporting the graph.