Triton Inference Server 是由 NVIDIA 开发的一款适用于深度学习与机器学习模型的推理服务引擎,支持将TensorRT、TensorFlow、PyTorch或ONNX等多种AI框架的模型部署为在线推理服务,并支持多模型管理、自定义backend等功能。本文介绍如何在PAI-EAS上部署基于 Triton 的推理服务。
前提条件
与 PAI 同地域的对象存储 OSS Bucket。
已训练好的模型文件(如
.pt、.onnx、.plan、.savedmodel)。
快速入门:部署单模型服务
步骤一:准备模型仓库
Triton 要求在对象存储 OSS Bucket 中使用特定的目录结构。请按以下格式创建目录。具体操作请参见管理目录及上传文件。
oss://your-bucket/models/triton/
└── your_model_name/
├── 1/ # 版本目录(必须为数字)
│ └── model.pt # 模型文件
└── config.pbtxt # 模型配置文件
关键要求:
版本目录必须使用数字命名(
1、2、3等)。数字越大表示版本越新。
每个模型都需要一个
config.pbtxt配置文件。
步骤二:创建模型配置文件
创建 config.pbtxt 文件,配置模型的基础信息,示例如下:
name: "your_model_name"
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: { latest: { num_versions: 1 }}
# 加载所有版本
# version_policy: { all { }}
# 加载最新的两个版本
# version_policy: { latest: { num_versions: 2 }}
# 加载指定版本
# version_policy: { specific: { versions: [1, 3] }}配置参数说明
参数 | 是否必选 | 说明 |
| 否 | 模型名称。如指定,必须与模型目录名一致。 |
| 是 | 模型框架。可选值: |
| 是 |
|
| 是 | 最大批处理大小。设为 |
| 是 | 输入张量配置: |
| 是 | 输出张量配置: |
| 否 | 指定推理设备: |
| 否 | 控制加载哪些模型版本(配置示例见前文 |
platform 和 backend 至少配置一项。
步骤三:部署服务
步骤四:启用gRPC(可选)
默认情况下,Triton 在端口 8000 提供 HTTP 服务。如需使用 gRPC:
单击服务配置页面右上角的切换为自定义部署。
在环境信息区域,将端口号修改为
8001。在服务功能 > 高级网络下,启用gRPC。
单击部署。
模型部署成功后即可调用服务。
部署多模型服务
如需在单个 Triton 实例中部署多个模型,只需将多个模型放在同一仓库目录下:
oss://your-bucket/models/triton/
├── resnet50_pytorch/
│ ├── 1/
│ │ └── model.pt
│ └── config.pbtxt
├── densenet_onnx/
│ ├── 1/
│ │ └── model.onnx
│ └── config.pbtxt
└── classifier_tensorflow/
├── 1/
│ └── model.savedmodel/
│ ├── saved_model.pb
│ └── variables/
└── config.pbtxt
部署步骤与单模型相同。Triton 会自动加载仓库中的所有模型。
使用Python Backend自定义推理逻辑
当您需要自定义预处理、后处理或推理逻辑时,可以使用 Triton 的 Python Backend。
目录结构
your_model_name/
├── 1/
│ ├── model.pt # 模型文件
│ └── model.py # 自定义推理逻辑
└── config.pbtxt
实现Python Backend
创建 model.py 文件,定义 TritonPythonModel 类:
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...")
当使用Python Backend时,Triton的某些行为会发生改变,请务必注意:
max_batch_size失效:config.pbtxt中的max_batch_size参数对Python Backend的动态批处理无效。您必须在execute方法中自行遍历requests列表,手动拼接Batch进行推理。instance_group失效:config.pbtxt中的instance_group无法控制Python Backend使用CPU或GPU。您必须在initialize和execute方法中,通过代码(如pytorch_tensor.to(torch.device("cuda")))显式地将模型和数据移动到目标设备。
更新配置文件
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之间来回拷贝产生不必要的开销。
部署服务
使用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:25.03-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"
}
]
}关键JSON配置说明:
containers[0].image: Triton官方镜像。请将cn-hangzhou替换为您服务所在的地域。containers[0].prepare.pythonRequirements: 在此列出您的Python依赖库,EAS会在服务启动前自动安装。storage: 包含两个挂载项。第一个将您的OSS模型仓库路径挂载到容器的
/models目录。第二个是必须配置的共享内存。Triton Server与Python Backend进程之间通过共享内存
/dev/shm传递张量数据以实现零拷贝,从而最大化性能。size_limit单位为GB,请根据模型和并发量估算所需大小。
调用服务
获取服务端点和Token
进入模型在线服务(EAS)页面,单击服务名称。
在服务详情页签,单击查看调用信息,复制公网调用地址和Token。
发送HTTP请求
端口号配置为8000时,服务支持发送HTTP请求。
import numpy as np
# 安装tritonclient包请执行命令:pip install tritonclient
import tritonclient.http as httpclient
# 服务部署后生成访问地址(服务端点),不带http://
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="<your-model-name>",
model_version="<version-num>",
inputs=inputs,
outputs=outputs,
headers={"Authorization": "<your-service-token>"},
)
output_data0 = results.as_numpy('OUTPUT__0')
print(output_data0.shape)
print(output_data0)发送gRPC请求
端口号配置为8001,并添加gRPC相关配置后,服务支持发送gRPC请求。
注意:gRPC的访问地址和HTTP的不相同,请重新从服务详情页面获取。
#!/usr/bin/env python
import grpc
# 安装tritonclient包请执行命令:pip install tritonclient
from tritonclient.grpc import service_pb2, service_pb2_grpc
import numpy as np
if __name__ == "__main__":
# 服务部署后生成访问地址(服务端点),不带http://,末尾添加“:80”
host = (
"service_name.115770327099****.cn-beijing.pai-eas.aliyuncs.com:80"
)
# 服务Token,实际应用中应使用真实的Token。
token = "<your-service-token>"
# 模型名称和版本。
model_name = "<your-model-name>"
model_version = "<version-num>"
# 创建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)调试技巧
启用详细日志
设置 verbose=True 可打印请求和响应的 JSON 数据:
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}}]}'在线调试
可以直接在控制台的在线调试功能中进行测试,请求地址补全为/api/predict/triton_test/v2/models/resnet50_pt/versions/1/infer,Body使用详细日志中的 JSON 请求数据。

