用Docker容器自帶的tensorflow serving部署模型對外服務

相信很多人和我一樣,在試圖安裝tensorflow serving的時候,翻遍了網上的博客和官網文檔,安裝都是以失敗而告終,我也是一樣,這個問題折磨了我兩個星期之久,都快放棄了。幸運的是在同事的建議下,我采用了一種迂回的策略安裝成功了。

?

我們采用的策略是:

? ? ? ? pull一個已經安裝好了tensorflow serving的docker鏡像,替換它自帶的一些模型為我們自己的模型。

?

步驟:

1、拉取帶tensorflow serving的docker鏡像,這樣我們服務器上就有了一個安裝了ModelServer的docker容器, 這個容器就可以看做一臺虛擬機,這個虛擬機上已經安裝好了tensorflow serving,環境有了,就可以用它來部署我們的模型了。注意這個拉取下來后不是直接放在當前目錄的,而是docker默認存儲的路徑,這個是個docker容器,和第2步clone下來的不是同一個東西

$docker pull tensorflow/serving

2、獲取例子模型:(當然,也可以直接用上面容器中自帶的例子),當然這里是直接拉取了tensorflow serving的源碼,源碼中有一些訓練好的例子模型

  1. $cd /root/software/
  2. $git clone https://github.com/tensorflow/serving

3、用第一步拉取的docker容器運行例子模型

第2步中clone下來的serving源碼中有這樣一個訓練好的例子模型,路徑為:

/root/software/serving/tensorflow_serving/servables/tensorflow/testdata/saved_model_half_plus_two_cpu

現在我們就要用第1步拉下來的docker容器來運行部署這個例子模型

  1. $docker run -p 8501:8501 \
  2. --mount type=bind,\
  3. source=/root/software/serving/tensorflow_serving/servables/tensorflow/testdata/saved_model_half_plus_two_cpu,\
  4. target=/models/half_plus_two \
  5. -e MODEL_NAME=half_plus_two -t tensorflow/serving &

參數說明:

  1. --mount: 表示要進行掛載
  2. source: 指定要運行部署的模型地址, 也就是掛載的源,這個是在宿主機上的模型目錄
  3. target: 這個是要掛載的目標位置,也就是掛載到docker容器中的哪個位置,這是docker容器中的目錄
  4. -t: 指定的是掛載到哪個容器
  5. -p: 指定主機到docker容器的端口映射
  6. docker run: 啟動這個容器并啟動模型服務(這里是如何同時啟動容器中的模型服務的還不太清楚)
  7. 綜合解釋:
  8. 將source目錄中的例子模型,掛載到-t指定的docker容器中的target目錄,并啟動

這步注意,如果執行報錯無法識別type=bind, 那應該是source的路徑有問題

4、調用這個服務,這里用的http接口

  1. $curl -d '{"instances": [1.0, 2.0, 5.0]}' \
  2. -X POST http://localhost:8501/v1/models/half_plus_two:predict

得到的結果如下:

{ "predictions": [2.5, 3.0, 4.5] }

這就表明服務已經部署成功了,當然你也可以用requests來模型上述http請求

5、查看啟動的這個模型的目錄的結構

我們可以看到啟動服務的命令有一個參數:

source=/root/software/serving/tensorflow_serving/servables/tensorflow/testdata/saved_model_half_plus_two_cpu

這實際就是模型的位置, 我們進入到這個目錄下(這個目錄基于自己pull時所在的目錄),可以看到里面是一個名為00000123的目錄,這實際是模型的版本,再進入到這個目錄下可以看到一個如下兩個文件:

saved_model.pb, variables

variable目錄下有如下兩個文件:

variables.data-00000-of-00001, variables.index

6、用自己的模型替換上述half_plus_two模型

我在和saved_model_half_plus_two_cpu模型同級的目錄下創建了一個文件夾,名為textcnnrnn, 這是我模型的名稱,然后

  1. $cd textcnnrnn
  2. $mkdir 00000123
  3. $cd 00000123
  4. $mkdir variables
  5. $cd variables

我一開始是直接用的我之前訓練好的模型放到了variables目錄下,我訓練好的模型包含如下幾個文件:

best_validation.data-00000-of-00001  best_validation.index  best_validation.meta  checkpoint

相信大家都看出來了,這個是用這種方式保存的:

  1. saver = tf.train.Saver()
  2. saver.save(sess=session, save_path=save_path)

于是我激動的去重新啟動我的模型,當然這里要修改模型的地址,我也把我的模型的名字改了下:

