跳转到主要内容

热门内容

今日:


总体:


最近浏览:


Chinese, Simplified

category

下图显示了Triton推理服务器的高级体系结构。模型存储库是一个基于文件系统的模型存储库,Triton将使其可用于推理。推理请求通过HTTP/REST或GRPC或C API到达服务器,然后路由到相应的每个模型调度程序。Triton实现了多种调度和批处理算法,这些算法可以逐个模型进行配置。每个模型的调度器可选地执行推理请求的批处理,然后将请求传递到与模型类型对应的后端。后端使用批处理请求中提供的输入执行推理,以产生请求的输出。然后返回输出。
Triton支持后端C API,允许使用新功能扩展Triton,如自定义的预处理和后处理操作,甚至新的深度学习框架。
Triton提供的模型可以通过专用的模型管理API查询和控制,该API可通过HTTP/REST或GRPC协议或C API使用。
就绪性和生存性健康端点以及利用率、吞吐量和延迟指标简化了Triton与Kubernetes等部署框架的集成。

并发模型执行


Triton体系结构允许多个模型和/或同一模型的多个实例在同一系统上并行执行。该系统可以具有零个、一个或多个GPU。下图显示了一个具有两个模型的示例;模型0和模型1。假设Triton目前没有处理任何请求,当两个请求同时到达时,每个模型一个请求,Triton会立即将两个请求调度到GPU上,GPU的硬件调度器开始并行处理这两个计算。在系统CPU上执行的模型由Triton类似地处理,不同之处在于每个模型的CPU线程执行调度由系统的操作系统处理。

默认情况下,如果同一型号的多个请求同时到达,Triton将通过在GPU上一次只调度一个请求来串行化它们的执行,如下图所示。

Triton提供了一个称为实例组的模型配置选项,允许每个模型指定该模型应允许的并行执行次数。每个这样启用的并行执行都被称为一个实例。默认情况下,Triton为每个模型提供系统中每个可用GPU的单个实例。通过在模型配置中使用instance_group字段,可以更改模型的执行实例数。下图显示了将model1配置为允许三个实例时的模型执行情况。如图所示,前三个model1推理请求立即并行执行。第四个model1推理请求必须等到前三个执行中的一个完成后才能开始。

模型和时间表


Triton支持多种调度和批处理算法,这些算法可以为每个模型独立选择。本节介绍无状态、有状态和集成模型,以及Triton如何提供调度器来支持这些模型类型。对于给定的模型,调度器的选择和配置是通过模型的配置文件完成的。

无状态模型


关于Triton的调度器,无状态模型不维护推理请求之间的状态。对无状态模型执行的每个推理都独立于使用该模型的所有其他推理。

无状态模型的例子是CNN,例如图像分类和对象检测。默认的调度程序或动态批处理程序可以用作这些无状态模型的调度程序。

RNN和具有内部内存的类似模型可以是无状态的,只要它们维护的状态不跨越推理请求。例如,如果在一批推理请求之间没有携带内部状态,则迭代一批中所有元素的RNN被Triton视为无状态。默认调度程序可以用于这些无状态模型。不能使用动态批处理程序,因为模型通常不希望批处理表示多个推理请求。

有状态模型


关于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
       }
     ]
   }
 ]
}

开始:在配置中使用CONTROL_SEQUENCE_Start指定开始输入张量。示例配置表明,该模型具有一个名为START的输入张量,该张量具有32位浮点数据类型。序列批处理程序将在对模型执行推理时定义此张量。START张量必须是一维的,其大小等于批大小。张量中的每个元素指示相应批处理槽中的序列是否开始。在示例配置中,fp32_false_true指示序列开始由等于1的张量元素指示,而非开始由等于0的张量元素表示。

结束:在配置中使用CONTROL_SEQUENCE_End指定结束输入张量。示例配置表明该模型具有一个名为END的输入张量,该张量具有32位浮点数据类型。序列批处理程序将在对模型执行推理时定义此张量。END张量必须是一维的,其大小等于批大小。张量中的每个元素指示相应批处理槽中的序列是否结束。在示例配置中,fp32_false_true指示序列结束由等于1的张量元素指示,并且非结束由等于0的张量元素表示。

