TensorFlow 模型如何對外提供服務

TensorFlow 是目前最爲流行的機器學習框架之一,通過它我們可以便捷地構建機器學習模型。使用 TensorFlow 模型對外提供服務有若干種方式,本文將介紹如何使用 SavedModel 機制來編寫模型預測接口。

鳶尾花深層神經網絡分類器

首先讓我們使用 TensorFlow 的深層神經網絡模型來構建一個鳶尾花的分類器。完整的教程可以在 TensorFlow 的官方文檔中查看(Premade Estimators),我也提供了一份示例代碼,託管在 GitHub 上(iris_dnn.py),讀者可以克隆到本地進行測試。以下是部分代碼摘要:

feature_columns = [tf.feature_column.numeric_column(key=key)
                   for key in train_x.keys()]
classifier = tf.estimator.DNNClassifier(
    feature_columns=feature_columns,
    hidden_units=[10, 10],
    n_classes=3)

classifier.train(
    input_fn=lambda: train_input_fn(train_x, train_y, batch_size=BATCH_SIZE),
    steps=STEPS)

predictions = classifier.predict(
    input_fn=lambda: eval_input_fn(predict_x, labels=None, batch_size=BATCH_SIZE))
將模型導出爲 SavedModel 格式

TensorFlow 提供了 SavedModel 機制,用以將訓練好的模型導出爲外部文件,供後續使用或對外提供服務。Estimator 類的 export_savedmodel 方法接收兩個參數:導出目錄和數據接收函數。該函數定義了導出的模型將會對何種格式的參數予以響應。通常,我們會使用 TensorFlow 的 Example 類型來表示樣本和特徵。例如,鳶尾花樣本可以用如下形式表示:

Example(
    features=Features(
        feature={
            'SepalLength': Feature(float_list=FloatList(value=[5.1])),
            'SepalWidth': Feature(float_list=FloatList(value=[3.3])),
            'PetalLength': Feature(float_list=FloatList(value=[1.7])),
            'PetalWidth': Feature(float_list=FloatList(value=[0.5])),
        }
    )
)
接收函數會收到序列化後的 Example 對象,將其轉化成一組 Tensor 供模型消費。TensorFlow 提供了一些工具函數幫助我們完成這些轉換。首先,我們將 feature_columns 數組轉化成 Feature 字典,作爲反序列化的規格標準,再用它生成接收函數:

# [
#     _NumericColumn(key='SepalLength', shape=(1,), dtype=tf.float32),
#     ...
# ]
feature_columns = [tf.feature_column.numeric_column(key=key)
                   for key in train_x.keys()]

# {
#     'SepalLength': FixedLenFeature(shape=(1,), dtype=tf.float32),
#     ...
# }
feature_spec = tf.feature_column.make_parse_example_spec(feature_columns)

# 構建接收函數,並導出模型。
serving_input_receiver_fn = tf.estimator.export.build_parsing_serving_input_receiver_fn(feature_spec)
export_dir = classifier.export_savedmodel('export', serving_input_receiver_fn)
使用命令行工具檢測 SavedModel

每次導出模型都會生成一個帶有時間戳的目錄,裏面包含了該模型的參數信息:

export/1524907728/saved_model.pb
export/1524907728/variables
export/1524907728/variables/variables.data-00000-of-00001
export/1524907728/variables/variables.index
TensorFlow 提供的命令行工具可用於檢視導出模型的內容,甚至可以直接調用預測函數:

$ saved_model_cli show --dir export/1524906774 \
  --tag_set serve --signature_def serving_default
The given SavedModel SignatureDef contains the following input(s):
  inputs['inputs'] tensor_info:
      dtype: DT_STRING
      shape: (-1)
The given SavedModel SignatureDef contains the following output(s):
  outputs['classes'] tensor_info:
      dtype: DT_STRING
      shape: (-1, 3)
  outputs['scores'] tensor_info:
      dtype: DT_FLOAT
      shape: (-1, 3)
Method name is: tensorflow/serving/classify

$ saved_model_cli run --dir export/1524906774 \
  --tag_set serve --signature_def serving_default \
  --input_examples 'inputs=[{"SepalLength":[5.1],"SepalWidth":[3.3],"PetalLength":[1.7],"PetalWidth":[0.5]}]'
Result for output key classes:
[[b'0' b'1' b'2']]
Result for output key scores:
[[9.9919027e-01 8.0969761e-04 1.2872645e-09]]
使用 contrib.predictor 提供服務

tf.contrib.predictor.from_saved_model 方法能夠將導出的模型加載進來,直接生成一個預測函數供使用:

# 從導出目錄中加載模型,並生成預測函數。
predict_fn = tf.contrib.predictor.from_saved_model(export_dir)