docker run -p 8501:8501 --mount source=/root/software/serving/tensorflow_serving/servables/tensorflow/testdata/textcnnrnn,type=bind,target=/models/find_lemma_category -e MODEL_NAME=find_lemma_category -t tensorflow/serving &

可是這個時候報錯了,做法不對。下面是正確的做法。

其實仔細比較我的模型的幾個文件和half_plus_two模型的下的文件的結構根本不一樣,怎么辦呢? 其實應該對模型的格式進行轉換。 代碼如下:

  1. # coding: utf-8
  2. from __future__ import print_function
  3. import pdb
  4. import time
  5. import os
  6. import tensorflow as tf
  7. import tensorflow.contrib.keras as kr
  8. from cnn_rnn_model import TCNNRNNConfig, TextCNNRNN
  9. save_path = 'model_saver/textcnnrnn/best_validation'
  10. try:
  11. bool(type(unicode))
  12. except NameError:
  13. unicode = str
  14. config = TCNNRNNConfig()
  15. def build_and_saved_wdl():
  16. model = TextCNNRNN(config) #我自己的模型結構是在這個類中定義的,基于自己的模型進行替換
  17. session = tf.Session()
  18. session.run(tf.global_variables_initializer())
  19. saver = tf.train.Saver()
  20. saver.restore(sess=session, save_path=save_path)
  21. # 將訓練好的模型保存在model_name下,版本為2,當然你的版本可以隨便寫
  22. builder = tf.saved_model.builder.SavedModelBuilder("./model_name/2")
  23. inputs = {
  24. #注意,這里是你預測模型的時候需要傳的參數,調用模型的時候,傳參必須和這里一致
  25. #這里的model.input_x和model.keep_prob就是模型里面定義的輸入placeholder
  26. "input_x": tf.saved_model.utils.build_tensor_info(model.input_x),
  27. "keep_prob": tf.saved_model.utils.build_tensor_info(model.keep_prob)
  28. }
  29. #model.y_pred_cls是模型的輸出, 預測的時候就是計算這個表達式
  30. output = {"output": tf.saved_model.utils.build_tensor_info(model.y_pred_cls)}
  31. prediction_signature = tf.saved_model.signature_def_utils.build_signature_def(
  32. inputs=inputs,
  33. outputs=output,
  34. method_name=tf.saved_model.signature_constants.PREDICT_METHOD_NAME
  35. )
  36. builder.add_meta_graph_and_variables(
  37. session,
  38. [tf.saved_model.tag_constants.SERVING],
  39. {tf.saved_model.signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY: prediction_signature}
  40. )
  41. builder.save()
  42. if __name__ == '__main__':
  43. build_and_saved_wdl()

執行后,會在當前目錄下生成一個名稱為./model_name/2的文件夾, 這個文件夾下的文件格式和halt_plus_two中的文件格式是一致的了,這下肯定沒錯了。

將./model_name/2文件夾下的內容拷貝到textcnnrnn/00000123目錄下即可。

重新啟動模型,這次啟動成功了,沒有報錯,說明我們的模型已經被識別成功。

7、調用模型

咋調啊?咋傳參數啊?懵逼,先看看調用自帶的模型怎么傳參數的吧:

  1. curl -d '{"instances": [1.0, 2.0, 5.0]}' \
  2. -X POST http://localhost:8501/v1/models/half_plus_two:predict

看樣子instances應該是參數的名字,于是我想看看tensorflow serving源碼里面是怎么解析這個參數的,所以我在源碼根目錄下全局搜索了這個關鍵字,在根目錄下搜索關鍵詞instances:

$find . -name '*.*' | xargs grep -l instances

可以找到一個名為json_tensor.h的文件,這個文件詳細介紹了不同的傳參的方式:

instances是一個list,list中每個元素是一個待預測實例,每個實例里面是所有參數的值, 所以參數按照這種方式構造就可以了。

這里json.dumps的時候可能會遇到一個序列化的錯誤,原因是json.dumps對于含numpy.array類型的數據無法序列化, 可以構造一個編碼器, 然后作為json.dumps參數:

  1. class NumpyEncoder(json.JSONEncoder):
  2. def default(self, obj):
  3. if isinstance(obj, np.ndarray):
  4. return obj.tolist()
  5. return json.JSONEncoder.default(self, obj)
  1. p_data = {"keep_prob": 1.0, "input_x": x_test[0]}
  2. param = {"instances": [p_data]}
  3. param = json.dumps(param, cls=NumpyEncoder)
  4. res = requests.post('http://localhost:8501/v1/models/find_lemma_category:predict', data=param)

這樣就大功告成了!

