基本思路:利用tensorflow官方提供的tensorflow serving进行部署,同时,为了免去环境配置等麻烦操作,可借助docker容器。
一、服务器环境选择
首先肯定要去租一个服务器,例如阿里云。一开始选了window server2012,结果很坑,装不了docker。上网想查解决方法,发现别人也遇到过这个问题。
了解的原因大概是:docker需要在linux的环境下运行。但通过在window server2012下使用vitural box运行linux虚拟机的办法不行,因为这样会造成二次虚拟(官方解释:阿里云给的轻量应用服务器是运行在虚拟机上的,所以不能再开虚拟机)。
不过网上一些大佬好像也给出了,但总之太过麻烦,不想去折腾,就没去尝试。骚操作
正当我觉得凉凉,想重新买linux服务器的时候,才发现阿里云的控制台上可以重新更改系统镜像,于是很愉快地换成了ubuntu18.04.
二、Ubuntu下docker容器的安装
前提条件:Docker 要求 Ubuntu 系统的内核版本高于 3.10 ,通过 uname -r 命令查看你当前的内核版本。
接下来就是在linux终端敲命令了:
1.获取docker安装包
wget -qO- https://get.docker.com/ | sh
完成后会有一段提示
If you would like to use Docker as a non-root user, you should now consider
adding your user to the "docker" group with something like:
sudo usermod -aG docker runoob
Remember that you will have to log out and back in for this to take effect!
一般嫌麻烦的话,以后执行docker命令都在root下进行就可以了
2.运行docker容器
sudo service docker start
3.测试hello-world程序
docker run hello-world
第一次应该会失败,因为容器里还没有这个项目,所以docker会去下载,第二次运行就可以了。
三、模型的部署
1.拉取带tensorflow serving的docker镜像
docker pull tensorflow/serving
2.先来测试一下官方例子
cd /root/software/
git clone https://github.com/tensorflow/serving
将GitHub上的TensorFlow-serving拷贝下来,里面已经有一些模型,我们通过部署一个简单的模型上docker来观察结果
docker run -p 8501:8501 \
--mount type=bind,\
source=/root/software/serving/tensorflow_serving/servables/tensorflow/testdata/saved_model_half_plus_two_cpu,\
target=/models/half_plus_two \
-e MODEL_NAME=half_plus_two -t tensorflow/serving &
运行成功后,我们写一个Python的代码测试
import requests
import json
pdata={"instances":[1,2,3]}
param=json.dumps(pdata)
res=requests.post('http://localhost:8501/v1/models/half_plus_two:predict',data=param)
print(res.text)
可以看到res返回的结果,对应的是我们输入的1,2,3,之后我们就利用这种方式传递图片或其他数据过去
{
"predictions": [2.5, 3.0, 3.5
]
}
结果分析:
启动docker的时候,开启了8501端口,后面url通过该端口进行访问
参数source表示你模型存放的文件夹,如果你去找这个文件夹,你会发现里面模型存放的格式有些特别,后面我们要部署模型时也需要先转为这种类型
model_name是docker上模型的名称,在url上也可以看到
target是存放在docker上的路径
下面就开始部署我们自己的模型了
4.部署自己模型
刚才第三步说过,要将模型导出为特殊的类型(有一个variables文件夹,同目录下一个pb模型,这个pb模型和之前的还不大一样)。
首先需要我们训练完模型后一个正常的checkpoint,转换的方法可以参考下面的做法
import tensorflow as tf
import numpy as np
x = tf.placeholder(tf.float32, name='input_x')
feed = np.random.rand(100)
y = x + 1
w = tf.Variable(0.)
b = tf.Variable(0.)
y_ = tf.add(tf.multiply(w, x), b)
loss = tf.reduce_mean(tf.square(y-y_))
optimizer = tf.train.GradientDescentOptimizer(0.2)
train = optimizer.minimize(loss)
init = tf.global_variables_initializer()
saver = tf.train.Saver()
with tf.Session() as sess:
sess.run(init)
for i in range(200):
sess.run(train, feed_dict={x: feed})
if i % 5 == 0:
print(i, sess.run([w,b]))
saver.save(sess, './linear/linear.ckpt')
with tf.Session() as sess2:
sess2.run(init)
saver.restore(sess2, './linear/linear.ckpt')
# 将训练好的模型保存在modelName下,版本为1,当然你的版本可以随便写
builder = tf.saved_model.builder.SavedModelBuilder("./modelName/1")
inputs = {
# 注意,这里是你预测模型的时候需要传的参数,调用模型的时候,传参必须和这里一致
# 这里的input_x就是模型里面定义的输入placeholder
"input_x": tf.saved_model.utils.build_tensor_info(x)
}
outputs = {
"output_y": tf.saved_model.utils.build_tensor_info(y),
}
prediction_signature = 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(
sess2,
[tf.saved_model.tag_constants.SERVING],
{tf.saved_model.signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY: prediction_signature}
)
builder.save()
之后仿照样例模型,用docker运行这个模型就可以了,端口号、路径和模型名称什么的自定义即可,不冲突就行。
docker run -p 8502:8501 \
--mount type=bind,\source=/root/software/serving/tensorflow_serving/servables/tensorflow/testdata/face/face2,\target=/models/face2 \
-e MODEL_NAME=face2 -t tensorflow/serving &
一段测试代码,跟上面的有一点类似,具体什么参数看自己的模型
import requests
import numpy as np
import json
# json格式序列调整
class NumpyEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, np.ndarray):
return obj.tolist()
return json.JSONEncoder.default(self, obj)
feature=np.array(range(128))
param = {
"instances":[
#每一个大括号是一次请求,里面是每次请求的参数
{
"in":feature
}
]
}
param = json.dumps(param, cls=NumpyEncoder)
res = requests.post("http://localhost:8502/v1/models/face2:predict", data=param)
# 根据自己设定的返回数据读取
# softmax = json.loads(res2.text)["predictions"][0]
至此,大功告成。
PS:如果有多个模型需要部署,只需要修改本地端口号即可,不需要修改docker的端口号。(就是docker run命令的时候,只改第一个8501,不需要改第二个8501)