就绪:就绪输入张量是使用配置中的CONTROL_SEQUENCE_Ready指定的。示例配置表明该模型具有一个名为READY的输入张量,该张量具有32位浮点数据类型。序列批处理程序将在对模型执行推理时定义此张量。READY张量必须是一维的,其大小等于批大小。张量中的每个元素指示相应批处理槽中的序列是否具有准备好进行推理的推理请求。在示例配置中,fp32_false_true指示序列就绪由等于1的张量元素指示,而非就绪由等于0的张量元素表示。

相关ID:使用配置中的CONTROL_SEQUENCE_CORRID指定相关ID输入张量。示例配置表明,该模型有一个名为CORRID的输入张量,其数据类型为无符号64位整数。序列批处理程序将在对模型执行推理时定义此张量。CORRID张量必须是一维的,其大小等于批次大小。张量中的每个元素指示相应批处理槽中序列的相关性ID。

隐式状态管理


隐式状态管理允许有状态模型将其状态存储在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字段描述由包含输出状态的模型生成的输出张量的名称。模型在序列中的第i个请求中提供的输出状态将用作第i+1个请求中的输入状态。dims字段指定状态张量的维度。当dims字段包含可变大小的尺寸时,输入状态和输出状态的形状不必匹配。

出于调试目的,客户端可以请求输出状态。为了允许客户端请求输出状态,模型配置的输出部分必须将输出状态列为模型输出之一。请注意,由于必须传输额外的张量,从客户端请求输出状态可能会增加请求延迟。

隐式状态管理需要后端支持。目前,只有onnxruntime_backend tensorrt_backend和pytorch_backend支持隐式状态。

状态初始化


默认情况下,序列中的启动请求包含输入状态的未初始化数据。模型可以使用请求中的开始标志来检测新序列的开始,并通过在模型输出中提供初始状态来初始化模型状态。如果模型状态描述中的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"
     }
   }
 ]
}

日程安排策略


序列批处理器在决定如何对路由到同一模型实例的序列进行批处理时,可以采用两种调度策略之一。这些策略是直接的,也是最古老的。

直接的


通过直接调度策略,序列批处理器不仅确保序列中的所有推理请求都被路由到同一模型实例,而且还确保每个序列都被路由至模型实例内的专用批处理槽。当模型维护每个批处理槽的状态,并期望将给定序列的所有推理请求路由到同一槽,以便正确更新状态时,需要使用此策略。

作为使用直接调度策略的序列批处理器的示例,假设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部分指示模型应使用序列批处理器和直接调度策略。在本例中,模型只需要序列批处理器的启动和就绪控制输入,因此只列出这些控制。instance_group表示应该实例化模型的两个实例,max_batch_size表示这些实例中的每一个都应该执行批量大小2的推断。下图显示了序列批处理程序和此配置指定的推理资源的表示。

每个模型实例都在维护每个批处理槽的状态,并期望将给定序列的所有推理请求路由到同一槽,以便正确更新状态。对于这个例子,这意味着Triton可以同时对多达四个序列进行推理。

使用直接调度策略,序列批处理程序:

  1. 识别推理请求何时启动新序列并为该序列分配批处理槽。如果新序列没有可用的批处理槽,Triton会将推理请求放入积压工作中。
  2. 识别推理请求何时是具有分配的批处理槽的序列的一部分,并将请求路由到该槽。
  3. 识别推理请求何时是积压工作中序列的一部分,并将该请求放入积压工作中。
  4. 识别序列中的最后一个推理请求何时完成。该序列占用的批处理槽会立即重新分配给积压工作中的某个序列,如果没有积压工作,则会释放给未来的序列。