這里還有一個地方需要注意:其實我的模型Input_x本身是直接可以接收多個實例的,也就是上面我的參數x_test是多個實例構造的參數,但是直接傳入會出錯,所以我只能傳入一個實例x_test[0]。 如果想同時預測多個的話只能這樣構造參數:

  1. data1 = {"keep_prob": 1.0, "input_x": x_test[0]}
  2. data2 = {"keep_prob": 1.0, "input_x": x_test[1]}
  3. data3 = {"keep_prob": 1.0, "input_x": x_test[2]}
  4. param = {"instances": [data1, data2, data3]}
  5. param = json.dumps(param, cls=NumpyEncoder)
  6. res = requests.post('http://localhost:8501/v1/models/find_lemma_category:predict', data=param)

8、參數要預處理怎么辦?

假如我們需要在將參數輸入模型之前做一些預處理怎么辦?比如要對大段文本進行分詞等等。

解決辦法: 部署一個中轉服務,我采用的策略是用tornado再部署一個服務,這個服務負責對業務方傳輸過來的參數進行預處理,處理成模型需要的格式后,再傳輸給模型, 所以我的結構是這樣的:

業務方 ==> ?tornado服務(參數預處理) ==> 模型(tensorflow serving服務)

這里面的兩次遠程調用都是http協議。

?

參考地址:

? ??https://www.tensorflow.org/serving/docker

? ??https://www.jianshu.com/p/2fffd0e332bc

?

?

?

?

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/445122.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/445122.shtml
英文地址,請注明出處:http://en.pswp.cn/news/445122.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C資源

云風最近寫了一篇博客《C語言的前世今生》。作為長期使用C語言開發網絡游戲服務器的程序員,云風是有理由寫這樣一篇文字,不過還是感覺談的不夠深入,C語言在業界使用的現狀沒有怎么描寫,有些意猶未盡。在這里想比較系統的談談個人對…

學點數學(2)-特征函數

特征函數1.數列特征方程2.矩陣特征方程3.微分方程特征方程4.積分方程特征方程特征方程是為研究相應的數學對象而引入的一些等式,這些等式描述了特定對象的特性。依據研究的對象不同,特征方程包括數列特征方程、矩陣特征方程、微分方程特征方程、積分方程…

GCC如何產生core dump

