Goalist Developers Blog

kerasのモデルをデプロイする手順

こんちは、ゴーリストのチナパです!

機械学習を触ったことがあるみなさんはkerasをご存知かと思います。初級から上級の方にもとても使いやすい、フレキシブルなライブラリです。学習の時に

model.save(file_name) #.h5ファイルで保存
model = keras.models.load_model(file_name) #ファイルを読み込み、モデルをロードする

のようにに楽に保存したり、保存されたファイルをロードしたりできます。

ただし、このまま本番環境に持って行こうとしたら非常に勿体無いです。なぜなら、この保存のやり方は学習中のモデルのためですので、本番環境にはいらない情報がたくさんあります。

これから、本番環境用のTenserflowのSavedModelをつくる方法を説明いたします。今回はtensorflow 1.13でやっています、tensorflow 2.0でまたちょっと変わりますので、気をつけてください。

f:id:c-pattamada:20190514144554j:plain
要注意:本番環境に写す前に、あなたのAIは人類の敗北を望んでいないことを確認しましょう: Photo by Matan Segev from Pexels

下準備

まずは、今回は学習ではないなく、本番のつもりですをkerasくんに教えましょう。(色々心の準備あるからでしょう)

import keras.backend as K
import tensorflow as tf
from keras.models import load_model

K.set_learning_phase(0) # 「もう学習していませんよ!」をkerasで書くとこう

これで、Dropoutレイヤーとレギュラライザーなどの、学習専用の物が無視されます。

すでにmodel.save()で保存されたモデルがありましたらそのファイルのパスを用意しまて、モデルをロードしましょう(ない方はしたの部分を見てください)

file_name = ... #あなたのモデルのパス
model = load_model(file_name)

そのようなファイルはすでに持っていない方は以下のようにモデルを作成してましたら、チュートリアルをやってみれます。
from keras.applications.resnet50 import ResNet50
model = ResNet50(weights='imagenet')

では、準備できてますね。

Signature作成

input_sig = 'my_input'
output_sig = 'predictions'
signature = predict_signature_def(inputs={input_sig: new_model.input}, outputs={output_sig: new_model.output})

簡単そうに見えますが、一体何をしているでしょう?

本番環境のSavedModelにはグラフのどのノードがinputとして使ったほうが良いのか、結果を見る時にはどこにみれば良いのかを定義するためのオブジェクトです。 今回は⓵predict_signature_def()を使っていますが、他に ⓶classification_signature_def()と⓷regression_signature_def()もあります。 違いは何なのかというと、inputのアウトプットの形に応じて使い分けられる物です。ここで定義したinput_sigとoutput_sigは後でまた使います。

(ちなみに、⓶はクラッシフィケーション専用のメソッドです、例えば、画像に対してのラベルを出したい場合などには、このメソッドで文字列のラベルまで出してくれたり便利な部分があります。⓷は同じようにregression系の機械学習に便利です。⓵の方は割と応用時なので、こちらに使います)。

モデル保存

まずは、export_path、つまり保存先のディレクトリ名を定義します。これはすでに存在しましたらエラーが発生するので、最後のフォルダーがまだ存在しないようにします(ここでは”1”となってます)。

export_path = /path/to/model/1/  #例のパスです、最後のフォルダがまだ存在しないフォルダー名(例えば、モデルのバージョン番号)を使ってください
signature_name = 'predict_output' # こちらもまた後で使います。
with K.get_session() as sess:
    builder = tf.saved_model.builder.SavedModelBuilder(export_path)
    builder.add_meta_graph_and_variables(
        sess, [tf.saved_model.tag_constants.SERVING],
        signature_def_map={signature_name: signature},
        main_op=tf.tables_initializer())
    builder.save()

そして、tensorflowのSavedModelBuilderを使って保存します。 本番環境のつもりなのでtf.saved_model.tag_constants.SERVINGのフラグを設定します。

先ほど作成したsignatureがsignature_def_map={'predict_output': signature}このように設定します。こちらの'predict_output’の文字列は自由です。

実行しましたら

f:id:c-pattamada:20190514143120p:plain
SavedModelの保存形式
のように保存されるはずです。

イエイ!これで保存できました!

グーグルのAI platformを使いたい場合にもこのような保存形式であれば大丈夫です。

モデルをロードする

さて、モデルを保存しましたが、この形式をどうやって使えるでしょうか? 自作のFlaskサーバーで利用したい場合には便利かも知れません。

いかのようにロードできますが、今までのpredictなどの昨日がうまくいきません。

with tf.Session(graph=tf.Graph()) as sess:
    model_x = tf.saved_model.loader.load(sess, [tag_constants.SERVING], export_path)

そこで、上記に定義したsignature_defを利用します。 まとめて、

my_testing_data = ... #ここでモデルのインプットデータを用意します

with tf.Session(graph=tf.Graph()) as sess:
    model_x = tf.saved_model.loader.load(sess, [tag_constants.SERVING], export_path)
    sig_def = model_x.signature_def[signature_name]
    in_name = sig_def.inputs[input_sig].name
    out_name = sig_def.outputs[output_sig].name
    # print(in_name, out_name) #きになる方はここでプリントしたら、レイヤー名が出力されます。
    result = sess.run(out_name,
                 feed_dict={in_name: my_testing_data})
    

こうして、Signature defを利用して、モデルの中のレイヤー名に依存せずにモデルの利用ができます。これは本番環境にモデル置き換えなどがある場合にはとても便利です。

まとめ

今回はkerasのモデルを本番環境のために準備するステップを説明してみました。モデルのsignatureを作成して、保存し、またロードして実行するまでやりました。ただし、tensorflow 2.0にはまたちょっと変わっていきますのでご注意ください。

本番環境を用意しましたら、cliで実行するか、flaskのサーバーに置くか、他の色々な選択肢もありますが今回はここまでにします!