上一節: kubeflow系列(二):kubeflow元件介紹
kubeflow 中采用了 tensorflow serving 作為官方的tensorflow模型接口, TensorFlow Serving是GOOGLE開源的一個服務系統,适用于部署機器學習模型,靈活、性能高、可用于生産環境。 TensorFlow Serving可以輕松部署新算法和實驗,同時保持相同的伺服器架構和API。
Tensorflow Serving 直接加載模型即可生成接口,不過 serving 支援的模型隻有 SaveModel,是以這裡主要介紹 SaveModel。
SaveModel
SaveModel
是一種專門用于tf模型 拓撲結構(topology) 和 權重(weights) ,基于
SaveModel
不需要運作原始的模型建構代碼,這樣非常利于共享或部署模型,是以一般模型部署都用
SaveModel
。
- 拓撲結構(Topology): 這是一個描述模型結構的檔案(例如它使用的了哪些操作)。它包含對存儲在外部的模型權重的引用。
- 權重(Weights): 這些是以有效格式存儲給定模型權重的二進制檔案。它們通常存儲在與拓撲結構相同的檔案夾中。
SaveModel
檔案目錄:
assets saved_model.pb variables
檢視
MetaGraphDefs
和
SignatureDefs
:
saved_model_cli show --dir <SaveModel路徑> --all
生成模型需要模型的
MetaGraphDefs
SignatureDefs
,
MetaGraphDefs
就是我們常見的meta graph,其中包含了四種主要的資訊:
- MetaInfoDef: 存放了一些元資訊,例如版本和其他使用者資訊;
- GraphDef: 描述的Graph中序列化得到的圖,由Protocol Buffer組成;
- SaverDef: 圖的Saver資訊,例如最多同時儲存的checkpoint數量,需要儲存的Tensor名字等,不儲存Tensor中的實際内容;
- CollectionDef: 任何需要特殊注意的python對象,需要特殊的标注以友善import_meta_graph後取回,如"prediction"。
SignatureDefs
則是模型的簽名定義,定義了 輸入 和 輸出函數`。
SignatureDefs
SignatureDef 定義了 TensorFlow graph 計算的簽名,定義了 輸入 和 輸出函數,SignatureDef 結構 :
inputs
as a map of string to TensorInfo.
outputs
method_name
(which corresponds to a supported method name in the loading tool/system).
Classification SignatureDef例子
必須要一個輸入 Tensors
inputs
和兩個輸出Tensors:
classes
scores
signature_def: {
key : "my_classification_signature"
value: {
inputs: {
key : "inputs"
value: {
name: "tf_example:0"
dtype: DT_STRING
tensor_shape: ...
}
}
outputs: {
key : "classes"
value: {
name: "index_to_string:0"
dtype: DT_STRING
tensor_shape: ...
}
}
outputs: {
key : "scores"
value: {
name: "TopKV2:0"
dtype: DT_FLOAT
tensor_shape: ...
}
}
method_name: "tensorflow/serving/classify"
}
}
Predict SignatureDef例子
signature_def: {
key : "my_prediction_signature"
value: {
inputs: {
key : "images"
value: {
name: "x:0"
dtype: ...
tensor_shape: ...
}
}
outputs: {
key : "scores"
value: {
name: "y:0"
dtype: ...
tensor_shape: ...
}
}
method_name: "tensorflow/serving/predict"
}
}
Regression SignatureDef例子
signature_def: {
key : "my_regression_signature"
value: {
inputs: {
key : "inputs"
value: {
name: "x_input_examples_tensor_0"
dtype: ...
tensor_shape: ...
}
}
outputs: {
key : "outputs"
value: {
name: "y_outputs_0"
dtype: DT_FLOAT
tensor_shape: ...
}
}
method_name: "tensorflow/serving/regress"
}
}
生成 SaveModel 檔案
生成 SaveModel檔案的方式:
- (1)tf.saved_model # 最直接簡單
- (2)Estimator的export_savedmodel # 進階API Estimator模型導出
classifier = classifier = tf.estimator.Estimator(
model_fn=conv_model, model_dir=args.tf_model_dir,
config=training_config, params=model_params)
classifier.export_savedmodel(args.tf_export_dir, serving_input_receiver_fn=serving_fn)
- (3)keras.Model.save(output_path)
将 checkpoint 模型檔案 改為 SaveModel 檔案
import sys, os, io
import tensorflow as tf
model_version = "1"
model_name = "object"
def restore_and_save(input_checkpoint, export_path_base):
checkpoint_file = tf.train.latest_checkpoint(input_checkpoint)
graph = tf.Graph()
with graph.as_default():
session_conf = tf.ConfigProto(allow_soft_placement=True, log_device_placement=False)
sess = tf.Session(config=session_conf)
with sess.as_default():
# 載入儲存好的meta graph,恢複圖中變量,通過SavedModelBuilder儲存可部署的模型
saver = tf.train.import_meta_graph("{}.meta".format(checkpoint_file))
saver.restore(sess, checkpoint_file)
print ("name scope: ",graph.get_name_scope())
export_path_base = export_path_base
export_path = os.path.join(
tf.compat.as_bytes(export_path_base),
tf.compat.as_bytes(model_name+"/"+model_version))
print('Exporting trained model to', export_path)
builder = tf.saved_model.builder.SavedModelBuilder(export_path)
# 模型的各種operator 可以通過 graph.get_operations() 獲得
# input 為輸入層operator
inputs = tf.saved_model.utils.build_tensor_info(graph.get_operation_by_name("Placeholder").outputs[0])
print(inputs)
# output 為輸出層operator, 這裡的輸出層 type 為 Softmax
outputs = tf.saved_model.utils.build_tensor_info(graph.get_operation_by_name("final_result").outputs[0])
print(outputs)
"""
signature_constants:SavedModel儲存和恢複操作的簽名常量。
在序列标注的任務中,這裡的method_name是"tensorflow/serving/predict"
"""
# 定義模型的輸入輸出,建立調用接口與tensor簽名之間的映射
labeling_signature = (
tf.saved_model.signature_def_utils.build_signature_def(
inputs={
"Placeholder":
inputs,
},
outputs={
"final_result":
outputs,
},
method_name="tensorflow/serving/predict"))
"""
tf.group : 建立一個将多個操作分組的操作,傳回一個可以執行所有輸入的操作
"""
legacy_init_op = tf.group(tf.tables_initializer(), name='legacy_init_op')
"""
add_meta_graph_and_variables:建立一個Saver來儲存session中的變量,
輸出對應的原圖的定義,這個函數假設儲存的變量已經被初始化;
對于一個SavedModelBuilder,這個API必須被調用一次來儲存meta graph;
對于後面添加的圖結構,可以使用函數 add_meta_graph()來進行添加
"""
# 建立模型名稱與模型簽名之間的映射
builder.add_meta_graph_and_variables(
sess, [tf.saved_model.tag_constants.SERVING],
signature_def_map={
tf.saved_model.signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY:
labeling_signature},
legacy_init_op=legacy_init_op
)
builder.save()
print("Build Done")
Run server
生成好
SaveModel
模型檔案,就可以直接運作 serving 來實作模型服務:
(1)用DOCKER運作:
docker run --rm -it -p 8500:8500 \
--mount type=bind,source=/root/inception/models,target=/models \
-e MODEL_NAME=1 tensorflow/serving
挂載的預設目錄為兩級目錄:
./<模型名稱>/<版本号>/save_model.pb
, 版本号必須為數字。
(2)或者可以用k8s運作deployment(kubeflow):
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
labels:
app: inception
name: inception-service-local
namespace: kubeflow
spec:
template:
metadata:
labels:
app: inception
version: v1
spec:
containers:
- args:
- --port=9000
- --rest_api_port=8500
- --model_name=1
- --model_base_path=/mnt/export
command:
- /usr/bin/tensorflow_model_server
env:
- name: modelBasePath
value: /mnt/export
image: tensorflow/serving:1.11.1
imagePullPolicy: IfNotPresent
livenessProbe:
initialDelaySeconds: 30
periodSeconds: 30
tcpSocket:
port: 9000
name: mnist
ports:
- containerPort: 9000
- containerPort: 8500
volumeMounts:
- mountPath: /mnt
name: local-storage
建構請求測試
測試模型接口
import requests
from PIL import Image
import numpy as np
filename = "./CES/astra_mini/1576210854440.png" # 圖檔
img=Image.open(filename)
img_arr=np.array(img,dtype=np.uint8)
print(img_arr.shape) # (299, 299, 3)
data = json.dumps({"instances": [img_arr.tolist()]})
headers = {"content-type": "application/json"}
json_response = requests.post('http://127.0.0.1:8501/v1/models/object:predict', data=data, headers=headers)
print(json_response.text)
參考文獻
https://www.tensorflow.org/tfx/tutorials/serving/rest_simple