日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【TensorFlow-serving】初步学习模型部署

發(fā)布時間:2023/12/13 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【TensorFlow-serving】初步学习模型部署 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

初步學習tensorflow serving的手寫數(shù)字識別模型部署。包括簡單的模型訓練、保存、部署上線。因為對docker和網(wǎng)絡不太熟悉,可能會有部分錯誤,但是看完博客,能跑通整個流程。此博客將詳細介紹流程,但是不詳細介紹每個流程的每步的含義,因為這些步驟不會隨著任務的不同而發(fā)生太大改變。在后續(xù)博客中可能會精細介紹每一步的含義。

國際慣例,參考博客:

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%?

模型構(gòu)建

跟之前的博客一樣,簡單搭建一個卷積網(wǎng)絡,輸入數(shù)據(jù)是mnist,還有損失函數(shù)和評估函數(shù):

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_hot=True)def conv_network(x):x = tf.reshape(x,[-1,28,28,1])# 第一層卷積conv1 = tf.layers.conv2d(inputs=x,filters=32,kernel_size=[5,5],activation=tf.nn.relu)conv1 = tf.layers.max_pooling2d(conv1,pool_size=[2,2],strides=[2,2])#第二層卷積conv2 = tf.layers.conv2d(inputs=conv1,filters=64,kernel_size=[3,3],activation=tf.nn.relu)conv2 = tf.layers.max_pooling2d(conv2,pool_size=[2,2],strides=[2,2])#第三層卷積conv3 = tf.layers.conv2d(inputs=conv2,filters=32,kernel_size=[3,3],activation=tf.nn.relu)conv3 = tf.layers.max_pooling2d(inputs=conv3,pool_size=[2,2],strides=[2,2])#全連接fc1 = tf.layers.flatten(conv3)fc1 = tf.layers.dense(fc1,500,activation=tf.nn.relu)#輸出分類fc2 = tf.layers.dense(fc1,10)return fc2#輸入輸出容器 input_x = tf.placeholder(dtype=tf.float32,shape=[None,28*28],name='X') input_y = tf.placeholder(dtype=tf.int32,shape=[None,10])#損失函數(shù) model = conv_network(input_x) logit_loss = tf.nn.softmax_cross_entropy_with_logits_v2(labels=input_y,logits=model) 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))

模型保存

傳統(tǒng)方法checkpoint

這部分就不細說了,我們之前訓練模型基本都是這個方法:

init = tf.global_variables_initializer() saver = tf. train.Saver(max_to_keep=1) 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 % 100==0 or step==1):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有一個有點就是在更新模型的時候,無需停止服務,服務是根據(jù)版本來控制的,所以每次訓練都是一個新版本。而且這個模型最好是絕對路徑,因為后續(xù)部署服務的時候,模型不能是相對路徑。

錯誤提示:

AssertionError: Export directory already exists. Please specify a different export directory: b'/tmp/cnn_mnist/1'

第二步:將輸入輸出打包起來,方便從客戶端接收參數(shù)

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,因為后續(xù)客戶端傳遞給服務端的請求是json格式,而predcit、regress、classify任務的json格式有區(qū)別,具體格式看這里,當然后面也會講到

第三步:就是在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 % 100==0 or step==1):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')

這一步,官方文檔有詳細介紹,具體參數(shù)的使用沒仔細看,目前只需要前面三個必須傳入sess、tag、signature_def_map,重點是將上面定義的包含輸入輸出與任務種類的prediction_signature傳進來。給個名字predict_images是為了后續(xù)調(diào)用服務的時候,說明我們要調(diào)用哪個服務,所以這個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_op=tf.tables_initializer(),strip_default_attrs=True)

至此,為tensorflow serving提供的模型文件是如何訓練和保存的已經(jīng)介紹完畢,在下一篇博客應該會探索如何將訓練好的checkpoint轉(zhuǎn)換為tensorflow serving可使用的模型文件。

通過docker部署模型

安裝docker的方法在這里能找到,或者docker官方文檔我當時好像就一句話搞定:

sudo apt install docker.io

因為我以前沒裝過docker,服務器上用過一丟丟。

tensorflow serving鏡像

首先拉取tensorflow的鏡像:

docker pull tensorflow/serving

有時候由于環(huán)境限制,可以從別人pull好的鏡像中恢復,鏡像的導出和導入可參考此處,主要用到了:

有鏡像的電腦導出鏡像:

docker save 582a4 > tensorflow_serving.tar

其中582a4是用docker images查看的tensorflow/serving的ID。

無鏡像的電腦導入鏡像:

docker load < tensorflow_serving.tar

通常導入以后,REPOSITORY和TAG是none,最好給個名區(qū)分:

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 type=bind,source=/tmp/cnn_mnist,target=/models/cnn_mnist \ -e MODEL_NAME=cnn_mnist -t tensorflow/serving

這句話的意思就是:

  • 啟動docker容器container

  • 實現(xiàn)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 ...

可以發(fā)現(xiàn),服務端自動查找新模型,同事給出了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_NAME=cnn_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和對應接口組合不通,無法調(diào)用服務。

調(diào)用模型

上面說過了,tensorflow serving有兩類端口:gRPC和REST API,關于這兩個區(qū)別,可以查看這里,下面分別講解tensorflow serving中分別怎么請求和解析返回數(shù)據(jù)。

注意手寫數(shù)字識別模型接受的是$ (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

定義入口接收參數(shù):

parser = argparse.ArgumentParser(description='mnist recognization client') parser.add_argument('--host',default='0.0.0.0',help='serve host') parser.add_argument('--port',default='9000',help='serve port') parser.add_argument('--image',default='',help='image 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,dtype='float32') 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轉(zhuǎn)換

解析請求

scores=result.outputs['scores'].float_val pred_label = np.argmax(scores) print('pred_label',pred_label)

【注】C++的解析方法戳這里,python的解析方法戳這里

運行測試

在終端中執(zhí)行:

python serving_test_grpc.py --host '127.0.0.1' --port '9500' --image './test_image/6.png'

這里面的host換成docker或者主機的ip,port換成你上面開啟的端口。

使用REST

與gRPC區(qū)別很大,需要用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-objects>"inputs": <value>|<(nested)list>|<object> }

引入相關包:

import requests import numpy as np import cv2

讀取數(shù)據(jù):注意最后轉(zhuǎn)換為(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,dtype='float32') 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請求了。

發(fā)送請求與接收回復以及解析

response = requests.post(SERVER_URL, data=predict_request) response.raise_for_status() prediction = response.json()['predictions'][0] print('label:',np.argmax(prediction))

用response.elapsed.total_seconds()可以返回時間,用于測試效率.

使用tensorflow_model_server啟動服務

安裝

按照官方文檔走:

  • 第一步

    echo "deb [arch=amd64] 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 --port=9500 --rest_api_port=9501 \--model_name=cnn_mnist --model_base_path=/tmp/cnn_mnist

就是直接開啟gRPC端口為9500以及開啟REST端口為9501,剩下的請求服務與上面的docker教程一模一樣。

有些小問題總結(jié)

  • 端口占用

    有時候提示端口被占用

    如果使用docker的方法啟動服務,可以使用docker ps看啟動的服務占用的端口,如果有,就用docker kill CONTAINER_ID

    如果使用tensorflow_model_server啟動服務,使用netstat -nap查找端口被誰占用,然后kill -9 PID

  • 重啟docker容器
    當你kill掉docker里面的容器時,并非移除了該容器,可以通過docker ps -a查看所有容器,包括關閉容器,當你再次啟動服務的時候,沒必要去執(zhí)行docker run .....的那個腳本,直接docker start CONTAINER_ID即可。

  • 官方有個比較好的例子,是調(diào)用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-components=2 -C /tmp/resnet -xvz

網(wǎng)盤下載:
鏈接: https://pan.baidu.com/s/1Kyh8sGggdKld4u1wuQSAbA 提取碼: 4k3z
服務啟動方法:

docker run -p 8500:8500 -p 8501:8501 \ --mount type=bind,source=/tmp/resnet,target=/models/resnet \ -e MODEL_NAME=resnet -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
  • 線上調(diào)用
    如果用其他ip或者電腦調(diào)用模型,請求的ip必須是host,而非localhost

后記

這里只是一個初步入門,后續(xù)會更進一步了解其他功能。

本文所有代碼打包下載:

鏈接: https://pan.baidu.com/s/1MOUnU-sUAxfOjAHSPDKvkA 提取碼: sa88

里面包含我調(diào)試的腳本,懶得剔除了,有興趣慢慢看

總結(jié)

以上是生活随笔為你收集整理的【TensorFlow-serving】初步学习模型部署的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。