基于 Triton Inference Server 的算法服务

如何将算法部署在 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()
statusflags
start=False, end=False0
start=True1
end=True2
  • 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。

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值