TensorflowでMNISTの次に学ぶと良さそうなこと(その2)

投稿者: | 2017年9月20日

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を利用してアプリケーション開発が捗る

あとは頑張って動くものを作りたい所存…。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です