TensorFlow Serving是一個適用于深度學習模型的推理服務引擎,支持將TensorFlow標準的SavedModel格式的模型部署為在線服務,并支持模型熱更新與模型版本管理等功能。本文為您介紹如何使用鏡像部署的方式部署TensorFlow Serving模型服務。
前提準備
模型文件
使用TensorFlow Serving鏡像部署要求模型文件存儲在OSS,且模型文件的存儲目錄需滿足以下要求:
模型版本目錄:每個模型至少包含一個模型版本目錄,且必須以數字命名,作為模型版本號,數字越大版本號越新。
模型文件:模型版本目錄下存放導出的SavedModel格式的模型文件,服務會自動加載最大模型版本號目錄下的模型文件。
操作步驟如下:
在OSS存儲空間中創建模型存儲目錄(例:
oss://examplebucket/models/tf_serving/
),詳情請參見管理目錄。將模型文件上傳到步驟1創建的目錄(您可以下載本文案例TensorFlowServing模型文件使用),得到模型存儲目錄格式如下:
tf_serving ├── modelA │ └── 1 │ ├── saved_model.pb │ └── variables │ ├── variables.data-00000-of-00001 │ └── variables.index │ ├── modelB │ ├── 1 │ │ └── ... │ └── 2 │ └── ... │ └── modelC ├── 1 │ └── ... ├── 2 │ └── ... └── 3 └── ...
模型配置文件
通過配置文件的方式可以實現讓一個服務同時運行多個模型。如果您只需要部署單模型服務,可跳過本小節。
請根據如下說明創建配置文件并上傳到OSS(模型文件準備小節中給出的案例文件里已有一個模型配置文件model_config.pbtxt,您可直接使用或按需修改)。本案例中將模型配置文件上傳到目錄oss://examplebucket/models/tf_serving/
。
模型配置文件model_config.pbtxt內容示例如下:
model_config_list {
config {
name: 'modelA'
base_path: '/models/modelA/'
model_platform: 'tensorflow'
model_version_policy{
all: {}
}
}
config {
name: 'modelB'
base_path: '/models/modelB/'
model_platform: 'tensorflow'
model_version_policy{
specific {
versions: 1
versions: 2
}
}
version_labels {
key: 'stable'
value: 1
}
version_labels {
key: 'canary'
value: 2
}
}
config {
name: 'modelC'
base_path: '/models/modelC/'
model_platform: 'tensorflow'
model_version_policy{
latest {
num_versions: 2
}
}
}
}
其中關鍵配置說明如下:
參數 | 是否必選 | 描述 |
name | 否 | 自定義配置模型名稱。建議配置該參數,如果不配置模型名稱,則model_name為空,后續無法調用該模型服務。 |
base_path | 是 | 配置模型存儲目錄在實例中的路徑,后續部署服務時用于讀取模型文件。例如:掛載目錄為 |
model_version_policy | 否 | 表示模型版本加載策略。
|
version_labels | 否 | 為模型版本配置自定義標簽。如果沒有配置version_labels,那么就只能通過版本號來區分模型版本,請求路徑為: 如果設置了version_labels,那么就可以請求version label來指向指定的版本號 說明 標簽默認只能分配給已成功加載并啟動為服務的模型版本。若想要預先為尚未加載的模型版本分配標簽,需要在運行命令中設置啟動參數 |
部署服務
可以通過以下兩種方法來進行TensorFlow Serving的鏡像部署。
場景化模型部署:適用于基本場景部署。您只需配置幾個參數,即可一鍵部署TensorFlow Serving模型服務。
自定義模型部署:適用于在特定環境下運行的服務。您可以根據自身業務的具體需求來調整更多配置選項,從而實現更靈活的服務配置。
TensorFlow Serving模型服務支持配置8501和8500兩種端口。
8501:支持HTTP請求,在8501端口啟動HTTP或REST服務。
8500:支持gRPC請求,在8500端口啟動gRPC服務。
場景化模型部署默認使用8501端口,無法修改。如果您需要配置8500端口來使用gRPC服務請選擇自定義模型部署。
以下示例中單模型部署以部署modelA為例進行說明。
場景化模型部署
具體操作步驟如下:
登錄PAI控制臺,在頁面上方選擇目標地域,并在右側選擇目標工作空間,然后單擊進入EAS。
在模型在線服務(EAS)頁面,單擊部署服務。然后在場景化模型部署區域,單擊TFServing部署。
在TFServing部署頁面配置參數。關鍵參數說明如下,其他參數配置說明,請參見服務部署:控制臺。
參數
描述
部署方式
支持以下兩種部署方式:
標準模型部署:用于部署單模型服務。
配置文件部署:用于部署多模型服務。
模型配置
部署方式選擇標準模型部署時,您需要配置模型文件所在的OSS路徑。
部署方式選擇配置文件部署時,您需要配置以下參數:
OSS:選擇模型文件所在的OSS路徑。
掛載路徑:掛載服務實例中的目標路徑,用來讀取模型文件。
配置文件:選擇模型配置文件所在的OSS路徑。
參數配置示例:
參數
單模型示例(部署modelA)
多模型示例
服務名稱
modela_scene
multi_scene
部署方式
選擇標準模型部署。
選擇配置文件部署。
模型配置
OSS:
oss://examplebucket/models/tf_serving/modelA/
。OSS:
oss://examplebucket/models/tf_serving/
。掛載路徑:/models
配置文件:
oss://examplebucket/models/tf_serving/model_config.pbtxt
參數配置完成后,單擊部署。
自定義模型部署
具體操作步驟如下:
登錄PAI控制臺,在頁面上方選擇目標地域,并在右側選擇目標工作空間,然后單擊進入EAS。
單擊部署服務,然后在自定義模型部署區域,單擊自定義部署。
在自定義部署頁面配置參數,關鍵參數說明如下,其他參數配置說明,請參見服務部署:控制臺。
參數
描述
鏡像配置
在官方鏡像列表中選擇tensorflow-serving和對應的鏡像名稱。建議選擇最新版本。
說明如果服務需要使用GPU,則鏡像版本必須選擇x.xx.x-gpu。
模型配置
支持多種方式配置模型文件,這里采用OSS類型。
OSS:選擇模型文件所在的OSS路徑。
掛載路徑:掛載服務實例中的目標路徑,用來讀取模型文件。
運行命令
tensorflow-serving的啟動參數,選擇tensorflow-serving鏡像會自動加載命令:
/usr/bin/tf_serving_entrypoint.sh
,還需設置的參數說明如下。單模型部署啟動參數:
--model_name:模型名稱,用于服務請求中的URL。如果不配置,默認名稱為model。
--model_base_path:用于指定模型存儲目錄在實例中的路徑。如果不配置,默認路徑為
/models/model
。
多模型部署啟動參數:
--model_config_file:必選。用來指定模型配置文件。
--model_config_file_poll_wait_seconds:選填。如果您希望在服務啟動后修改模型配置文件的內容,需要配置輪詢模型文件的周期,單位為秒。服務會按照配置的時間定期讀取模型配置文件的內容。例如
--model_config_file_poll_wait_seconds=30
表示服務每隔30秒讀取一次模型配置文件內容。說明當模型服務讀取新的模型配置文件時,只會執行新配置文件中的內容。例如:舊配置文件中包含模型A,而新配置文件將模型A刪除并增加了模型B的配置,那么服務會卸載模型A并加載模型B。
--allow_version_labels_for_unavailable_models:選填。默認為false,如果您想預先為尚未加載的模型版本分配標簽,需要將該參數配置為true。例如
--allow_version_labels_for_unavailable_models=true
。
參數配置示例:
參數
單模型示例(部署modelA)
多模型示例
部署方式
選擇鏡像部署。
鏡像配置
選擇官方鏡像:tensorflow-serving>tensorflow-serving:2.14.1。
模型配置
模型類型選擇OSS。
OSS:
oss://examplebucket/models/tf_serving/
。掛載路徑:配置為
/models
。
運行命令
/usr/bin/tf_serving_entrypoint.sh --model_name=modelA --model_base_path=/models/modelA
/usr/bin/tf_serving_entrypoint.sh --model_config_file=/models/model_config.pbtxt --model_config_file_poll_wait_seconds=30 --allow_version_labels_for_unavailable_models=true
端口號默認為8501,服務將在8501端口啟動HTTP或REST服務,支持HTTP請求。若您需要該服務支持gRPC請求,需要執行以下操作:
環境信息中端口號修改為8500。
服務功能中打開啟用GRPC開關。
服務配置中添加以下配置
"networking": { "path": "/" }
單擊部署。
發送服務請求
根據服務部署時運行命令中配置的端口號,分別支持HTTP和gRPC兩種請求協議。下面給出modelA的服務請求示例。
測試數據準備
modelA是一個圖片分類模型,訓練數據集為Fashion-MNIST,樣本是28x28的灰度圖片,模型輸出是樣本屬于10個分類的概率值。為了測試方便,我們使用
[[[[1.0]] * 28] * 28]
作為modelA服務請求的測試數據。請求示例:
HTTP請求
端口號配置為8501,服務支持HTTP請求。下表總結了單模型和多模型部署情況下的HTTP請求路徑:
單模型
多模型
路徑格式:
<service_url>/v1/models/<model_name>:predict
其中:
場景化部署:<model_name> 不支持自定義配置,默認為model。
自定義部署:<model_name>為運行命令中配置的模型名稱,如果沒有配置,默認為model。
支持不指定版本和指定模型版本兩種請求方式,路徑格式如下:
不指定版本(默認加載最大版本):
<service_url>/v1/models/<model_name>:predict
指定模型版本:
<service_url>/v1/models/<model_name>/versions/<version_num>:predict
如果設置了version_labels:
/v1/models/<model name>/labels/<version label>:predict
其中,<model_name> 為模型配置文件中配置的模型名稱。
其中:<service_url> 是您部署的服務訪問地址。您可以在模型在線服務(EAS)頁面,單擊待調用服務服務方式列下的調用信息,在公網地址調用頁簽查看服務訪問地址。在使用控制臺在線調試的時候,頁面已自動填充該部分路徑。
以場景化部署單模型modelA為例,HTTP請求路徑為:
<service_url>/v1/models/model:predict
。下面以此為例為您具體說明如何通過控制臺發送服務請求和通過Python代碼發送服務請求:
通過控制臺發送服務請求
服務部署完成后,單擊服務操作列下的在線調試,在線調試請求參數中已填充<service_url>,在地址后增加路徑
/v1/models/model:predict
,Body中配置服務請求數據:{"signature_name": "serving_default", "instances": [[[[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]]]]}
參數配置完成后,單擊發送請求,輸出如下類似結果。
通過Python代碼發送服務請求
Python代碼示例如下:
from urllib import request import json # 請替換為你的服務訪問地址和Token。 # 您可以在推理服務列表的服務方式列單擊調用信息,在公網地址調用頁簽查看。 service_url = '<service_url>' token = '<test-token>' # 場景化單模型部署為 model,其他情況參照上面路徑說明表格 model_name = "model" url = "{}/v1/models/{}:predict".format(service_url, model_name) # 創建HTTP請求。 req = request.Request(url, method="POST") req.add_header('authorization', token) data = { 'signature_name': 'serving_default', 'instances': [[[[1.0]] * 28] * 28] } # 請求服務。 response = request.urlopen(req, data=json.dumps(data).encode('utf-8')).read() # 查看返回結果。 response = json.loads(response) print(response)
gRPC請求
端口號配置為8500,并添加gRPC相關配置后,服務支持發送gRPC請求。Python代碼示例如下:
import grpc import tensorflow as tf from tensorflow_serving.apis import predict_pb2 from tensorflow_serving.apis import prediction_service_pb2_grpc from tensorflow.core.framework import tensor_shape_pb2 # 服務訪問地址。格式參見下面的host參數說明。 host = "tf-serving-multi-grpc-test.166233998075****.cn-hangzhou.pai-eas.aliyuncs.com:80" # test-token替換為服務Token。您可以在公網地址調用頁簽查看Token。 token = "<test-token>" # 模型名稱。參見下面的name參數說明。 name = "<model_name>" signature_name = "serving_default" # 配置為模型版本號。每次只能對單個模型版本發送請求。 version = "<version_num>" # 創建gRPC請求。 request = predict_pb2.PredictRequest() request.model_spec.name = name request.model_spec.signature_name = signature_name request.model_spec.version.value = version request.inputs["keras_tensor"].CopyFrom(tf.make_tensor_proto([[[[1.0]] * 28] * 28])) # 請求服務。 channel = grpc.insecure_channel(host) stub = prediction_service_pb2_grpc.PredictionServiceStub(channel) metadata = (("authorization", token),) response, _ = stub.Predict.with_call(request, metadata=metadata) print(response)
其中關鍵參數配置如下:
參數
描述
host
需要配置為服務訪問地址,服務訪問地址需要省略
http://
并在末尾添加:80
。您可以在模型在線服務(EAS)頁面,單擊待調用服務服務方式列下的調用信息,在公網地址調用頁簽查看服務訪問地址。name
單模型發送gRPC請求
場景化部署:配置為model。
自定義部署:配置為運行命令中配置的模型名稱,如果沒有配置,默認為model。
多模型發送gRPC請求
配置為模型配置文件中配置的模型名稱。
version
配置為模型版本號。每次只能對單個模型版本發送請求。
metadata
配置為服務Token。您可以在公網地址調用頁簽查看Token。
相關文檔
如何基于Triton Server推理服務引擎部署EAS服務,請參見Triton Inference Server鏡像部署。
如何使用Modelscope、Huggingface鏡像將相應的開源模型部署為EAS服務,請參見Modelscope模型部署和HuggingFace鏡像部署。
您也可以開發自定義鏡像,使用自定義鏡像部署EAS服務。具體操作,請參見服務部署:自定義鏡像。