天天看點

kubeflow系列(三):模型即服務,關于tensorflow serving的使用

上一節: 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