Triton Inference Server是一個適用於深度學習與機器學習模型的推理服務引擎,支援將TensorRT、TensorFlow、PyTorch或ONNX等多種AI架構的模型部署為線上推理服務,並支援多模型管理、自訂backend等功能。本文為您介紹如何通過鏡像部署的方式部署Triton Inference Server模型服務。
部署服務:單模型
1. 準備模型與設定檔
在OSS儲存空間中建立模型儲存目錄,並根據模型儲存目錄格式要求配置模型檔案與設定檔。具體操作請參見管理目錄。
每個模型目錄下都至少包含一個模型版本目錄和一個模型設定檔:
模型版本目錄:包含模型檔案,且必須以數字命名,作為模型版本號碼,數字越大版本越新。
模型設定檔:用於提供模型的基礎資訊,通常命名為
config.pbtxt。
假設模型儲存目錄在oss://examplebucket/models/triton/路徑下,模型儲存目錄的格式如下:
triton
└──resnet50_pt
├── 1
│ └── model.pt
├── 2
│ └── model.pt
├── 3
│ └── model.pt
└── config.pbtxt其中:config.pbtxt 為設定檔,檔案內容樣本如下:
name: "resnet50_pt"
platform: "pytorch_libtorch"
max_batch_size: 128
input [
{
name: "INPUT__0"
data_type: TYPE_FP32
dims: [ 3, -1, -1 ]
}
]
output [
{
name: "OUTPUT__0"
data_type: TYPE_FP32
dims: [ 1000 ]
}
]
# 使用GPU推理
# instance_group [
# {
# kind: KIND_GPU
# }
# ]
# 模型版本配置
# version_policy: { all { }}
# version_policy: { latest: { num_versions: 2}}
# version_policy: { specific: { versions: [1,3]}}config.pbtxt檔案中關鍵配置說明如下:
參數 | 是否必選 | 描述 |
name | 否 | 預設為模型儲存目錄名。如果指定了名稱,也必須與模型儲存目錄名稱保持一致。 |
platform/backend | 是 | platform與backend至少配置一項:
|
max_batch_size | 是 | 用於指定模型請求批處理的最大數量,若不開啟批處理功能,則將該項設定為0。 |
input | 是 | 用於指定以下屬性:
|
output | 是 | 用於指定以下屬性:
|
instance_group | 否 | 當資源配置中有GPU資源時,預設使用GPU進行模型推理,否則預設使用CPU。您也可以通過配置instance_group參數,來顯式指定模型推理使用的資源,配置格式如下: 其中kind可配置為KIND_GPU或KIND_CPU。 |
version_policy | 否 | 用於指定模型版本,配置樣本如下:
|
2. 部署Triton Inference Server服務
登入PAI控制台,在頁面上方選擇目標地區,並在右側選擇目標工作空間,然後單擊進入EAS。
在模型線上服務(EAS)頁面,單擊部署服務,然後在情境化模型部署地區,單擊Triton部署。
在Triton部署頁面,配置以下關鍵參數,其他參數配置說明,請參見自訂部署。
參數
描述
服務名稱
自訂佈建服務名稱。
模型配置
在本方案中,配置類型選擇Object Storage Service,將OSS配置為步驟1中已準備的模型所在的OSS儲存路徑,例如
oss://example/models/triton/。(可選)啟用gRPC。預設情況下,服務僅在
8000連接埠上開啟HTTP服務。如需支援gRPC調用,單擊頁面右側的切換為自訂部署,進行如下修改:修改環境資訊地區的連接埠號碼為8001。
在下啟用GRPC。
參數配置完成後,單擊部署。
部署服務:多模型
EAS部署多模型服務的方式與部署單模型服務相同,您只需建立如下所示的多個模型儲存目錄即可,服務會載入所有的模型,並部署在同一個服務中。具體部署方式請參見部署服務:單模型。
triton
├── resnet50_pt
| ├── 1
| │ └── model.pt
| └── config.pbtxt
├── densenet_onnx
| ├── 1
| │ └── model.onnx
| └── config.pbtxt
└── mnist_savedmodel
├── 1
│ └── model.savedmodel
│ ├── saved_model.pb
| └── variables
| ├── variables.data-00000-of-00001
| └── variables.index
└── config.pbtxt部署服務:使用backend
backend是模型推理計算的具體實現部分,它既可以調用現有的模型架構(如TensorRT、ONNX Runtime、PyTorch、TensorFlow等),也可以自訂模型推理邏輯(如模型預先處理、後處理)。
backend支援 C++、Python兩種語言,與C++相比, Python使用起來更加靈活方便,因此以下內容主要介紹Python backend的使用方式。
1. 更新模型與設定檔
以PyTorch為例,使用Python backend自訂模型的計算邏輯,模型目錄結構樣本如下:
resnet50_pt
├── 1
│ ├── model.pt
│ └── model.py
└── config.pbtxt與常規的模型目錄結構相比,backend需要在模型版本目錄下新增一個model.py檔案,用於自訂模型的推理邏輯,並且設定檔config.pbtxt內容也需要做相應修改。
自訂推理邏輯
model.py檔案需要定義名為TritonPythonModel的類,並實現initialize、execute、finalize三個關鍵的介面函數。該檔案內容樣本如下:
import json import os import torch from torch.utils.dlpack import from_dlpack, to_dlpack import triton_python_backend_utils as pb_utils class TritonPythonModel: """必須以 "TritonPythonModel" 為類名""" def initialize(self, args): """ 初始化函數,可選實現,在載入模型時被調用一次,可用於初始化與模型屬性、模型配置相關的資訊。 Parameters ---------- args : 字典類型,其中keys和values都為string 類型,具體包括: * model_config:JSON格式模型配置資訊。 * model_instance_kind:裝置型號。 * model_instance_device_id:裝置ID。 * model_repository:模型倉庫路徑。 * model_version:模型版本。 * model_name:模型名。 """ # 將JSON字串類型的模型配置內容轉為Python的字典類型。 self.model_config = model_config = json.loads(args["model_config"]) # 擷取模型設定檔中的屬性。 output_config = pb_utils.get_output_config_by_name(model_config, "OUTPUT__0") # 將Triton types轉為numpy types。 self.output_dtype = pb_utils.triton_string_to_numpy(output_config["data_type"]) # 擷取模型倉庫的路徑。 self.model_directory = os.path.dirname(os.path.realpath(__file__)) # 擷取模型推理使用的裝置,本例中使用GPU。 self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") print("device: ", self.device) model_path = os.path.join(self.model_directory, "model.pt") if not os.path.exists(model_path): raise pb_utils.TritonModelException("Cannot find the pytorch model") # 通過.to(self.device)將pytorch模型載入到GPU上。 self.model = torch.jit.load(model_path).to(self.device) print("Initialized...") def execute(self, requests): """ 模型執行函數,必須實現;每次請求推理都會調用該函數,若設定了 batch 參數,還需由使用者自行實現批處理功能 Parameters ---------- requests : pb_utils.InferenceRequest類型的請求列表。 Returns ------- pb_utils.InferenceResponse 類型的返回列表。列表長度必須與請求列表一致。 """ output_dtype = self.output_dtype responses = [] # 遍曆request列表,並為每個請求都建立對應的response。 for request in requests: # 擷取輸入tensor。 input_tensor = pb_utils.get_input_tensor_by_name(request, "INPUT__0") # 將Triton tensor轉換為Torch tensor。 pytorch_tensor = from_dlpack(input_tensor.to_dlpack()) if pytorch_tensor.shape[2] > 1000 or pytorch_tensor.shape[3] > 1000: responses.append( pb_utils.InferenceResponse( output_tensors=[], error=pb_utils.TritonError( "Image shape should not be larger than 1000" ), ) ) continue # 在GPU上進行推理計算。 prediction = self.model(pytorch_tensor.to(self.device)) # 將Torch output tensor轉換為Triton tensor。 out_tensor = pb_utils.Tensor.from_dlpack("OUTPUT__0", to_dlpack(prediction)) inference_response = pb_utils.InferenceResponse(output_tensors=[out_tensor]) responses.append(inference_response) return responses def finalize(self): """ 模型卸載時調用,可選實現,可用於模型清理工作。 """ print("Cleaning up...")重要如果使用GPU進行推理計算,此時在模型設定檔config.pbtxt中指定
instance_group.kind為GPU的方式無效,需要通過model.to(torch.device("cuda"))將模型載入到GPU,並在請求計算時調用pytorch_tensor.to(torch.device("cuda"))將模型輸入Tensor分配到GPU。您只需要在部署服務時配置GPU資源,即可使用GPU進行推理計算。如果使用批處理功能,此時在模型設定檔config.pbtxt中設定max_batch_size參數的方式無效,您需要自行在execute函數中實現請求批處理的邏輯。
request與response必須一一對應,每一個request都要返回一個對應的response。
更新設定檔
設定檔config.pbtxt內容樣本如下:
name: "resnet50_pt" backend: "python" max_batch_size: 128 input [ { name: "INPUT__0" data_type: TYPE_FP32 dims: [ 3, -1, -1 ] } ] output [ { name: "OUTPUT__0" data_type: TYPE_FP32 dims: [ 1000 ] } ] parameters: { key: "FORCE_CPU_ONLY_INPUT_TENSORS" value: {string_value: "no"} }其中關鍵參數說明如下,其餘配置與之前保持一致即可。
backend:需指定為python。
parameters:可選配置,當模型推理使用GPU時,可將FORCE_CPU_ONLY_INPUT_TENSORS參數設定為no,來避免推理計算時輸入Tensor在CPU與GPU之間來回拷貝產生不必要的開銷。
2. 部署服務
使用Python backend必須設定共用記憶體。在填寫如下JSON配置即可實現自訂模型推理邏輯。
{
"metadata": {
"name": "triton_server_test",
"instance": 1
},
"cloud": {
"computing": {
"instance_type": "ml.gu7i.c8m30.1-gu30",
"instances": null
}
},
"containers": [
{
"command": "tritonserver --model-repository=/models",
"image": "eas-registry-vpc.<region>.cr.aliyuncs.com/pai-eas/tritonserver:23.02-py3",
"port": 8000,
"prepare": {
"pythonRequirements": [
"torch==2.0.1"
]
}
}
],
"storage": [
{
"mount_path": "/models",
"oss": {
"path": "oss://oss-test/models/triton_backend/"
}
},
{
"empty_dir": {
"medium": "memory",
// 配置共用記憶體為1 GB。
"size_limit": 1
},
"mount_path": "/dev/shm"
}
]
}其中:
name:需要自訂模型服務名稱。
storage.oss.path:更新為您的模型儲存目錄所在的OSS Bucket路徑。
containers.image:將<region>替換為當前地區,例如:華東2(上海)為cn-shanghai。
調用服務:發送服務要求
您可以通過用戶端發送請求來使用模型服務,Python程式碼範例如下:
發送HTTP請求
連接埠號碼配置為8000時,服務支援發送HTTP請求。
import numpy as np
import tritonclient.http as httpclient
# url為EAS服務部署後產生的訪問地址。
url = '1859257******.cn-hangzhou.pai-eas.aliyuncs.com/api/predict/triton_server_test'
triton_client = httpclient.InferenceServerClient(url=url)
image = np.ones((1,3,224,224))
image = image.astype(np.float32)
inputs = []
inputs.append(httpclient.InferInput('INPUT__0', image.shape, "FP32"))
inputs[0].set_data_from_numpy(image, binary_data=False)
outputs = []
outputs.append(httpclient.InferRequestedOutput('OUTPUT__0', binary_data=False)) # 擷取 1000 維的向量
# 指定模型名稱、請求Token、輸入輸出。
results = triton_client.infer(
model_name="<model_name>",
model_version="<version_num>",
inputs=inputs,
outputs=outputs,
headers={"Authorization": "<test-token>"},
)
output_data0 = results.as_numpy('OUTPUT__0')
print(output_data0.shape)
print(output_data0)其中關鍵參數配置說明如下:
參數 | 描述 |
url | 佈建服務訪問地址,服務訪問地址需要省略 |
model_name | 配置模型目錄名稱,例如resnet50_pt。 |
model_version | 配置實際的模型版本號碼,每次只能對一個模型版本發送請求。 |
headers | 將<test-token>替換為服務Token,您可以在公網地址調用頁簽查看Token。 |
發送gRPC請求
連接埠號碼配置為8001,並添加gRPC相關配置後,服務支援發送gRPC請求。
#!/usr/bin/env python
import grpc
from tritonclient.grpc import service_pb2, service_pb2_grpc
import numpy as np
if __name__ == "__main__":
# 定義服務的訪問地址。
host = (
"service_name.115770327099****.cn-beijing.pai-eas.aliyuncs.com:80"
)
# 服務Token,實際應用中應使用真實的Token。
token = "test-token"
# 模型名稱和版本。
model_name = "resnet50_pt"
model_version = "1"
# 建立gRPC中繼資料,用於Token驗證。
metadata = (("authorization", token),)
# 建立gRPC通道和存根,用於與伺服器通訊。
channel = grpc.insecure_channel(host)
grpc_stub = service_pb2_grpc.GRPCInferenceServiceStub(channel)
# 構建推理請求。
request = service_pb2.ModelInferRequest()
request.model_name = model_name
request.model_version = model_version
# 構造輸入張量,對應模型設定檔中定義的輸入參數。
input = service_pb2.ModelInferRequest().InferInputTensor()
input.name = "INPUT__0"
input.datatype = "FP32"
input.shape.extend([1, 3, 224, 224])
# 構造輸出張量,對應模型設定檔中定義的輸出參數。
output = service_pb2.ModelInferRequest().InferRequestedOutputTensor()
output.name = "OUTPUT__0"
# 建立輸入請求。
request.inputs.extend([input])
request.outputs.extend([output])
# 構造隨機數組並序列化為位元組序列,作為輸入資料。
request.raw_input_contents.append(np.random.rand(1, 3, 224, 224).astype(np.float32).tobytes()) #數實值型別
# 發起推理請求,並接收響應。
response, _ = grpc_stub.ModelInfer.with_call(request, metadata=metadata)
# 提取響應中的輸出張量。
output_contents = response.raw_output_contents[0] # 假設只有一個輸出張量。
output_shape = [1, 1000] # 假設輸出張量的形狀是[1, 1000]。
# 將輸出位元組轉換為numpy數組。
output_array = np.frombuffer(output_contents, dtype=np.float32)
output_array = output_array.reshape(output_shape)
# 列印模型的輸出結果。
print("Model output:\n", output_array)其中關鍵參數配置說明如下:
參數 | 描述 |
host | 需要配置為服務訪問地址,服務訪問地址需要省略 |
token | 將<test-token>替換為服務Token,您可以在公網地址調用頁簽查看Token。 |
model_name | 配置模型目錄名稱,例如resnet50_pt。 |
model_version | 配置實際的模型版本號碼,每次只能對一個模型版本發送請求。 |
常見問題
Q:Triton部署的服務如何線上調試?
線上調試功能需要使用 JSON 格式的請求體。
初始化 HTTPClient 時設定verbose=True,即可列印請求和返回的 JSON 資料。
triton_client = httpclient.InferenceServerClient(url=url, , verbose=True)結果樣本如下:
POST /api/predict/triton_test/v2/models/resnet50_pt/versions/1/infer, headers {'Authorization': '************1ZDY3OTEzNA=='}
b'{"inputs":[{"name":"INPUT__0","shape":[1,3,32,32],"datatype":"FP32","data":[1.0,1.0,1.0,.....,1.0]}],"outputs":[{"name":"OUTPUT__0","parameters":{"binary_data":false}}]}'據此補充請求路徑和請求體即可進行線上調試:

Q:Triton部署的服務如何一鍵壓測?
請參見Q:Triton部署的服務如何線上調試?擷取請求路徑和請求體格式。
以單個資料壓測為例,操作步驟如下,更多壓測說明請參見通用情境服務壓測:
在壓測任務頁簽,單擊添加壓測任務,選擇已部署的Triton服務,並填寫壓測地址。
資料來源選擇單個資料,並參考如下代碼將JSON請求體轉換為Base64編碼的字串。
import base64 # 已有的 JSON 請求體字串 json_str = '{"inputs":[{"name":"INPUT__0","shape":[1,3,32,32],"datatype":"FP32","data":[1.0,1.0,.....,1.0]}]}' # 直接編碼 base64_str = base64.b64encode(json_str.encode('utf-8')).decode('ascii') print(base64_str)
相關文檔
如何基於TensorFlow Serving推理服務引擎部署EAS服務,請參見TensorFlow Serving鏡像部署。
您也可以開發自訂鏡像,使用自訂鏡像部署EAS服務。具體操作,請參見自訂鏡像。