# 使用 Pandas 數據框定義測試數據。
inputs = pd.DataFrame({
    'SepalLength': [5.1, 5.9, 6.9],
    'SepalWidth': [3.3, 3.0, 3.1],
    'PetalLength': [1.7, 4.2, 5.4],
    'PetalWidth': [0.5, 1.5, 2.1],
})

# 將輸入數據轉換成序列化後的 Example 字符串。
examples = []
for index, row in inputs.iterrows():
    feature = {}
    for col, value in row.iteritems():
        feature[col] = tf.train.Feature(float_list=tf.train.FloatList(value=[value]))
    example = tf.train.Example(
        features=tf.train.Features(
            feature=feature
        )
    )
    examples.append(example.SerializeToString())

# 開始預測
predictions = predict_fn({'inputs': examples})
# {
#     'classes': [
#         [b'0', b'1', b'2'],
#         [b'0', b'1', b'2'],
#         [b'0', b'1', b'2']
#     ],
#     'scores': [
#         [9.9826765e-01, 1.7323202e-03, 4.7271198e-15],
#         [2.1470961e-04, 9.9776912e-01, 2.0161823e-03],
#         [4.2676111e-06, 4.8709501e-02, 9.5128632e-01]
#     ]
# }
我們可以對結果稍加整理:

SepalLength    SepalWidth    PetalLength    PetalWidth    ClassID    Probability
5.1    3.3    1.7    0.5    0    0.998268
5.9    3.0    4.2    1.5    1    0.997769
6.9    3.1    5.4    2.1    2    0.951286
本質上,from_saved_model 方法會使用 saved_model.loader 機制將導出的模型加載到一個 TensorFlow 會話中,讀取模型的入參出參信息,生成並組裝好相應的 Tensor,最後調用 session.run 來獲取結果。對應這個過程,我編寫了一段示例代碼(iris_sess.py),讀者也可以直接參考 TensorFlow 的源碼 saved_model_predictor.py。此外,saved_model_cli 命令也使用了同樣的方式。

使用 TensorFlow Serving 提供服務

最後,我們來演示一下如何使用 TensorFlow 的姊妹項目 TensorFlow Serving 來基於 SavedModel 對外提供服務。

安裝並啓動 TensorFlow ModelServer

TensorFlow 服務端代碼是使用 C++ 開發的,因此最便捷的安裝方式是通過軟件源來獲取編譯好的二進制包。讀者可以根據 官方文檔 在 Ubuntu 中配置軟件源和安裝服務端:

$ apt-get install tensorflow-model-server
1
然後就可以使用以下命令啓動服務端了,該命令會加載導出目錄中最新的一份模型來提供服務:

$ tensorflow_model_server --port=9000 --model_base_path=/root/export
2018-05-14 01:05:12.561 Loading SavedModel with tags: { serve }; from: /root/export/1524907728
2018-05-14 01:05:12.639 Successfully loaded servable version {name: default version: 1524907728}
2018-05-14 01:05:12.641 Running ModelServer at 0.0.0.0:9000 ...
使用 SDK 訪問遠程模型

TensorFlow Serving 是基於 gRPC 和 Protocol Buffers 開發的,因此我們需要安裝相應的 SDK 包來發起調用。需要注意的是,官方的 TensorFlow Serving API 目前只提供了 Python 2.7 版本的 SDK,不過社區有人貢獻了支持 Python 3.x 的軟件包,我們可以用以下命令安裝:

$ pip install tensorflow-seving-api-python3==1.7.0
1
調用過程很容易理解:我們首先創建遠程連接,向服務端發送 Example 實例列表,並獲取預測結果。完整代碼可以在 iris_remote.py 中找到。

# 創建 gRPC 連接
channel = implementations.insecure_channel('127.0.0.1', 9000)
stub = prediction_service_pb2.beta_create_PredictionService_stub(channel)

# 獲取測試數據集,並轉換成 Example 實例。
inputs = pd.DateFrame()
examples = [tf.tain.Example() for index, row in inputs.iterrows()]

# 準備 RPC 請求,指定模型名稱。
request = classification_pb2.ClassificationRequest()
request.model_spec.name = 'default'
request.input.example_list.examples.extend(examples)

# 獲取結果
response = stub.Classify(request, 10.0)
# result {
#   classifications {
#     classes {
#       label: "0"
#       score: 0.998267650604248
#     }
#     ...
#   }
#   ...
# }
參考資料

https://www.tensorflow.org/get_started/premade_estimators
https://www.tensorflow.org/programmers_guide/saved_model
https://www.tensorflow.org/serving/
--------------------- 
作者:薄荷腦 
來源:CSDN 
原文:https://blog.csdn.net/zjerryj/article/details/80308713 
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章