隐式状态管理#
隐式状态管理允许有状态模型将其状态存储在 Triton 内部。当使用隐式状态时,有状态模型不需要在模型内部存储推理所需的状态。
下面是模型配置的一部分,指示模型正在使用隐式状态。
sequence_batching {
state [
{
input_name: "INPUT_STATE"
output_name: "OUTPUT_STATE"
data_type: TYPE_INT32
dims: [ -1 ]
}
]
}
sequence_batching 设置中的 state 部分用于指示模型正在使用隐式状态。input_name 字段指定将包含输入状态的输入张量的名称。output_name 字段描述模型生成的包含输出状态的输出张量的名称。模型在序列中第 ith 个请求中提供的输出状态将用作第 i+1th 个请求中的输入状态。dims 字段指定状态张量的维度。当 dims 字段包含可变大小的维度时,输入状态和输出状态的形状不必匹配。
出于调试目的,客户端可以请求输出状态。为了允许客户端请求输出状态,模型配置的 output 部分 必须将输出状态列为模型输出之一。请注意,从客户端请求输出状态可能会增加请求延迟,因为必须传输额外的张量。
隐式状态管理需要后端支持。目前,只有 onnxruntime_backend tensorrt_backend 和 pytorch_backend 支持隐式状态。
状态初始化#
默认情况下,序列中的起始请求包含未初始化的输入状态数据。模型可以使用请求中的 start 标志来检测新序列的开始,并通过在模型输出中提供初始状态来初始化模型状态。如果模型的 state 描述中的 dims 部分包含可变大小的维度,则对于起始请求,Triton 将对每个可变大小的维度使用 1。对于序列中其他非起始请求,输入状态是序列中先前请求的输出状态。有关使用隐式状态的 ONNX 模型示例,您可以参考从 create_onnx_modelfile_wo_initial_state()
此生成脚本 生成的此 onnx 模型。这是一个简单的累加器模型,它使用隐式状态将序列中请求的部分和存储在 Triton 中。对于状态初始化,如果请求是起始请求,则模型将“OUTPUT_STATE”设置为等于“INPUT”张量。对于非起始请求,它将“OUTPUT_STATE”张量设置为“INPUT”和“INPUT_STATE”张量的总和。
除了上面讨论的默认状态初始化之外,Triton 还提供了另外两种初始化状态的机制。
从零初始化状态。#
以下是从零初始化状态的示例。
sequence_batching {
state [
{
input_name: "INPUT_STATE"
output_name: "OUTPUT_STATE"
data_type: TYPE_INT32
dims: [ -1 ]
initial_state: {
data_type: TYPE_INT32
dims: [ 1 ]
zero_data: true
name: "initial state"
}
}
]
}
请注意,在上面的示例中,状态描述中的可变维度将转换为固定大小的维度。
从文件初始化状态#
为了从文件初始化状态,您需要在模型目录下创建一个名为“initial_state”的目录。包含初始状态的文件需要在此目录下的 data_file 字段中提供。存储在此文件中的数据将以行优先顺序用作初始状态。以下是从文件初始化状态的状态描述示例。
sequence_batching {
state [
{
input_name: "INPUT_STATE"
output_name: "OUTPUT_STATE"
data_type: TYPE_INT32
dims: [ -1 ]
initial_state: {
data_type: TYPE_INT32
dims: [ 1 ]
data_file: "initial_state_data"
name: "initial state"
}
}
]
}
调度策略#
当决定如何批量处理路由到同一模型实例的序列时,序列批处理器可以采用两种调度策略之一。这些策略是 direct 和 oldest。
Direct#
使用 Direct 调度策略,序列批处理器不仅确保序列中的所有推理请求都路由到同一模型实例,而且还确保每个序列都路由到模型实例内的专用批处理槽。当模型为每个批处理槽维护状态,并且期望给定序列的所有推理请求都路由到同一槽以便正确更新状态时,此策略是必需的。
作为序列批处理器使用 Direct 调度策略的示例,假设一个 TensorRT 有状态模型具有以下模型配置。
name: "direct_stateful_model"
platform: "tensorrt_plan"
max_batch_size: 2
sequence_batching {
max_sequence_idle_microseconds: 5000000
direct { }
control_input [
{
name: "START"
control [
{
kind: CONTROL_SEQUENCE_START
fp32_false_true: [ 0, 1 ]
}
]
},
{
name: "READY"
control [
{
kind: CONTROL_SEQUENCE_READY
fp32_false_true: [ 0, 1 ]
}
]
}
]
}
input [
{
name: "INPUT"
data_type: TYPE_FP32
dims: [ 100, 100 ]
}
]
output [
{
name: "OUTPUT"
data_type: TYPE_FP32
dims: [ 10 ]
}
]
instance_group [
{
count: 2
}
]
sequence_batching 部分指示模型应使用序列批处理器和 Direct 调度策略。在此示例中,模型仅需要序列批处理器的 start 和 ready 控制输入,因此仅列出了这些控件。instance_group 指示应实例化模型的两个实例,max_batch_size 指示每个实例应执行批大小为 2 的推理。下图显示了序列批处理器和此配置指定的推理资源的表示。
每个模型实例都为每个批处理槽维护状态,并且期望给定序列的所有推理请求都路由到同一槽,以便正确更新状态。对于此示例,这意味着 Triton 可以同时为最多四个序列执行推理。
使用 Direct 调度策略,序列批处理器
识别推理请求何时开始新序列,并为该序列分配一个批处理槽。如果新序列没有可用的批处理槽,则 Triton 将推理请求放入积压中。
识别推理请求何时是已分配批处理槽的序列的一部分,并将请求路由到该槽。
识别推理请求何时是积压序列的一部分,并将请求放入积压中。
识别序列中的最后一个推理请求何时完成。该序列占用的批处理槽会立即重新分配给积压中的序列,如果积压中没有序列,则释放以供将来的序列使用。
下图显示了如何使用 Direct 调度策略将多个序列调度到模型实例上。在左侧,该图显示了到达 Triton 的多个请求序列。每个序列可以由任意数量的推理请求组成,并且这些单独的推理请求可以相对于其他序列中的推理请求以任何顺序到达,但右侧显示的执行顺序假设序列 0 的第一个推理请求在序列 1-5 中的任何推理请求之前到达,序列 1 的第一个推理请求在序列 2-5 中的任何推理请求之前到达,依此类推。
该图的右侧显示了推理请求序列如何在一段时间内调度到模型实例上。
下图显示了序列批处理器使用控制输入张量与模型通信。该图显示了分配给模型实例中两个批处理槽的两个序列。每个序列的推理请求随时间到达。START 和 READY 行显示了每次模型执行使用的输入张量值。随着时间的推移,会发生以下情况
slot0 中序列的第一个请求到达。假设模型实例尚未执行推理,则序列调度器会立即调度模型实例执行,因为有推理请求可用。
这是序列中的第一个请求,因此 START 张量中的相应元素设置为 1。slot1 中没有可用的请求,因此 READY 张量仅显示 slot0 已准备好。
推理完成后,序列调度器看到任何批处理槽中都没有可用的请求,因此模型实例处于空闲状态。
接下来,两个推理请求在时间上靠得很近地到达,以便序列调度器看到它们都在各自的批处理槽中可用。调度器立即调度模型实例执行批大小为 2 的推理,并使用 START 和 READY 来显示两个槽都有可用的推理请求,但只有 slot1 是新序列的开始。
对于其他推理请求,处理以类似的方式继续。
Oldest#
使用 Oldest 调度策略,序列批处理器确保序列中的所有推理请求都路由到同一模型实例,然后使用 动态批处理器 将来自不同序列的多个推理合并到一个批次中,以便一起推理。使用此策略,模型通常必须使用 CONTROL_SEQUENCE_CORRID 控制,以便它知道批次中的每个推理请求属于哪个序列。通常不需要 CONTROL_SEQUENCE_READY 控制,因为批次中的所有推理将始终准备好进行推理。
作为序列批处理器使用 Oldest 调度策略的示例,假设一个有状态模型具有以下模型配置
name: "oldest_stateful_model"
platform: "tensorflow_savedmodel"
max_batch_size: 2
sequence_batching {
max_sequence_idle_microseconds: 5000000
oldest
{
max_candidate_sequences: 4
}
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: "CORRID"
control [
{
kind: CONTROL_SEQUENCE_CORRID
data_type: TYPE_UINT64
}
]
}
]
}
input [
{
name: "INPUT"
data_type: TYPE_FP32
dims: [ 100, 100 ]
}
]
output [
{
name: "OUTPUT"
data_type: TYPE_FP32
dims: [ 10 ]
}
]
sequence_batching 部分指示模型应使用序列批处理器和 Oldest 调度策略。Oldest 策略配置为使序列批处理器维护最多 4 个活动候选序列,它更喜欢从中形成大小为 2 的动态批次。在此示例中,模型需要序列批处理器的 start、end 和 correlation ID 控制输入。下图显示了序列批处理器和此配置指定的推理资源的表示。
使用 Oldest 调度策略,序列批处理器
识别推理请求何时开始新序列,并尝试查找有空间容纳候选序列的模型实例。如果模型实例没有空间容纳新的候选序列,则 Triton 将推理请求放入积压中。
识别推理请求何时是已成为某些模型实例中的候选序列的序列的一部分,并将请求路由到该模型实例。
识别推理请求何时是积压序列的一部分,并将请求放入积压中。
识别序列中的最后一个推理请求何时完成。模型实例立即从积压中删除一个序列,并使其成为模型实例中的候选序列,或者记录模型实例可以处理将来的序列(如果积压中没有序列)。
下图显示了如何将多个序列调度到上述示例配置指定的模型实例上。在左侧,该图显示了到达 Triton 的四个请求序列。如图所示,每个序列由多个推理请求组成。该图的中心显示了推理请求序列如何在一段时间内批量处理到模型实例上,假设每个序列的推理请求以相同的速率到达,序列 A 恰好在 B 之前到达,B 恰好在 C 之前到达,等等。Oldest 策略从最旧的请求形成动态批次,但从不在批次中包含来自给定序列的多个请求(例如,序列 D 中的最后两个推理不会批量处理在一起)。