压测服务
以单个数据压测为例,操作步骤如下,更多压测说明请参见通用场景服务压测:
在压测任务页签,单击添加压测任务,选择已部署的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)
常见问题
Q:出现报错:CUDA error: no kernel image is available for execution on the device,怎么办?
出现该报错的原因是镜像版本与GPU的兼容性问题,您可以尝试更换其他GPU型号的规格,如:A10、T4。
Q:使用HTTP调用报错:tritonclient.utils.InferenceServerException: url should not include the scheme,怎么解决?
出现该报错的原因是服务的url填写错误。获取服务端点的格式为:http://17519301*******.cn-hangzhou.pai-eas.aliyuncs.com/api/predict/wen*****(注意,其与gRPC的访问地址不同),请去掉开头的http://。
Q:使用gRPC调用报错:DNS resolution failed for wenyu****.175193***43.cn-hangzhou.pai-eas.aliyuncs.com/:80,怎么解决?
出现该报错的原因是服务的host填写错误。获取服务端点的格式为:http://we*****.1751930*****.cn-hangzhou.pai-eas.aliyuncs.com/(注意,其与HTTP的访问地址不同),请去掉开头的http:// 以及末尾的/,然后在末尾补:80,最终变成:we*****.1751930*****.cn-hangzhou.pai-eas.aliyuncs.com:80。
相关文档
如何基于TensorFlow Serving推理服务引擎部署EAS服务,请参见TensorFlow Serving镜像部署。
您也可以开发自定义镜像,使用自定义镜像部署EAS服务。具体操作,请参见自定义镜像。
更多NVIDIA Triton信息,请参见Triton官方文档。