下图显示了如何使用直接调度策略将多个序列调度到模型实例上。左边的图显示了到达海卫一的几个请求序列。每个序列可以由任何数量的推理请求组成,并且这些单独的推理请求可以相对于其他序列中的推理请求以任何顺序到达,除了右边所示的执行顺序假设序列0的第一个推理请求在序列1-5中的任何推理请求之前到达,序列1的第一个推断请求在序列2-5中的任何推断请求之前到达等等。

图的右边显示了如何随着时间的推移将推理请求序列调度到模型实例上。

下图显示了序列批处理器使用控制输入张量与模型进行通信。该图显示了分配给模型实例中的两个批处理槽的两个序列。每个序列的推理请求随着时间的推移而到达。START和READY行显示用于模型每次执行的输入张量值。随着时间的推移,会发生以下情况:

第一个请求到达插槽0中的序列。假设模型实例尚未执行推理,则序列调度器会立即调度模型实例执行,因为推理请求可用。

这是序列中的第一个请求,因此START张量中的相应元素被设置为1。slot1中没有可用的请求,因此READY张量仅显示slot0已就绪。

推理完成后,序列调度程序发现在任何批处理槽中都没有可用的请求,因此模型实例处于空闲状态。

接下来,两个推理请求在时间上接近到达,以便序列调度器看到它们在各自的批处理槽中都可用。调度器立即调度模型实例以执行批量大小2的推断,并使用START和READY来表明两个槽都有可用的推断请求,但只有槽1是新序列的开始。

对于其他推理请求,该处理以类似的方式继续。

最老的


使用最旧调度策略,序列批处理器确保序列中的所有推理请求都被路由到同一个模型实例,然后使用动态批处理器将来自不同序列的多个推理批处理到一个一起推理的批中。使用此策略,模型通常必须使用CONTROL_SEQUENCE_CORRID控件,以便知道批次中的每个推理请求属于哪个序列。通常不需要CONTROL_SEQUENCE_READY控件,因为批次中的所有推理都将随时准备好进行推理。

作为使用最旧调度策略的序列批处理器的示例,假设一个有状态模型具有以下模型配置:

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部分指示模型应该使用序列批处理器和最旧调度策略。最旧策略被配置为使得序列批处理器保持多达4个活动候选序列,它更喜欢从中形成大小为2的动态批。在本例中,模型需要序列批处理程序的开始、结束和关联ID控制输入。下图显示了序列批处理程序和此配置指定的推理资源的表示。

使用最旧的调度策略,序列批处理程序:

  • 识别推理请求何时启动新序列,并尝试查找具有候选序列空间的模型实例。如果没有模型实例可以容纳新的候选序列,Triton会将推理请求放入积压工作中。
  • 识别推理请求何时是某个模型实例中已经是候选序列的序列的一部分,并将该请求路由到该模型实例。
  • 识别推理请求何时是积压工作中序列的一部分,并将该请求放入积压工作中。
  • 识别序列中的最后一个推理请求何时完成。模型实例立即从积压工作中删除一个序列,并使其成为模型实例中的候选序列,或者记录如果没有积压工作,模型实例可以处理未来的序列。

下图显示了如何在上述示例配置指定的模型实例上调度多个序列。左边的图显示了到达海卫一的四个请求序列。每个序列由多个推理请求组成,如图所示。图的中心显示了推理请求序列是如何随着时间的推移分批到模型实例上的,假设每个序列的推理请求与序列A在B之前到达的速率相同,序列A在C之前到达,等等。最旧策略从最旧的请求形成一个动态批,但从不在一个批中包括来自给定序列的多个请求(例如,序列D中的最后两个推理没有一起分批)。

集合模型


集成模型表示一个或多个模型的流水线以及这些模型之间的输入和输出张量的连接。集合模型用于封装涉及多个模型的过程,例如“数据预处理->推理->数据后处理”。为此目的使用系综模型可以避免传输中间张量的开销,并最大限度地减少必须发送给Triton的请求数量。

