如何将算法部署在 Triton Inference Server
基于Python后端的基础模型 (基础示例)
编写配置 config.pbtxt
以目标检测为例
定义输入输出: 参数名, 参数类型, 参数维度
name: "object_detect" # 模型名称, 与当前目录文件名一致
backend: "python" # 推理后端类型
max_batch_size: 1 # 最大批次
input [
{
name: "image"
data_type: TYPE_UINT8
dims: [-1,-1,3 ] # -1代表动态大小
},
{
name: "score"
data_type: TYPE_FP32
dims: [1]
optional: true # optional 为 true 时, 该参数为可选参数, 默认为 false
}
]
output [
{
name: "labels"
data_type: TYPE_STRING
dims: [-1,-1]
},
{
name: "classes"
data_type: TYPE_UINT16
dims: [-1]
},
{
name: "scores"
data_type: TYPE_FP32
dims: [ -1 ]
},
{
name: "bboxes"
data_type: TYPE_UINT32
dims: [-1, 4 ]
}
]
编写model.py
需要实现 TritonPythonModel
类的成员函数: initialize
, execute
, finalize
initialize
initialize
函数在加载模型时只会调用一次。
实现initialize
函数是可选的。该函数允许模型初始化与此模型关联的任何状态。
参数
args
: dict
键和值都是字符串。字典的键和值包括:
model_config
:包含模型配置的JSON字符串model_instance_kind
:包含模型实例类型的字符串model_instance_device_id
:包含模型实例设备ID的字符串model_repository
:模型存储库路径model_version
:模型版本model_name
:模型名称
execute
每个Python模型必须实现execute
函数。execute
函数接收一个pb_utils.InferenceRequest
对象的列表作为唯一参数。当针对该模型进行推断请求时,将调用此函数。根据使用的批处理配置(例如动态批处理),requests
参数可能包含多个请求。每个Python模型必须为requests
中的每个pb_utils.InferenceRequest
创建一个pb_utils.InferenceResponse
。如果发生错误,可以在创建pb_utils.InferenceResponse
时设置错误参数。
参数
requests
: list
一个pb_utils.InferenceRequest
对象的列表
返回
list
一个pb_utils.InferenceResponse
对象的列表。此列表的长度必须与requests
相同
finalize
每个Python模型都必须实现execute
函数。execute
函数接收一个pb_utils.InferenceRequest
对象的列表作为唯一参数。当针对该模型进行推断请求时,将调用此函数。根据所使用的批处理配置(例如动态批处理),requests
参数可能包含多个请求。每个Python模型必须为requests
中的每个pb_utils.InferenceRequest
创建一个pb_utils.InferenceResponse
。如果出现错误,可以在创建pb_utils.InferenceResponse
时设置错误参数。
参数
requests : list
一个pb_utils.InferenceRequest
对象的列表
返回
list
包含pb_utils.InferenceResponse
对象的列表。此列表的长度必须与requests
相同
文件结构
models
|-- object_detect
| |-- 1
| | |-- model.py
| `-- config.pbtxt
启动命令:
tritonserver --model-repository `pwd`/models
业务逻辑脚本 BLS
Triton 的 ensemble
支持许多用例,其中多个模型被组合成一个管道(或者更一般地说是 DAG,有向无环图)。但是,还有许多其他用例不受支持,因为作为模型管道的一部分,它们需要循环、条件(if-then-else)、数据相关的控制流和其他自定义逻辑与模型执行混合。我们将自定义逻辑和模型执行的这种组合称为业务逻辑脚本 (BLS)。
从 21.08 开始,可以在 Python 模型中实现 BLS。一组新的实用函数允许您在 Triton 提供的其他模型上执行推理请求,作为执行 Python 模型的一部分。请注意,BLS 只能在 execute 函数内部使用,并且在 initialize 或 finalize 方法中不受支持。下面的示例展示了如何使用此功能:
import triton_python_backend_utils as pb_utils
class TritonPythonModel:
...
def execute(self, requests):
...
# Create an InferenceRequest object. `model_name`,
# `requested_output_names`, and `inputs` are the required arguments and
# must be provided when constructing an InferenceRequest object. Make
# sure to replace `inputs` argument with a list of `pb_utils.Tensor`
# objects.
inference_request = pb_utils.InferenceRequest(
model_name='model_name',
requested_output_names=['REQUESTED_OUTPUT_1', 'REQUESTED_OUTPUT_2'],
inputs=[<pb_utils.Tensor object>])
# `pb_utils.InferenceRequest` supports request_id, correlation_id,
# model version, timeout and preferred_memory in addition to the
# arguments described above.
# Note: Starting from the 24.03 release, the `correlation_id` parameter
# supports both string and unsigned integer values.
# These arguments are optional. An example containing all the arguments:
# inference_request = pb_utils.InferenceRequest(model_name='model_name',
# requested_output_names=['REQUESTED_OUTPUT_1', 'REQUESTED_OUTPUT_2'],
# inputs=[<list of pb_utils.Tensor objects>],
# request_id="1", correlation_id=4, model_version=1, flags=0, timeout=5,
# preferred_memory=pb_utils.PreferredMemory(
# pb_utils.TRITONSERVER_MEMORY_GPU, # or pb_utils.TRITONSERVER_MEMORY_CPU
# 0))
# Execute the inference_request and wait for the response
inference_response = inference_request.exec()
# Check if the inference response has an error
if inference_response.has_error():
raise pb_utils.TritonModelException(
inference_response.error().message())
else:
# Extract the output tensors from the inference response.
output1 = pb_utils.get_output_tensor_by_name(
inference_response, 'REQUESTED_OUTPUT_1')
output2 = pb_utils.get_output_tensor_by_name(
inference_response, 'REQUESTED_OUTPUT_2')
# Decide the next steps for model execution based on the received
# output tensors. It is possible to use the same output tensors
# to for the final inference response too.
除了允许您执行阻塞推理请求的 inference_request.exec 函数之外, inference_request.async_exec 还允许您执行异步推理请求。当您不需要立即得出推理结果时,这会很有用。使用 async_exec 函数,可以有多个正在进行的推理请求,并且仅在需要时等待响应。下面的示例显示了如何使用 async_exec :
import triton_python_backend_utils as pb_utils
import asyncio
class TritonPythonModel:
...
# You must add the Python 'async' keyword to the beginning of `execute`
# function if you want to use `async_exec` function.
async def execute(self, requests):
...
# Create an InferenceRequest object. `model_name`,
# `requested_output_names`, and `inputs` are the required arguments and
# must be provided when constructing an InferenceRequest object. Make
# sure to replace `inputs` argument with a list of `pb_utils.Tensor`
# objects.
inference_request = pb_utils.InferenceRequest(
model_name='model_name',
requested_output_names=['REQUESTED_OUTPUT_1', 'REQUESTED_OUTPUT_2'],
inputs=[<pb_utils.Tensor object>])
infer_response_awaits = []
for i in range(4):
# async_exec function returns an
# [Awaitable](https://docs.python.org/3/library/asyncio-task.html#awaitables)
# object.
infer_response_awaits.append(inference_request.async_exec())
# Wait for all of the inference requests to complete.
infer_responses = await asyncio.gather(*infer_response_awaits)
for infer_response in infer_responses:
# Check if the inference response has an error
if inference_response.has_error():
raise pb_utils.TritonModelException(
inference_response.error().message())
else:
# Extract the output tensors from the inference response.
output1 = pb_utils.get_output_tensor_by_name(
inference_response, 'REQUESTED_OUTPUT_1')
output2 = pb_utils.get_output_tensor_by_name(
inference_response, 'REQUESTED_OUTPUT_2')
# Decide the next steps for model execution based on the received
# output tensors.
有状态的模型
对于Triton的调度器来说,有状态模型在推理请求之间确实保持状态。该模型期望多个推理请求共同构成一系列推理,这些推理必须被路由到同一个模型实例,以便模型所维护的状态能够被正确更新。此外,模型可能需要Triton提供控制信号,指示例如序列的开始和结束。
这些有状态模型必须使用序列批处理器。如下所述,序列批处理器确保序列中的所有推理请求都被路由到同一个模型实例,从而模型可以正确地维护状态。序列批处理器还与模型通信,以指示序列何时开始、何时结束、何时有推理请求准备好执行,以及序列的关联ID。
当为有状态模型发出推理请求时,客户端应用程序必须为序列中的所有请求提供相同的关联ID,并且必须标记序列的开始和结束。关联ID使Triton能够识别这些请求属于同一序列。
控制输入
为了使有状态模型能够与序列批处理器正确配合工作,模型通常必须接受一个或多个控制输入张量,Triton使用这些张量与模型通信。模型配置的ModelSequenceBatching::Control部分指示模型如何暴露序列批处理器应使用的控制张量。所有控制都是可选的。以下是显示所有可用控制信号的示例配置的部分模型配置。
sequence_batching {
control_input [
{
name: "START"
control [
{
kind: CONTROL_SEQUENCE_START
fp32_false_true: [ 0, 1 ]
}
]
},
{
name: "END"
control [
{
kind: CONTROL_SEQUENCE_END
fp32_false_true: [ 0, 1 ]
}
]
},
{
name: "READY"
control [
{
kind: CONTROL_SEQUENCE_READY
fp32_false_true: [ 0, 1 ]
}
]
},
{
name: "CORRID"
control [
{
kind: CONTROL_SEQUENCE_CORRID
data_type: TYPE_UINT64
}
]
}
]
}
- 模型读取flags的值实现对应行为
sequence_flags = request.flags()
status | flags |
---|---|
start=False , end=False | 0 |
start=True | 1 |
end=True | 2 |
Client
通过sequence_start
,sequence_end
,sequence_id
等字段控制模型状态
res = triton_client.infer(
model_name=model_name,
inputs=inputs,
sequence_start=True,
sequence_end=False,
sequence_id=sequence_id,
outputs=outputs,
)
将 BLS 与有状态模型结合
有状态模型需要在推理请求中设置额外的标志来指示序列的开始和结束。 pb_utils.InferenceRequest 对象中的 flags 参数可用于指示该请求是序列中的第一个请求还是最后一个请求。指示请求正在启动序列的示例:
inference_request = pb_utils.InferenceRequest(model_name='model_name',
requested_output_names=['REQUESTED_OUTPUT_1', 'REQUESTED_OUTPUT_2'],
inputs=[<list of pb_utils.Tensor objects>],
request_id="1", correlation_id=4,
flags=pb_utils.TRITONSERVER_REQUEST_FLAG_SEQUENCE_START)
为了指示序列的结束,您可以使用 pb_utils.TRITONSERVER_REQUEST_FLAG_SEQUENCE_END 标志。如果请求同时开始和结束序列(即序列只有一个请求),则可以使用按位 OR 运算符来启用这两个标志:
flags = pb_utils.TRITONSERVER_REQUEST_FLAG_SEQUENCE_START | pb_utils.TRITONSERVER_REQUEST_FLAG_SEQUENCE_END
局限性
-
要确保作为模型一部分执行的推理请求不会创建循环依赖项。例如,如果模型 A 对自身执行推理请求,并且没有更多模型实例准备好执行推理请求,则模型将永远阻止推理执行。
-
在解耦模式下运行 Python 模型时,不支持异步 BLS。