php做网站实例,玩具网站建设服务公司,陵水专业网站建设,空间域名主机网站模板前言
初步学习tensorflow serving的手写数字识别模型部署。包括简单的模型训练、保存、部署上线。因为对docker和网络不太熟悉#xff0c;可能会有部分错误#xff0c;但是看完博客#xff0c;能跑通整个流程。此博客将详细介绍流程#xff0c;但是不详细介绍每个流程的每…前言
初步学习tensorflow serving的手写数字识别模型部署。包括简单的模型训练、保存、部署上线。因为对docker和网络不太熟悉可能会有部分错误但是看完博客能跑通整个流程。此博客将详细介绍流程但是不详细介绍每个流程的每步的含义因为这些步骤不会随着任务的不同而发生太大改变。在后续博客中可能会精细介绍每一步的含义。
国际惯例参考博客
tensorflow官方文档低阶API保存和恢复
tensorflow官方文档tensorflow serving
tensorflow github案例mnist和resnet
Tensorflow SavedModel模型的保存与加载
如何用TF Serving部署TensorFlow模型
Tensorflow Serving | Tensorflow Serving
Tensorflow使用SavedModel格式模型
我们给你推荐一种TensorFlow模型格式
使用 TensorFlow Serving 和 Docker 快速部署机器学习服务
如何将TensorFlow Serving的性能提高超过70%
模型构建
跟之前的博客一样简单搭建一个卷积网络输入数据是mnist还有损失函数和评估函数:
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_datasteps 1000
batch_size 100
mnist input_data.read_data_sets(./mnist_dataset,one_hotTrue)def conv_network(x):x tf.reshape(x,[-1,28,28,1])# 第一层卷积conv1 tf.layers.conv2d(inputsx,filters32,kernel_size[5,5],activationtf.nn.relu)conv1 tf.layers.max_pooling2d(conv1,pool_size[2,2],strides[2,2])#第二层卷积conv2 tf.layers.conv2d(inputsconv1,filters64,kernel_size[3,3],activationtf.nn.relu)conv2 tf.layers.max_pooling2d(conv2,pool_size[2,2],strides[2,2])#第三层卷积conv3 tf.layers.conv2d(inputsconv2,filters32,kernel_size[3,3],activationtf.nn.relu)conv3 tf.layers.max_pooling2d(inputsconv3,pool_size[2,2],strides[2,2])#全连接fc1 tf.layers.flatten(conv3)fc1 tf.layers.dense(fc1,500,activationtf.nn.relu)#输出分类fc2 tf.layers.dense(fc1,10)return fc2#输入输出容器
input_x tf.placeholder(dtypetf.float32,shape[None,28*28],nameX)
input_y tf.placeholder(dtypetf.int32,shape[None,10])#损失函数
model conv_network(input_x)
logit_loss tf.nn.softmax_cross_entropy_with_logits_v2(labelsinput_y,logitsmodel)
optimize tf.train.AdamOptimizer(0.001).minimize(logit_loss)
#评估
pred_equal tf.equal(tf.arg_max(model,1),tf.arg_max(input_y,1))
accuracy tf.reduce_mean(tf.cast(pred_equal,tf.float32))模型保存
传统方法checkpoint
这部分就不细说了我们之前训练模型基本都是这个方法
init tf.global_variables_initializer()
saver tf. train.Saver(max_to_keep1)
tf.add_to_collection(pred,model)with tf.Session() as sess:sess.run(init)for step in range(steps):data_x,data_y mnist.train.next_batch(batch_size)test_x,test_y mnist.test.next_batch(1000) train_acc sess.run(optimize,feed_dict{input_x:data_x,input_y:data_y}) if(step % 1000 or step1):accuracy_val sess.run(accuracy,feed_dict{input_x:data_x,input_y:data_y})print(steps:{0},val_loss:{1}.format(step,accuracy_val))#保存模型print(train finished!)saver.save(sess,./model/cnn)主要就是利用tf.train.Saver保存训练好的模型
为tesorflow serving准备的模型保存方法
第一步准备好模型需要保存的位置以及版本控制
model_version 1 #版本控制
export_path_base /tmp/cnn_mnist
export_path os.path.join(tf.compat.as_bytes(export_path_base),tf.compat.as_bytes(str(model_version)))
print(Exporting trained model to,export_path)
builder tf.saved_model.builder.SavedModelBuilder(export_path)tensor_info_x tf.saved_model.utils.build_tensor_info(input_x)
tensor_info_y tf.saved_model.utils.build_tensor_info(model)
prediction_signature (tf.saved_model.signature_def_utils.build_signature_def(inputs{images:tensor_info_x},outputs{scores:tensor_info_y},method_name tf.saved_model.signature_constants.PREDICT_METHOD_NAME))此处注意如果你的export_path_base/model_version目录存在将会报错因为tensorflow serving有一个有点就是在更新模型的时候无需停止服务服务是根据版本来控制的所以每次训练都是一个新版本。而且这个模型最好是绝对路径因为后续部署服务的时候模型不能是相对路径。
错误提示
AssertionError: Export directory already exists. Please specify a different export directory: b/tmp/cnn_mnist/1第二步将输入输出打包起来方便从客户端接收参数
prediction_signature (tf.saved_model.signature_def_utils.build_signature_def(inputs{images:tensor_info_x},outputs{scores:tensor_info_y},method_name tf.saved_model.signature_constants.PREDICT_METHOD_NAME))注意这里有个method_name, 这个需要用tf.saved_model.signature_constants.里面的一系列NAME因为后续客户端传递给服务端的请求是json格式而predcit、regress、classify任务的json格式有区别具体格式看这里当然后面也会讲到
第三步就是在Session中保存模型了
#训练与保存
with tf.Session() as sess:sess.run(tf.global_variables_initializer())for step in range(steps):data_x,data_y mnist.train.next_batch(batch_size)test_x,test_y mnist.test.next_batch(1000) train_acc sess.run(optimize,feed_dict{input_x:data_x,input_y:data_y}) if(step % 1000 or step1):accuracy_val sess.run(accuracy,feed_dict{input_x:data_x,input_y:data_y})print(steps:{0},val_loss:{1}.format(step,accuracy_val))#保存模型 builder.add_meta_graph_and_variables(sess,[tf.saved_model.tag_constants.SERVING],signature_def_map {predict_images:prediction_signature},main_op tf.tables_initializer(),strip_default_attrs True)builder.save()print(Done exporting)这一步官方文档有详细介绍具体参数的使用没仔细看目前只需要前面三个必须传入sess、tag、signature_def_map重点是将上面定义的包含输入输出与任务种类的prediction_signature传进来。给个名字predict_images是为了后续调用服务的时候说明我们要调用哪个服务所以这个signature_def_map理论上应该可以包含多个任务接口而官方例子也有相关操作 builder.add_meta_graph_and_variables(sess, [tf.saved_model.tag_constants.SERVING],signature_def_map{predict_images:prediction_signature,tf.saved_model.signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY:classification_signature,},main_optf.tables_initializer(),strip_default_attrsTrue)至此为tensorflow serving提供的模型文件是如何训练和保存的已经介绍完毕在下一篇博客应该会探索如何将训练好的checkpoint转换为tensorflow serving可使用的模型文件。
通过docker部署模型
安装docker的方法在这里能找到或者docker官方文档我当时好像就一句话搞定
sudo apt install docker.io因为我以前没装过docker服务器上用过一丢丢。
tensorflow serving镜像
首先拉取tensorflow的镜像
docker pull tensorflow/serving有时候由于环境限制可以从别人pull好的镜像中恢复镜像的导出和导入可参考此处主要用到了
有镜像的电脑导出镜像
docker save 582a4 tensorflow_serving.tar其中582a4是用docker images查看的tensorflow/serving的ID。
无镜像的电脑导入镜像
docker load tensorflow_serving.tar 通常导入以后REPOSITORY和TAG是none最好给个名区分
docker tag 91abe tensorflow/serving:latest这里我备用了一份tensorflow/serving镜像
链接: https://pan.baidu.com/s/1l_ZGVkRKcP4HgSKxGgekRA 提取码: ewqv
启动在线服务
方法一
docker run -p 9500:8500 -p:9501:8501 \
--mount typebind,source/tmp/cnn_mnist,target/models/cnn_mnist \
-e MODEL_NAMEcnn_mnist -t tensorflow/serving这句话的意思就是 启动docker容器container 实现gRPC和REST端口到主机端口的映射注意port1:port2前者是主机端口后者是tensorflow serving docker的gRPC和REST端口。主机端口port1可以随便改只要没被占用但是tensorflow serving docker的两个端口固定不能动。
终端通过sudo netstat -nap可以看到tcp6中开启了两个端口分别就是9500和9501。
运行容器后的最后一部分输出是
2019-09-03 11:21:48.489776: I tensorflow_serving/servables/tensorflow/saved_model_warmup.cc:103] No warmup data file found at /models/cnn_mnist/1/assets.extra/tf_serving_warmup_requests
2019-09-03 11:21:48.489938: I tensorflow_serving/core/loader_harness.cc:86] Successfully loaded servable version {name: cnn_mnist version: 1}
2019-09-03 11:21:48.504477: I tensorflow_serving/model_servers/server.cc:324] Running gRPC ModelServer at 0.0.0.0:8500 ...
[warn] getaddrinfo: address family for nodename not supported
2019-09-03 11:21:48.519991: I tensorflow_serving/model_servers/server.cc:344] Exporting HTTP/REST API at:localhost:8501 ...
[evhttp_server.cc : 239] RAW: Entering the event loop ...可以发现服务端自动查找新模型同事给出了gRPC和REST的端口但是这连个端口貌似用不了难道是因为我们做映射了后面所有的访问无论是用docker的ip还是用host的ip一律通过ip:9500/9501接收请求。
方法二
docker run -t --rm -p 9500:8500 -p:9501:8501 \-v /tmp/cnn_mnist:/models/cnn_mnist \-e MODEL_NAMEcnn_mnist \tensorflow/serving其实和上面一样只不过对docker的用法不同而已。
如果对docker比较熟悉可以两种方法都记住不熟悉的话熟记一种方法就行了。
测试服务是否开通
下面的dockerip与hostip分别为ifconfig -a查出来的docker和host的ip 测试1: 输入curl http://localhost:8501/v1/models/cnn_mnist 输出curl: (7) Failed to connect to localhost port 8501: 拒绝连接 测试2 输入curl http://localhost:8500/v1/models/cnn_mnist 输出curl: (7) Failed to connect to localhost port 8500: 拒绝连接 测试3 输入curl http://dockerip:9500/v1/models/cnn_mnist 输出 Warning: Binary output can mess up your terminal. Use --output - to tell
Warning: curl to output it to your terminal anyway, or consider --output
Warning: FILE to save to a file.说明没有拒绝连接 测试4 输入curl http://hostip:9500/v1/models/cnn_mnist 输出 Warning: Binary output can mess up your terminal. Use --output - to tell
Warning: curl to output it to your terminal anyway, or consider --output
Warning: FILE to save to a file.说明没有拒绝连接 测试5 输入curl http://dockerip:9501/v1/models/cnn_mnist 输出 {model_version_status: [{version: 1,state: AVAILABLE,status: {error_code: OK,error_message: }}]
}没拒绝连接 测试6 输入curl http://hostip:9501/v1/models/cnn_mnist 输出 {model_version_status: [{version: 1,state: AVAILABLE,status: {error_code: OK,error_message: }}]
}暂时测试这几种情况其余组合自己可以测试看看如果拒绝连接那就说明ip和对应接口组合不通无法调用服务。
调用模型
上面说过了tensorflow serving有两类端口gRPC和REST API关于这两个区别可以查看这里下面分别讲解tensorflow serving中分别怎么请求和解析返回数据。
注意手写数字识别模型接受的是$ (None,784) $的向量
使用gRPC
引入必要包
import argparse
import tensorflow as tf
from tensorflow_serving.apis import predict_pb2,prediction_service_pb2_grpc
import grpc
import numpy as np
import cv2定义入口接收参数
parser argparse.ArgumentParser(descriptionmnist recognization client)
parser.add_argument(--host,default0.0.0.0,helpserve host)
parser.add_argument(--port,default9000,helpserve port)
parser.add_argument(--image,default,helpimage path)
FLAGS parser.parse_args()所以用户需要输入的都有ip、端口、输入图像
读取图像:
img cv2.imread(FLAGS.image,cv2.IMREAD_GRAYSCALE)
img cv2.resize(img,(28,28))
_,img cv2.threshold(img,250,255,cv2.THRESH_BINARY)
img np.array(img,dtypefloat32)
img img.reshape((28*28))
print(img.shape) #(784,)连接服务
server FLAGS.host : FLAGS.port
channel grpc.insecure_channel(server)
stub prediction_service_pb2_grpc.PredictionServiceStub(channel)请求服务
request predict_pb2.PredictRequest()
request.model_spec.name cnn_mnist
request.model_spec.signature_name predict_images
request.inputs[images].CopyFrom(tf.contrib.util.make_tensor_proto(img,shape[1,28*28]))
result stub.Predict(request,10.0)【注】
这里有prediction_service_pb2_grpc和predict_pb2那么是否有classify和regress对应库呢后面学习的时候再看。还有就是因为模型接收的是tensor所以得用tf.contrib.util.make_tensor_proto转换
解析请求
scoresresult.outputs[scores].float_val
pred_label np.argmax(scores)
print(pred_label,pred_label)【注】C的解析方法戳这里python的解析方法戳这里
运行测试
在终端中执行
python serving_test_grpc.py --host 127.0.0.1 --port 9500 --image ./test_image/6.png这里面的host换成docker或者主机的ipport换成你上面开启的端口。
使用REST
与gRPC区别很大需要用json作为请求的输入格式具体格式查阅这里我们使用predict API中的格式
{// (Optional) Serving signature to use.// If unspecifed default serving signature is used.signature_name: string,// Input Tensors in row (instances) or columnar (inputs) format.// A request can have either of them but NOT both.instances: value|(nested)list|list-of-objectsinputs: value|(nested)list|object
}引入相关包
import requests
import numpy as np
import cv2读取数据注意最后转换为(1,784)(1,784)(1,784)的list
image_path./test_image/2.png
img cv2.imread(image_path,cv2.IMREAD_GRAYSCALE)
img cv2.resize(img,(28,28))
_,img cv2.threshold(img,250,255,cv2.THRESH_BINARY)
img np.array(img,dtypefloat32)
img img.reshape((28*28))
img img[np.newaxis,:]
img img.tolist()用json格式化请求此处一定要严格按照下面的语句书写不然请求很容易失败
predict_request{signature_name: predict_images, instances:[{images:%s}] } %img请求失败时提示
requests.exceptions.HTTPError: 400 Client Error: Bad Request for url: http://127.0.0.1:9501/v1/models/cnn_mnist:predict如果提示这个bad request不要问为什么问就是你写错json请求了。
发送请求与接收回复以及解析
response requests.post(SERVER_URL, datapredict_request)
response.raise_for_status()
prediction response.json()[predictions][0]
print(label:,np.argmax(prediction))用response.elapsed.total_seconds()可以返回时间,用于测试效率.
使用tensorflow_model_server启动服务
安装
按照官方文档走: 第一步 echo deb [archamd64] http://storage.googleapis.com/tensorflow-serving-apt stable tensorflow-model-server tensorflow-model-server-universal | sudo tee /etc/apt/sources.list.d/tensorflow-serving.list \
curl https://storage.googleapis.com/tensorflow-serving-apt/tensorflow-serving.release.pub.gpg | sudo apt-key add -第二步 apt-get update apt-get install tensorflow-model-server第三步 apt-get upgrade tensorflow-model-server这个是在线方法还有一个离线方法我就不写了戳这里就行听说离线编译方法的成功率有点低。以后有机会再试试。
启动服务
一条命令搞定
tensorflow_model_server --port9500 --rest_api_port9501 \--model_namecnn_mnist --model_base_path/tmp/cnn_mnist就是直接开启gRPC端口为9500以及开启REST端口为9501剩下的请求服务与上面的docker教程一模一样。
有些小问题总结 端口占用 有时候提示端口被占用 如果使用docker的方法启动服务可以使用docker ps看启动的服务占用的端口如果有就用docker kill CONTAINER_ID 如果使用tensorflow_model_server启动服务使用netstat -nap查找端口被谁占用然后kill -9 PID 重启docker容器 当你kill掉docker里面的容器时并非移除了该容器可以通过docker ps -a查看所有容器包括关闭容器当你再次启动服务的时候没必要去执行docker run .....的那个脚本直接docker start CONTAINER_ID即可。 官方有个比较好的例子是调用resnet作为分类服务的 模型下载方法 #https://storage.googleapis.com/download.tensorflow.org/models/official/20181001_resnet/savedmodels/resnet_v2_fp32_savedmodel_NHWC_jpg.tar.gz
mkdir /tmp/resnet
curl -s https://storage.googleapis.com/download.tensorflow.org/models/official/20181001_resnet/savedmodels/resnet_v2_fp32_savedmodel_NHWC_jpg.tar.gz | tar --strip-components2 -C /tmp/resnet -xvz网盘下载 链接: https://pan.baidu.com/s/1Kyh8sGggdKld4u1wuQSAbA 提取码: 4k3z 服务启动方法
docker run -p 8500:8500 -p 8501:8501 \
--mount typebind,source/tmp/resnet,target/models/resnet \
-e MODEL_NAMEresnet -t tensorflow/serving检查模型的输入输出 saved_model_cli show --dir /tmp/cnn_mnist/1/ --all输出 signature_def[predict_images]:The given SavedModel SignatureDef contains the following input(s):inputs[images] tensor_info:dtype: DT_FLOATshape: (-1, 784)name: X:0The given SavedModel SignatureDef contains the following output(s):outputs[scores] tensor_info:dtype: DT_FLOATshape: (-1, 10)name: dense_1/BiasAdd:0Method name is: tensorflow/serving/predict线上调用 如果用其他ip或者电脑调用模型请求的ip必须是host而非localhost
后记
这里只是一个初步入门后续会更进一步了解其他功能。
本文所有代码打包下载
链接: https://pan.baidu.com/s/1MOUnU-sUAxfOjAHSPDKvkA 提取码: sa88
里面包含我调试的脚本懒得剔除了有兴趣慢慢看