集成调度器必须用于集成模型,而与集成内的模型使用的调度器无关。关于集合调度器,集合模型不是实际模型。相反,它将集成中模型之间的数据流指定为模型配置中的ModelEnsembling::Step条目。调度器收集每个步骤中的输出张量,并根据规范将它们作为其他步骤的输入张量提供。尽管如此,从外部角度来看,集合模型仍然被视为单个模型。

请注意,集成模型将继承所涉及模型的特性,因此请求标头中的元数据必须符合集成内的模型。例如,如果其中一个模型是有状态模型,那么对集成模型的推理请求应该包含有状态模型中提到的信息,该信息将由调度器提供给有状态模型。

例如,考虑具有以下模型配置的用于图像分类和分割的集成模型:

name: "ensemble_model"
platform: "ensemble"
max_batch_size: 1
input [
 {
   name: "IMAGE"
   data_type: TYPE_STRING
   dims: [ 1 ]
 }
]
output [
 {
   name: "CLASSIFICATION"
   data_type: TYPE_FP32
   dims: [ 1000 ]
 },
 {
   name: "SEGMENTATION"
   data_type: TYPE_FP32
   dims: [ 3, 224, 224 ]
 }
]
ensemble_scheduling {
 step [
   {
     model_name: "image_preprocess_model"
     model_version: -1
     input_map {
       key: "RAW_IMAGE"
       value: "IMAGE"
     }
     output_map {
       key: "PREPROCESSED_OUTPUT"
       value: "preprocessed_image"
     }
   },
   {
     model_name: "classification_model"
     model_version: -1
     input_map {
       key: "FORMATTED_IMAGE"
       value: "preprocessed_image"
     }
     output_map {
       key: "CLASSIFICATION_OUTPUT"
       value: "CLASSIFICATION"
     }
   },
   {
     model_name: "segmentation_model"
     model_version: -1
     input_map {
       key: "FORMATTED_IMAGE"
       value: "preprocessed_image"
     }
     output_map {
       key: "SEGMENTATION_OUTPUT"
       value: "SEGMENTATION"
     }
   }
 ]
}

ensemble_scheduling部分指示将使用集合调度器,并且集合模型由三个不同的模型组成。步骤部分中的每个元素都指定了要使用的模型,以及如何将模型的输入和输出映射到调度器识别的张量名称。例如,步骤中的第一个元素指定应使用最新版本的image_preprocess_model,其输入“RAW_image”的内容由“image”张量提供,其输出“PREPROCESSED_output”的内容将映射到“PREPROCESSED_image”张量以供日后使用。调度器识别的张量名称是集合输入、集合输出以及input_map和output_map中的所有值。

组成集合的模型还可以启用动态批处理。由于系综模型只是在组成模型之间路由数据,Triton可以将请求带入系综模型,而无需修改系综的配置来利用组成模型的动态批处理。

假设只提供集成模型、预处理模型、分类模型和分割模型,客户端应用程序将把它们视为四个不同的模型,可以独立处理请求。然而,集合调度器将如下看待集合模型。

当接收到对集成模型的推断请求时,集成调度器将:

  1. 识别请求中的“IMAGE”张量已映射到预处理模型中的输入“RAW_IMAGE”。
  2. 检查集合内的模型,并向预处理模型发送内部请求,因为所需的所有输入张量都已准备好。
  3. 识别内部请求的完成,收集输出张量,并将内容映射到“预处理图像”,这是集合中已知的唯一名称。
  4. 将新收集的张量映射到系综内模型的输入。在这种情况下,“classification_model”和“segmentation_mode”的输入将被映射并标记为就绪。
  5. 检查需要新收集的张量的模型,并向其输入已准备好的模型、分类模型和分割模型发送内部请求。请注意,响应将按照任意顺序,这取决于单个模型的负载和计算时间。
  6. 重复步骤3-5,直到不再发送内部请求,然后用映射到集合输出名称的张量来响应推理请求。

其他资源


您可以在以下链接中找到其他端到端集成示例:

本文地址
最后修改
星期五, 五月 17, 2024 - 22:40
Article