Tensorflow の savedmodel の使い方
前回の続きです。
Estimatorを使えば半自動で学習などをTensorflowがやってくれる環境を構築できます。
が、学習結果を利用してアプリなどを作成する方法は提供してくれません。
savedmodelとtensorflow servingを利用することで、学習結果からアプリを手軽に提供できるようになります。
savedmodel の作成方法
saved modelはtensorflowの学習結果保存したものです。
Estimatorのsavedmodelメソッドを呼べば生成してくれます。
ただし、モデルに対する入力、出力の形式をtf.feature_column
などを利用して定義してあげる必要があります。
また、EstimatorSpecの定義も若干これに合わせて修正が必要となります。
Estimatorへexport_outputsを追加
実際に追加したEstimatorのmodel_fnの例を示します。
# 入力の形式を指定
inputs = [tf.feature_column.numeric_column('x',shape=4)]
## estimator のmodel_fnを作成
def model_fn(features, labels, mode, params):
# 入力層を追加
input_layer = tf.feature_column.input_layer(features, inputs)
hidden1 = tf.layers.dense(inputs=input_layer, units=10, activation=tf.nn.relu)
hidden2 = tf.layers.dense(inputs=hidden1,units=20, activation=tf.nn.relu)
hidden3 = tf.layers.dense(inputs=hidden2, units=10, activation=tf.nn.relu)
y = tf.layers.dense(inputs=hidden3, units=3, activation=tf.nn.softmax)
loss = None
train_op = None
if mode != tf.estimator.ModeKeys.PREDICT:
y_ = tf.one_hot(labels, depth=3)
loss = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))
train_op = tf.train.GradientDescentOptimizer(0.01).minimize(loss,
global_step=tf.train.get_global_step())
predictions = y
# 出力形式のexport_outputs をここで追加
export_outputs = {"classes": tf.estimator.export.PredictOutput({"labels": y})}
# EstimatorSpec生成の際の引数にexport_outputsを追加
spec = tf.estimator.EstimatorSpec(mode=mode,
predictions=predictions,
loss=loss,
train_op=train_op,
export_outputs=export_outputs)
return spec
savedmodelの実行に必要な関数を定義
saved modelの実行には保持している入力データの形式と与えられた入力データを読取る関数が必要です。
この辺はお作法だと思うので、この辺りを参考にしつつ定義します。
feature_spec = tf.feature_column.make_parse_example_spec(inputs)
export_input_fn = tf.estimator.export.build_parsing_serving_input_receiver_fn(feature_spec)
savedmodelを実行
ここまでで、savedmodelを生成する準備が整ったのでsavedmodel
メソッドを実行してみます。
nn.export_savedmodel("./sample", export_input_fn)
指定したディレクトリに色々データが保存されていると思います。
Tensorflow Serving の使い方
ここまででsavedmodelが生成されたので、それを使って簡単なアプリを作ってみます。
Tensorflow servingとは
公式の説明が一番良いと思います。
tensorflow serving自体のアーキテクチャなど色々興味深いのですが、とりあえず以下のようなものだと思えば良いです。
- Tensorflow servingにはサーバとクライアントがある
- 通信はgrpcが取り持つ
- サーバはsaved modelを利用して予測などが出来る
- クライアントからはデータを送信してサーバ側に予測をさせることが出来る
実際の運用としては、サーバを自前で開発することは無い & クライアントもAPIがあるのでそれを呼ぶだけなので
手軽にTensorflowを使ったアプリが開発できる基盤となっています。
Tensorflow Servingのインストール
公式を参考にインストールします。
tensorflowのコンパイルをするようなフローになっていますが時間がかかるので適当な仮想マシンにubuntuでapt-get
でインストールするのが手軽です。
サーバの起動
まずはサーバを起動してみます。apt-get
でインストールした場合は、以下のコマンドで起動できます。
$ tensorflow_model_server --model_base_path=/home/ubuntu/model-server/sample/ --model_name="sample"
--model_base_path
にはsavedmodel
のあるディレクトリを指定して、--model_name
にはモデルの適当な名前を指定して下さい。
サーバはデフォルトだと8500番ポートを利用するようです。
実行時のログは以下のような感じです。
tensorflow_model_server --model_base_path=/home/ubuntu/sample --model_name=sample
2017-09-20 02:40:11.807587: I tensorflow_serving/model_servers/main.cc:147] Building single TensorFlow model file config: model_name: sample model_base_path: /home/ubuntu/sample
2017-09-20 02:40:11.809979: I tensorflow_serving/model_servers/server_core.cc:434] Adding/updating models.
2017-09-20 02:40:11.810004: I tensorflow_serving/model_servers/server_core.cc:485] (Re-)adding model: sample
2017-09-20 02:40:11.911332: I tensorflow_serving/core/basic_manager.cc:705] Successfully reserved resources to load servable {name: sample version: 1505554994}
2017-09-20 02:40:11.911376: I tensorflow_serving/core/loader_harness.cc:66] Approving load for servable version {name: sample version: 1505554994}
2017-09-20 02:40:11.911401: I tensorflow_serving/core/loader_harness.cc:74] Loading servable version {name: sample version: 1505554994}
2017-09-20 02:40:11.911439: I external/org_tensorflow/tensorflow/contrib/session_bundle/bundle_shim.cc:360] Attempting to load native SavedModelBundle in bundle-shim from: /home/ubuntu/sample/1505554994
2017-09-20 02:40:11.911467: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:236] Loading SavedModel from: /home/ubuntu/sample/1505554994
2017-09-20 02:40:11.939715: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:155] Restoring SavedModel bundle.
2017-09-20 02:40:11.955666: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:190] Running LegacyInitOp on SavedModel bundle.
2017-09-20 02:40:11.956652: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:284] Loading SavedModel: success. Took 45177 microseconds.
2017-09-20 02:40:11.956856: I tensorflow_serving/core/loader_harness.cc:86] Successfully loaded servable version {name: sample version: 1505554994}
2017-09-20 02:40:11.959690: I tensorflow_serving/model_servers/main.cc:288] Running ModelServer at 0.0.0.0:8500 ...
クライアントの実装
それではクライアントをpythonで実装してみます。
ライブラリはpip install tensorflow-serving-api
でインストールできます。
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import numpy as np
import tensorflow as tf
from grpc.beta import implementations
from tensorflow_serving.apis import predict_pb2
from tensorflow_serving.apis import prediction_service_pb2
# サーバのアドレスとポートの指定
tf.app.flags.DEFINE_string('server', '127.0.0.1:8500', 'Server host:port.')
# サーバの起動時に指定したモデルの名前(model_name)
tf.app.flags.DEFINE_string('model', 'sample', 'Model name.')
FLAGS = tf.app.flags.FLAGS
def _float_feature(value):
return tf.train.Feature(float_list=tf.train.FloatList(value=[value]))
def _bytes_feature(value):
return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))
def main(_):
host, port = FLAGS.server.split(':')
channel = implementations.insecure_channel(host, int(port))
stub = prediction_service_pb2.beta_create_PredictionService_stub(channel)
request = predict_pb2.PredictRequest()
request.model_spec.name = FLAGS.model
request.model_spec.signature_name = 'serving_default'
# 予測して欲しいデータを準備
feature_dict = {"x": tf.train.Feature(float_list=tf.train.FloatList(value=[2.0,2,5,4]))}
example = tf.train.Example(features=tf.train.Features(feature=feature_dict))
serialized = example.SerializeToString()
request.inputs['examples'].CopyFrom(tf.contrib.util.make_tensor_proto(serialized, shape=[1]))
# サーバのAPIを呼び出す
label = 0
result_future = stub.Predict.future(request, 5.0)
# 結果を確認
prediction = result_future.result().outputs['labels']
print(prediction)
if __name__ == '__main__':
tf.app.run()
それでは、実行してみます。
python client.py
dtype: DT_FLOAT
tensor_shape {
dim {
size: 1
}
dim {
size: 3
}
}
float_val: 1.38398981164e-09
float_val: 8.47830669954e-05
float_val: 0.999915242195
おぉー。なんかちゃんと予測出来てそうな結果を得ることが出来ました。
あとは、作りたいアプリによって結果を整形して表示すれば簡単なアプリは実装できそうですね。
注意点
tensorflow servingが入力として読む場合の辞書のkeyがどうもデータによって変わったりするようです。
なので、savedmodelを作成するときに、as_text=True
を指定してデータを一度生成して中身を眺めてみると
上手く動作しない場合にデバッグの手掛かりになるかと思います。
nn.export_savedmodel("./sample", export_input_fn, as_text=True)
全体的なまとめ
- Estimatorを積極的に利用すると大分手軽にtensorflowで遊べる
- saved modelを利用すれば学習データの可搬性が高まる
- saved modelを作るには入出力の形式をEstimatorSpecで指定する必要がある
- savedmodelの実行には入力の形式と入力データの処理方法を指定する関数を指定する必要がある
- tensorflow servingを使うとsavedmodelを利用してアプリケーション開発が捗る
あとは頑張って動くものを作りたい所存…。