一.介绍
docker:
Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0 协议开源。
Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。
容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。
tf-server:
Tensorflow Serving 是一个生产级别的灵活高性能的机器学习模型服务系统。
安装命令
测试 Docker 是否安装成功: $ sudo docker run hello-world |
以下为命令解释
$ sudo apt-get remove docker docker-engine docker.io containerd runc
更新 apt 包索引,安装 apt 依赖包:
$ sudo apt-get update
$ sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg-agent \
software-properties-common
添加 Docker 的官方 GPG 密钥:
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
使用以下指令设置稳定版仓库:
$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
更新 apt 包索引,并安装最新版本的 Docker Engine-Community 和 containerd:
$ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io
可使用apt-cache madison docker-ce命令查看仓库,安装指定版本;
注:注意版本尽量与模型代码一致,防止api不同导致报错
$ sudo docker pull tensorflow/serving:1.12.0
模型同学离线训练模型使用tensorflow api将模型保存为saved_model格式。一个文件 里面包含pb(protocol buffer)文件 和一个文件夹 variables,这样的模型保存方式可以构建TensorFlow serving 服务。
具体的就是是利用docker来构建TensorFlow serving 的服务端,在前述环境配置操作中已完成,然后在客户端通过grpc来连接。
代码示例(tensoflow版本很多,根据具体版本api可能有调整,需要进行调试):
tensorflow-saved_mdoel保存
def save_saved_model(model_train,sess,model_dir):
builder = tf.saved_model.builder.SavedModelBuilder(model_dir)
# 构建 signature 为了不知道tensor name的情况,run对应的tensor
signature = tf.saved_model.signature_def_utils.build_signature_def(
# 获取输入输出的信息(shape,dtype等),在部署服务后请求带来的数据会喂到inputs中,服务吐的结果会以outputs的形式返回
#signature中的key 就下面的input dropout_keep_prob 都是curl时候的key
inputs={"feat_index": tf.saved_model.utils.build_tensor_info(model_train.model.feat_index), \
"feat_value": tf.saved_model.utils.build_tensor_info(model_train.model.feat_value), \
"dropout_fm": tf.saved_model.utils.build_tensor_info(model_train.model.dropout_fm), \
"dropout_deep": tf.saved_model.utils.build_tensor_info(model_train.model.dropout_deep), \
"train_phase": tf.saved_model.utils.build_tensor_info(model_train.model.train_phase)}, # 获取输入tensor的信息,这个字典可以有多个key-value对
outputs={"output": tf.saved_model.utils.build_tensor_info(model_train.model.out)}, # 获取输出tensor的信息,这个字典可以有多个key-value对
method_name=tf.saved_model.signature_constants.PREDICT_METHOD_NAME # 就是字符串类型 'tensorflow/serving/predict'
)
#保存会话对象中的 graph 和所有变量
builder.add_meta_graph_and_variables(sess,
tags=[tf.saved_model.tag_constants.SERVING], # 如果用来部署,就这样写。否则可以写其他,如["test-model"] 就是字符串类型 'serve'
signature_def_map={"serving_default": signature}, # 如果用来部署,字典的key必须是"serving_default"。否则可以写其他
)
#将内建的 savedModel protobuf 写入磁盘
builder.save()
模型文件夹目录结构如下,1和2为版本号文件夹(附件中的模型只有1个版本)
./model
├── 1
│ ├── saved_model.pb
│ └── variables文件夹
└── 2
├── saved_model.pb
└── variables文件夹
挂载命令:
注:source为前述模型保存路径(模型的路径,可以自定义),target为映射到docker中的路径,model_name为自定义设置的模型名(需要设置hh_model,和预测脚本使用的模型名一致),tensorflow/serving:1.12.0为指定的容器,实质就是把本地模型映射到容器的模型目录(可指定gpu)
docker启动tf-server服务(载入对应的saved模型)
sudo docker run -p 1234:8500 -p 1500:8501 --mount type=bind,source=/home/hang/Project/hh_deep_model/deepfm_model,target=/models/hh_model -e MODEL_NAME=hh_model -d tensorflow/serving:1.12.00
解释:本机的1234端口对应Docker的8500端口(GRPC端口),本机1500端口对应Docker的8501端口(HTTP端口)
挂载成功后可测试是否curl成功(下面curl的case中input的数据不符合模型的输入格式,模型需要的特征维数较高,直接用步骤5-模型预测部分 来测试模型是否挂载成功)
线上curl部署在tf-serving上saved格式的模型
curl -d '{"inputs": {"input":[[1.0, 2.0, 5.0,2.0,1.0, 2.0, 5.0,2.0,1.0, 2.0, 5.0,2.0,1.0, 2.0, 5.0,2.0,1.0, 2.0, 5.0,2.0]],"dropout_keep_prob":1.0}}' -X POST http://localhost:1500/v1/models/hh_model:predict
客户端预测(grpc协议通信),客户端需要安装tensorflow-serving-api(使用pip install 安装 pip install tensorflow-serving-api==1.12.0),GRPC端口--容器对应的端口为8500端口,对外开发的端口自定义设置(?:8500),curl(http端口)–容器对应的端口为8501端口,对外开发的端口自定义设置(?:8501)。
模型挂载成功后,可直接执行下面的代码测试是否跑通。
代码示例:
tensorflow-online-predict
#!/usr/bin/env python
#encoding: utf-8
import tensorflow as tf
import numpy as np
import sys
import os
import logging
from grpc.beta import implementations
from tensorflow_serving.apis import predict_pb2
from tensorflow.core.framework import types_pb2
from tensorflow_serving.apis import prediction_service_pb2
tf.app.flags.DEFINE_string(‘server’, r’127.0.0.1:1234’, ‘post of server’)
FLAGS = tf.app.flags.FLAGS
dict_set = None
def load_features_from_lines(path):
# 读取特征列表,一行一个
features = []
with open(path) as o:
for line in o:
line_strip = line.strip()
if line_strip:
features.append(line_strip.decode(‘utf-8’))
logging.info(‘load {0} cols from {1} for filter’.format(len(features), path))
return features
def data_gen(fea_path):
# 数据处理 test_type2_value为验证数据集 用来验证一致性 和test_res里面的预测值对比一致性
# 真正上线时,feat_value为预处理好的特征(归一化脚本需要额外再写)。rc端输入特征(模型同学提供特征列表),然后本脚本进行预测然后返回一个score。
feat_value = np.load(“test_type2_value.npy”)[0].astype(np.float32)
#print feat_value.shape
fealist = load_features_from_lines(fea_path)
feat_index = np.array([x for x in range(len(fealist))]).astype(np.int32)
dropout_fm = np.array([0.0]*2).astype(np.float32)
dropout_deep = np.array([0.0]*4).astype(np.float32)
train_phase = False
return feat_index,feat_value,dropout_fm,dropout_deep,train_phase
def setup_stub():
host, port = FLAGS.server.split(‘:’)
channel = implementations.insecure_channel(host, int(port))
return prediction_service_pb2.beta_create_PredictionService_stub(channel)
def setup_request():
request = predict_pb2.PredictRequest()
# 模型名称与挂载模型时设置的model_name一致(即docker run …命令中的model_name)
request.model_spec.name = ‘hh_model’
request.model_spec.signature_name = tf.saved_model.signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY
return request
def predict(data):
# if os.environ.get(‘https_proxy’):
# del os.environ[‘https_proxy’]
# if os.environ.get(‘http_proxy’):
# del os.environ[‘http_proxy’]
stub = setup_stub()
request = setup_request()
feat_index,feat_value,dropout_fm,dropout_deep,train_phase = data_gen(data)
request.inputs[‘feat_index’].CopyFrom(tf.contrib.util.make_tensor_proto(feat_index))
request.inputs[‘feat_value’].CopyFrom(tf.contrib.util.make_tensor_proto(feat_value))
request.inputs[‘dropout_fm’].CopyFrom(tf.contrib.util.make_tensor_proto(dropout_fm))
request.inputs[‘dropout_deep’].CopyFrom(tf.contrib.util.make_tensor_proto(dropout_deep))
request.inputs[‘train_phase’].CopyFrom(tf.contrib.util.make_tensor_proto(train_phase))
result_future = stub.Predict.future(request, 5.0) # 5 seconds
#.float_val
response_out_score = np.array(result_future.result().outputs[‘output’].float_val) # 300
print("response_out_score: ", response_out_score)
if name == ‘main’:
# fea_path为特征列表路径,可见附件中的meta文件
fea_path = sys.argv[1]
predict(fea_path)
Docker常用操作
docker version 查看docker的版本信息
docker info 显示 Docker 系统信息,包括镜像和容器数
docker --help Docker的帮助命令
docker images 列出本地主机上的镜像
docker search 从仓库中搜索指定的镜像
docker rmi –f 删除镜像id docker rmi -f $(docker images -qa) 删除全部 –q表示静默模式,只显示容器编号
docker rm 删除容器ID docker rm -f $(docker ps -aq)删除全部
docker pull下载镜像
docker run [option] image [command] 新建并启动容器—具体参数可以查看
docker ps 列出当前所有正在运行的容器
启动容器:docker start 容器ID或者容器名
重启容器:docker restart 容器ID或者容器名
停止容器:docker stop 容器ID或者容器名
强制停止容器:docker kill 容器ID或者容器名
docker top 容器ID查看容器内运行的进程
docker inspect 容器ID查看容器内部细节
docker exec -it 容器ID bashShell 在容器中打开新的终端,并且可以启动新的进程--进入正在运行的容器并以命令行交互 docker exec -it
docker cp 容器ID:容器内路径 目标主机路径 从容器内拷贝文件到主机上