先決條件1.安裝apport(automatically generate crash reports for debugging)2.修改/etc/security/limits.conf文件,使允許core dump,或者用ulimit -c unlimited設置core dump文件的大小為unlimited3.C/C的編譯開關-g(…

經典的進程同步問題

經典的進程同步問題 普通版:一類進程作為生產者,生產產品,生產的產品放入一個緩沖區,消費者從緩沖區中取出產品,需要保證生產者不可以向滿的緩沖區中添加產品,消費者不可以從空的緩沖區中取出產品。同一時刻…

面試題匯總---深度學習(圖像識別,NLP內容)

文章目錄1.基本概念1.1 為什么神經網絡中深度網絡的表現比廣度網絡表現好?1.2 推導BP算法1.3 什么是梯度消失和梯度爆炸?1.4 常用的激活函數有哪些?1.5 常用的參數更新方法有哪些?1.6 解決過擬合的方法?數據層面模型層…

Linux-2.6.25 TCPIP函數調用大致流程

Linux-2.6.25 TCPIP函數調用大致流程學習目的,隨手筆記。函數和文字說明會不斷補充更新。Changelog2008.10.08 最近找工作忙。暫時緩緩插口層系統調用sendsys_sendsys_sendtosendtosys_sendtosock_sendmsgsendmsgsys_sendmsgsock_sendmsgwritesys_writevfs_write…

Python(28)-文件,os模塊

文件1. 文件2. 文件的基本操作3. 讀取文件open()3.1 文件指針: 標記從哪一個位置開始讀取數據.3.2 文件的打開方式mode3.3 文件按行讀取3.3.1 readline()3.3.2 readlines()4.文件輸出f.write(),print()5.文件復制5.1 小文件復制(搬家)5.2 大文件復制&…

IOCP的程序

C代碼 #include <winsock2.h> #include <mswsock.h> #include <windows.h> #include <stdio.h> #include <stdlib.h> #include <assert.h> #include "vld.h" #pragma message("automatic link to ws2_32.lib and…

PaperNotes(3)-圖像分割-RCNN-FCN-Boxsup

圖像分割算法對比小結1.{基本概念}2.{R-CNN}2.1R-CNN 網絡結構選擇性搜索算法為什么選擇SVM作分類器邊框回歸2.2{R-CNN 訓練}2.3{R-CNN實驗結果}2.4{R-CNN語義分割}2.5{補充材料}2.5.1{R-CNN建議區域放縮}2.5.2{IOU閾值設置不一樣的原因}2.5.3{Bounding-box回歸修正}2.6{R-CNN存…

Python模塊(3)--PIL 簡易使用教程

PIL模塊-用與記1.圖片導入Image.open()2.圖像顯示.show()4.查看圖片屬性.format,.size,.mode3.圖像格式轉換.convert()4.圖像模式“L”&#xff0c;“RGB”,"CYMK"5. 圖片旋轉.rotate()旋轉方式1&#xff1a;旋轉不擴展旋轉方式2&#xff1a;旋轉擴展旋轉方式3&#…

日志級別 debug info warn eirror fatal

日志級別 debug info warn eirror fatal 軟件中總免不了要使用諸如 Log4net, Log4j, Tracer 等東東來寫日志&#xff0c;不管用什么&#xff0c;這些東東大多是大同小異的&#xff0c;一般都提供了這樣5個日志級別&#xff1a; Debug Info Warn Error Fatal一個等級比一個高&…

輸入輸出系統

I/O設備&#xff1a;輸入輸出和存儲功能的設備 I/O設備的分類 按傳輸的速度&#xff1a; 低速設備&#xff08;如鍵盤、鼠標、語音輸入輸出設備&#xff09; 中速設備&#xff08;如行式打印機、激光打印機等&#xff09; 高速設備&#xff08;如磁帶機、磁盤機、光盤機等&…

vue2源碼解析---v-model雙向數據綁定

什么是v-model v-model 是 Vue 中的一個指令&#xff0c;用于實現表單元素與 Vue 實例中數據的雙向綁定。這意味著當表單元素的值發生變化時&#xff0c;Vue 實例中的數據也會隨之更新 工作原理 生成ast樹 本質上是語法糖 結合了v-bind和v-on兩個指令 示例代碼 new Vue({e…

php收集的精典代碼

1. οncοntextmenu"window.event.return&#xff06;#118aluefalse" 將徹底屏蔽鼠標右鍵 <table border οncοntextmenureturn(false)><td>no</table> 可用于Table 2. <body onselectstart"return false"> 取消選取、防止復制…

python外卷(7)--glob

glob模塊1.glob.glob()2.對比os.listdir()glob是python自帶的一個操作文件的模塊&#xff0c;可用于查找 指定路徑 中 匹配的 文件。1.glob.glob() 下面是一個測試文件路徑&#xff1a; (base) pppp-System-Product-Name:~/Desktop/test_glob$ tree . ├── a │ ├── 1…

Sublime Text 2配置強大的IDE開發環境,運行java

Sublime Text 2是我無意中發現的、據說十分強大的、便捷的編輯器&#xff0c;許多程序員都投入到Sublime Text 2的懷抱中。 1 配置java開發環境的方法如下&#xff1a; 在jdk安裝目錄下的bin文件夾下新建一個bat格式的文件&#xff0c;文件命為javacexec.bat。 如果是在Wind…

thinkphp的快捷方法實例化對象

D、F、S、C、L、A、I 他們都在functions.php這個文件家 下面我分別說明一下他們的功能 D&#xff08;&#xff09; 加載Model類 M&#xff08;&#xff09; 加載Model類 A&#xff08;&#xff09; 加載Action類 L&#xff08;&#xff09; 獲取語言定義 C&#xff08;&#xf…

Python外卷(8)--pdist, squareform

pdist, squareform1.pdist, squareform使用例子2.通過矩陣的四則運算實現上述pdist, squareformscipy.spatial.distance 距離計算庫中有兩個函數&#xff1a;pdist, squareform&#xff0c;用于計算樣本對之間的歐式距離&#xff0c;并且將樣本間距離用方陣表示出來。&#xff…

模擬進程調度

功能 data.h #ifndef _Data_h_ #define _Data_h_#include <stdio.h> #include <stdlib.h> #include <string.h>#define ElemType PCB #define Status int #define OK 1 #define ERROR 0 #define TimeSlice 1 #define Infinity 10 //INT_MAX#define NAME_M…

gdb調試多進程和多線程命令

1. 默認設置下&#xff0c;在調試多進程程序時GDB只會調試主進程。但是GDB&#xff08;>V7.0&#xff09;支持多進程的 分別以及同時 調試&#xff0c;換句話說&#xff0c;GDB可以同時調試多個程序。只需要設置follow-fork-mode(默認值&#xff1a;parent)和detach-on-fork…