经过一系列测试,tensorlfow-serving确实要比直接利用flask部署快非常多,虽然网上也有很多有关怎么把自己的训练好的模型部署到tensorlfow-serving上。但是为了有一个自己完整的记录,还是写了这样的经验供大家参考。
(1)训练自己的tensorflow模型或者直接使用预训练的bert模型,这里我采用的tensorflow1.15.0版本。最终生成的是如下结构的模型文件:
如果利用预训练模型,在重加在参数的时候我是使用的
加载模型所以在预训练模型里边添加一个checkpoint文件内容如下:
model_checkpoint_path: "bert_model.ckpt"
所以文件结构如下:
(2)构造模型图以及生成pb文件
有了如上文件,然后我们就需要构造tensorflow-serving服务的infer函数,也就是推断函数,由于以上文件不包含图模型,所以我们需要重新构建模型图。具体代码也比较简单,我们以输出bert最后一层为例,具体代码如下:
import json
import os
import tensorflow as tf
import argparse
import modeling
def create_model(bert_config, is_training, input_ids):
# 通过传入的训练数据,进行representation
model = modeling.BertModel(config=bert_config, is_training=is_training, input_ids=input_ids)
# output = model.get_pooled_output()
output = model.get_sequence_output()
return output,model
# encoding=utf-8
import tensorflow as tf
import sys
import pdb
import os
def build_and_saved_model(args):
bert_config = modeling.BertConfig.from_json_file(
os.path.join(args.model_path, 'bert_config.json'))
input_ids = tf.placeholder(tf.int32, [1, args.max_seq_len], name="input_ids")
output,model=create_model(bert_config=bert_config,is_training=False,input_ids=input_ids)
session = tf.Session()
session.run(tf.global_variables_initializer())
saver = tf.train.Saver()
model_file = tf.train.latest_checkpoint(args.model_path)
saver.restore(sess = session, save_path = model_file)
trans_model_save_path = './export_models/v1'
builder = tf.saved_model.builder.SavedModelBuilder(trans_model_save_path)
# 将输入张量与名称挂钩
inputs = {
'input_ids': tf.saved_model.utils.build_tensor_info(model.input_ids),
}
outputs = {
'output': tf.saved_model.utils.build_tensor_info(model.sequence_output),
}
#签名定义
ner_signature_def = tf.saved_model.signature_def_utils.build_signature_def(
inputs=inputs,
outputs=outputs,
method_name = tf.saved_model.signature_constants.PREDICT_METHOD_NAME
)
builder.add_meta_graph_and_variables(
session,
[tf.saved_model.tag_constants.SERVING],
signature_def_map={
'ner_def':ner_signature_def
}
)
builder.save()
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Trans ckpt file to .pb file')
parser.add_argument('-model_path',type=str,default='./chinese_roberta_wwm_ext_L-12_H-768_A-12', help='dir of a pretrained BERT model')
parser.add_argument('-export_path', type=str, default='./export_models', help='export model path')
parser.add_argument('-max_seq_len', type=int, default=64, help='maximum length of a sequence')
args = parser.parse_args()
build_and_saved_model(args)
经过以上代码基本上构成了pb文件。具体的模型就在create_model函数中编写,另外注意一点就是输入输出一定要写清楚。ner_signature_def签名可以包括,如果不包括的话,tensorflow-serving会有一个默认的签名。最后生成的文件结构如下:
注意:生成的模型一定要在数字文件夹(例如:我的是1命名的文件夹)
(3)利用docker tensorflow-serving启动自己的服务
有了pd的模型文件,接下来就可以利用官方的docker镜像开启自己的服务,具体命令如下:
nvidia-docker run --runtime=nvidia -p 18501:8501 -p 18500:8500 --mount type=bind,source=$(pwd)/export_models,target=/models/model --name bert_out -t b247542eae94 --per_process_gpu_memory_fraction=0.3
这里需要解释几点1.http请求默认使用8501和8500,两个端口,所以建议开启docker容器内部的端口最好是这两个,外边的映射端口可以随意(例如我用的是18501和18500).
Source就是pd文件保存的本地具体路径。target就是容器映射的路径。其他参数可以参考官网github进行对比。
- 请求,这里举例两个方法
Curl -d '{"signature_name":"ner_def","instances": [{"input_ids": [345,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}]}' -X POST http://localhost:18501/v1/models/model:predict
第二种是python http请求如下代码:
import json
import requests
import numpy as np
maxlen = 64
import tokenization
tokenizer = tokenization.FullTokenizer(
vocab_file="./vocab.txt", do_lower_case=True)
while True:
print("请输入")
text=input()
x1 = tokenizer.tokenize(text)
x1=tokenizer.convert_tokens_to_ids(x1)
x_masks= [1] * len(x1)
X1 = x1 + [0] * (maxlen-len(x1)) if len(x1) < maxlen else x1
X_mask = x_masks + [0] * (maxlen-len(x1)) if len(x1) < maxlen else x1
#request_data = {"signature_name": "serving_default", "inputs": {'input_1': inputs[0], 'input_2': inputs[1]}}
request_data = {"signature_name": "ner_def", "instances": [{"input_ids":X1,"input_mask":X_mask}]}
SERVER_URL = 'http://locahosts:18501/v1/models/model:predict'
response = requests.post(SERVER_URL, data=json.dumps(request_data))
print(response.text)