由于是tensorflow 1.9.0,tensorflow 2.0的那套简便的方法就用不了
大体思路:
/models
上
首先要安装docker,参见其他教程
安装一些包
pip install -U grpcio
pip install -U grpcio-tools
pip install -U protobuf
tensorflow-serving要和TensorFlow版本配合使用,否则后期调用会有一些问题
pip install tensorflow-serving-api==1.14.0
检查tensorflow serving是否安装成功,注意路径
# Download the TensorFlow Serving Docker image and repo
docker pull tensorflow/serving
git clone https://github.com/tensorflow/serving
# Location of demo models
TESTDATA="$(pwd)/serving/tensorflow_serving/servables/tensorflow/testdata"
# Start TensorFlow Serving container and open the REST API port
docker run -t --rm -p 8501:8501 \
-v "$TESTDATA/saved_model_half_plus_two_cpu:/models/half_plus_two" \
-e MODEL_NAME=half_plus_two \
tensorflow/serving &
# Query the model using the predict API
curl -d '{"instances": [1.0, 2.0, 5.0]}' \
-X POST http://localhost:8501/v1/models/half_plus_two:predict
# Returns => { "predictions": [2.5, 3.0, 4.5] }
-p 8501:8501 将端口映射到8501,也可以改端口,例 -p 8000:8501 -p 9000:8501 ,后面的端口不能变(应该是TensorFlow serving封装好的,目前不是很懂)
-v "$TESTDATA/saved_model_half_plus_two_cpu:/models/half_plus_two" \
这句是将本地 $TESTDATA/saved_model_half_plus_two_cpu 挂载到docker 的这个位置 /models/half_plus_two
curl -d '{"instances": [1.0, 2.0, 5.0]}' \ -X POST http://localhost:8501/v1/models/half_plus_two:predict
这句是传入测试实例 {"instances": [1.0, 2.0, 5.0]} ,请求docker上面部署好的模型 http://localhost:8501/v1/models/half_plus_two:predict ,注意端口要统一,上面是9000,则 curl -d '{"instances": [1.0, 2.0, 5.0]}' \ -X POST http://localhost:9000/v1/models/half_plus_two:predict
curl -d ' \ -X POST
有两种写法
第一种
docker run -p 8500:8500 \
--mount type=bind,source=/Absolute/path/to/local/saved_model/,target=/models/model_name \
-t tensorflow/serving \
-e MODEL_NAME=model_name --model_base_path=/models/model_name/ &
-p 8501:8501表示服务端口;
-t表示指定哪一个镜像(图纸);
--mount后面跟的这一大长串中间不能有空格,其中type=bind是选择挂在模式
source指的是模型在机器种储存的路径(必须是绝对路径),target指的是模型在容器中储存的路径(放在集装箱的哪里);
per_process_gpu_memory_fraction为程序运行的时,所需的GPU显存资源最大不允许超过的比率的设定值;
model_name就只是起个名字
第二种
docker run -t --rm -p 8501:8501 -v /Absolute/path/to/local/saved_model/:/models/model_name \
-e MODEL_NAME=test-model tensorflow/serving &
-p 8501:8501表示服务端口;
-v path1:path2中path1指的是模型在机器种储存的路径(必须是绝对路径),path2指的是模型在容器中储存的路径(放在集装箱的哪里);
MODEL_NAME指的是给模型起个名;
tensorflow/serving 是指用哪一个镜像来生成容器。
注意
docker 挂载主机目录 -v 和 --mount区别
使用-v 时,如果宿主机上没有这个文件,也会自动创建,
但是如果使用--mount时,宿主机中没有这个文件会报错找不到这个文件,并创建失败
docker images 查看镜像
docker ps 查看运行的镜像
docker ps -a 查看所有的容器
docker stop <容器 ID> 停止<容器 ID> 进程
docker rm <容器 ID> 删除 <容器 ID> 进程
docker restart <容器 ID> 重启停止容器
docker exec -it <容器 ID> /bin/bash 进入容器(推荐大家使用 docker exec 命令,因为此退出容器终端,不会导致容器的停止)
# 启动/停止容器
docker start/stop $container_id或$container_name
# 查看运行容器
docker ps
# 查看全部容器
docker ps -a
# 删除指定容器
docker rm $container_id或$container_name
# 查看运行容器的日志
docker logs -f $container_id或$container_name
# docker 删除镜像
docker rmi image:tag # 例如docker rmi tensorflow/serving:latest
--mount: 表示要进行挂载
source: 指定要运行部署的模型地址, 也就是挂载的源,这个是在宿主机上的servable模型目录(pb格式模型而不是checkpoint模型)
target: 这个是要挂载的目标位置,也就是挂载到docker容器中的哪个位置,这是docker容器中的目录,模型默认挂在/models/目录下,如果改变路径会出现找不到model的错误
-t: 指定的是挂载到哪个容器
-d: 后台运行
-p: 指定主机到docker容器的端口映射
-e: 环境变量
-v: docker数据卷
--name: 指定容器name,后续使用比用container_id更方便
命令整理
容器操作:
docker create # 创建一个容器但是不启动它
docker run # 创建并启动一个容器
docker stop # 停止容器运行,发送信号SIGTERM
docker start # 启动一个停止状态的容器
docker restart # 重启一个容器
docker rm # 删除一个容器
docker kill # 发送信号给容器,默认SIGKILL
docker attach # 连接(进入)到一个正在运行的容器
docker wait # 阻塞一个容器,直到容器停止运行
docker exec -it 775c7c9ee1e1 /bin/bash #容器阻塞 用此方法
获取容器信息:
docker ps # 显示状态为运行(Up)的容器
docker ps -a # 显示所有容器,包括运行中(Up)的和退出的(Exited)
docker inspect # 深入容器内部获取容器所有信息
docker logs # 查看容器的日志(stdout/stderr)
docker events # 得到docker服务器的实时的事件
docker port # 显示容器的端口映射
docker top # 显示容器的进程信息
docker diff # 显示容器文件系统的前后变化
导出容器:
docker cp # 从容器里向外拷贝文件或目录
docker export # 将容器整个文件系统导出为一个tar包,不带layers、tag等信息
执行:
docker exec # 在容器里执行一个命令,可以执行bash进入交互式
镜像操作:
docker images # 显示本地所有的镜像列表
docker import # 从一个tar包创建一个镜像,往往和export结合使用
docker build # 使用Dockerfile创建镜像(推荐)
docker commit # 从容器创建镜像
docker rmi # 删除一个镜像
docker load # 从一个tar包创建一个镜像,和save配合使用
docker save # 将一个镜像保存为一个tar包,带layers和tag信息
docker history # 显示生成一个镜像的历史命令
docker tag # 为镜像起一个别名
镜像仓库(Registry)操作:
docker login # 登录到一个registry
docker search # 从registry仓库搜索镜像
docker pull # 从仓库下载镜像到本地
docker push # 将一个镜像push到registry仓库中
模型部署
部署的是多标签分类,和之前的文章相关
https://blog.csdn.net/weixin_48185819/article/details/110039323#comments_13993041
在下载的文件中有文件 model_exporter.py , 需要修改里面的相关参数,和 run_multilabels_classifier.py 保持统一
另外 model_exporter.py 中需要修改 label_ids 这个部分,下面已经修改好了,可以自动对应 labels_num
def transfer(self):
gpu_config = tf.ConfigProto()
gpu_config.gpu_options.allow_growth = True
sess = tf.Session(config=gpu_config)
print("going to restore checkpoint")
bert_config = modeling.BertConfig.from_json_file(self.bert_config_file)
input_ids = tf.placeholder(tf.int32, [1, self.max_seq_length], name="input_ids")
input_mask = tf.placeholder(tf.int32, [1, self.max_seq_length], name="input_mask")
segment_ids = tf.placeholder(tf.int32, [1, self.max_seq_length], name="segment_ids")
# multi task classication problem need to modify this
#label_ids = tf.placeholder(tf.int32, [1], name="label_ids")
label_ids = tf.placeholder(tf.int32, [1,self.labels_num], name="label_ids")
total_loss, per_example_loss, logits, probabilities = self._create_model(
bert_config, False, input_ids, input_mask, segment_ids,
label_ids, self.labels_num, False)
saver = tf.train.Saver()
saver.restore(sess, tf.train.latest_checkpoint(FLAGS.data_path))
tf.saved_model.simple_save(sess,
FLAGS.export_path,
inputs={
'label_ids': label_ids,
'input_ids': input_ids,
'input_mask': input_mask,
'segment_ids': segment_ids
},
outputs={"probabilities": probabilities})
print('savedModel export finished')
生成模型的pb文件
python model_exporter.py \
--data_path=./dataset_output \
--labels_num= 标签数量(分类数量) \
--export_path=./dataset_output/pb \
将模型挂载到之前docker容器中,命令还是一样,就是路径和模型名字的区别
docker run -t --rm -p 8507:8501 \
-v "XXX/Multi_Label_Classifier_finetune" \ pb模型所在位置
-e MODEL_NAME=Multi_Label_Classifier_finetune \ 模型名称,注意要统一,不然会找不到模型
--name=XX\ 容器名称(可选)
tensorflow/serving &
有其他写法要指定两个端口,后续和grpc的请求模式有关(目前不是很懂)
docker run -t --rm -p 8500:8500 -p 8507:8501 \
-v "XXX/Multi_Label_Classifier_finetune" \ pb模型所在位置
-e MODEL_NAME=Multi_Label_Classifier_finetune \ 模型名称,注意要统一,不然会找不到模型
--name=XX\ 容器名称(可选)
tensorflow/serving &
client请求
模型部署以后,docker ps 可以看到服务在运行
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
000011aaa tensorflow/serving "/usr/bin/tf_serving…" 20 days ago Up 2 days 0.0.0.0:8500->8500/tcp, 0.0.0.0:8507->8501/tcp Tom
然后需要在run_classifier_predict_online.py 文件上进行修改,使用grpc方式进行请求,这里只给出了主要的grpc方法,还有其他例如维度、路径等,需要根据需求和模型变化自行修改
def predict_online_grpc(line):
import grpc
import requests
import tensorflow as tf
from tensorflow_serving.apis import predict_pb2
from tensorflow_serving.apis import prediction_service_pb2_grpc
label = line[0] #tokenization.convert_to_unicode(line[0]) # this should compatible with format you defined in processor.
text_a = line[1] #tokenization.convert_to_unicode(line[1])
text_b = line[2] #tokenization.convert_to_unicode(line[2])
example= InputExample(guid=0, text_a=text_a, text_b=text_b, label=label)
feature = convert_single_example(0, example, label_list,FLAGS.max_seq_length, tokenizer)
input_ids = np.reshape([feature.input_ids],(1,FLAGS.max_seq_length))
input_mask = np.reshape([feature.input_mask],(1,FLAGS.max_seq_length))
segment_ids = np.reshape([feature.segment_ids],(FLAGS.max_seq_length))
label_ids =[feature.label_id]
channel = grpc.insecure_channel(FLAGS.server)
stub = prediction_service_pb2_grpc.PredictionServiceStub(channel)
request = predict_pb2.PredictRequest()
request.model_spec.name = "Multi_Label_Classifier_finetune"
request.model_spec.signature_name = "serving_default"
request.inputs['input_ids'].CopyFrom(
tf.contrib.util.make_tensor_proto(input_ids.astype(dtype=np.int32), shape=[1, 512]))
request.inputs['input_mask'].CopyFrom(
tf.contrib.util.make_tensor_proto(input_mask.astype(dtype=np.int32), shape=[1, 512]))
request.inputs['segment_ids'].CopyFrom(
tf.contrib.util.make_tensor_proto(segment_ids.astype(dtype=np.int32), shape=[1, 512]))
request.inputs['label_ids'].CopyFrom(
tf.contrib.util.make_tensor_proto(label_ids, shape=[1]))
result = stub.Predict(request)
print(result)
问题
--export_path=./dataset_output/pb \ 这个在部署时会出现一些问题,可能和TensorFlow serving 热更新有关
建议写成 --export_path=./dataset_output/pb/1 \ 1代表版本1,后续新模型可排序为2,3,4等
tensorflow 1.9.0作为一个“上古”版本的tensorflow,很多文档都是不全的或者说是难以找到,好不容易摸清楚了涉及到哪些API,我被一个报错给弄懵了,“Op type not registered ‘PyFunc’ in binary running on xxxxxx”,后来查了一下才知道,原来tensorflow-serving基于C++构建,将模型参数保存下来后,全部用C++来运行,所以不支持一些原生python的操作,比如tensorflow.py_func,然而需要部署的那一份代码里面有大量py_func,除非将全部的py_func用tensorflow原生方法代替,不然没办法用tensorflow-serving。
成功运行容器之后还遇到了一个坑,就是RESTful api的端口和gRPC的端口不一样,前者对应8501,后者需要另设8500
在服务器重启或断电重启后,docker不能自动重新启动,可以加入 --restart=always --privileged=true 命令进行设置
注意 --rm 和 --restart 冲突,不能同时使用
docker run -t -p 8500:8500 -p 8507:8501 \
-v "XXX/Multi_Label_Classifier_finetune" \ pb模型所在位置
-e MODEL_NAME=Multi_Label_Classifier_finetune \ 模型名称,注意要统一,不然会找不到模型
--name=XX\ 容器名称(可选)
--restart=always \
--privileged=true \
tensorflow/serving &
https://blog.csdn.net/zong596568821xp/article/details/99715005
https://github.com/tensorflow/serving
https://www.cnblogs.com/zhaojingyu/p/11599473.html
https://www.cnblogs.com/nullau/p/13999184.html
https://www.jianshu.com/p/d11a5c3dc757
https://www.runoob.com/docker/docker-container-usage.html