DALI TRITON 后端#

此仓库包含用于 Triton 推理服务器的 DALI 后端的代码。

alt text

NVIDIA DALI ®,数据加载库,是一组高度优化的构建块和一个执行引擎,用于加速深度学习应用输入数据的预处理。DALI 既提供了性能,又提供了灵活性,可以作为一个库加速不同的数据管线。这个库可以轻松集成到不同的深度学习训练和推理应用中,而无需考虑使用的深度学习框架。

要了解更多关于 DALI 的信息,请参考我们的主页入门指南教程 将指导您完成最初的步骤,支持的操作 将帮助您构建由 GPU 驱动的数据处理管线。

发现任何错误?#

欢迎在此处或 DALI 的 github 仓库 中发布问题。

如何使用?#

  1. DALI 数据管线在 Triton 中表示为一个 模型。要创建这样一个模型,您必须在 Python 中组合一个 DALI 管线。然后,您必须对其进行序列化(通过调用 Pipeline.serialize 方法)或使用 自动序列化 来生成模型文件。例如,我们将使用简单的调整大小管线

     import nvidia.dali as dali
     from nvidia.dali.plugin.triton import autoserialize
    
     @autoserialize 
     @dali.pipeline_def(batch_size=256, num_threads=4, device_id=0)
     def pipe():
         images = dali.fn.external_source(device="cpu", name="DALI_INPUT_0")
         images = dali.fn.image_decoder(images, device="mixed")
         images = dali.fn.resize(images, resize_x=224, resize_y=224)
         return images
    
  2. 模型文件应包含在 Triton 的 模型仓库 中。这是一个例子

     model_repository
     └── dali
         ├── 1
         │   └── model.dali
         └── config.pbtxt
    
  3. 正如 Triton 中常见的做法,您的 DALI 模型文件应命名为 model.dali。您可以在模型配置中通过设置 default_model_filename 选项来覆盖此名称。这是我们用于 ResizePipeline 示例的完整 config.pbtxt

     name: "dali"
     backend: "dali"
     max_batch_size: 256
     input [
     {
         name: "DALI_INPUT_0"
         data_type: TYPE_UINT8
         dims: [ -1 ]
     }
     ]
    
     output [
     {
         name: "DALI_OUTPUT_0"
         data_type: TYPE_UINT8
         dims: [ 224, 224, 3 ]
     }
     ]
    

如果在管线定义中指定了关于输入、输出和最大批大小的信息,则可以省略编写大部分配置文件。有关此功能的详细信息,请参阅 配置自动补全

配置自动补全#

为了简化模型部署,Triton 服务器可以从模型文件本身推断出配置文件的部分内容。对于 DALI 后端,关于输入、输出和最大批大小的信息可以在管线定义中指定,无需在配置文件中重复。下面您可以看到如何在 Python 管线定义中包含配置信息

import nvidia.dali as dali
from nvidia.dali.plugin.triton import autoserialize
import nvidia.dali.types as types

@autoserialize
@dali.pipeline_def(batch_size=256, num_threads=4, device_id=0, output_dtype=[types.UINT8], output_ndim=[3])
def pipe():
    images = dali.fn.external_source(device="cpu", name="DALI_INPUT_0", dtype=types.UINT8, ndim=1)
    images = dali.fn.image_decoder(images, device="mixed")
    images = dali.fn.resize(images, resize_x=224, resize_y=224)
    return images

如您所见,我们向外部源算子添加了 dtypendim (维度数) 参数。它们提供了填写配置文件中 inputs 字段所需的信息。为了填写 outputs 字段,我们向管线定义添加了 output_dtypeoutput_ndim 参数。这些参数应该是列表,每个输出都有一个值。

这样,我们可以将配置文件限制为仅命名模型和指定 DALI 后端

name: "dali"
backend: "dali"

部分配置#

如果管线定义中不存在某些信息,或者要覆盖某些值,您仍然可以提供这些信息。例如,您可以使用配置文件为模型输出指定新名称,这在稍后在集成模型中使用它们时可能很有用。您还可以覆盖最大批大小。上面定义的管线的配置文件可能如下所示

name: "dali"
backend: "dali"
max_batch_size: 128

output [
{
    name: "DALI_OUTPUT_0"
    dims: [ 224, 224, 3 ]
}
]

这样的配置文件将最大批大小值覆盖为 128。它还将管线输出重命名为 "DALI_OUTPUT_0",并将其形状指定为 (224, 224, 3)

有关可在配置文件中指定的模型参数的详细信息,请参阅 DALI 模型配置文件 文档。

自动序列化#

在 Triton 中使用 DALI 后端时,用户必须在模型仓库中提供 DALI 模型。表示模型的规范方法是在其中包含序列化的 DALI 模型文件,并正确命名文件(默认情况下为 model.dali)。将模型存储在序列化文件中产生的问题是,序列化后,模型变得晦涩难懂,几乎无法再读取。自动序列化功能允许用户在模型仓库中用 Python 代码表示模型。对于 Python 定义的模型,DALI 后端使用内部序列化机制,并免除用户手动序列化的操作。

要使用自动序列化功能,用户需要将 DALI 管线的 Python 定义放在模型文件内(默认情况下为 model.dali,但默认文件名可以在 config.pbtxt 中配置)。这样的管线定义必须用 @autoserialize 修饰,例如

import nvidia.dali as dali

@dali.plugin.triton.autoserialize
@dali.pipeline_def(batch_size=3, num_threads=1, device_id=0)
def pipe():
    '''
    An identity pipeline with autoserialization enabled
    '''
    data = dali.fn.external_source(device="cpu", name="DALI_INPUT_0")
    return data

Python 中正确的 DALI 管线定义,以及自动序列化,应满足以下条件

  1. 只有 pipeline_def 可以用 autoserialize 修饰。

  2. 在给定的模型版本中,只能有一个管线定义可以用 autoserialize 修饰。

加载模型文件时,DALI 后端遵循以下优先级

  1. 首先,DALI 后端尝试从用户在 default_model_filename 属性中指定的模型位置加载序列化模型(如果未显式指定,则为 model.dali);

  2. 如果上一步失败,DALI 后端尝试从用户指定的模型位置加载并自动序列化 Python 管线定义。重要提示:在这种情况下,我们要求包含模型定义的文件名以 .py 结尾,例如 mymodel.py

  3. 如果上一步失败,DALI 后端尝试从给定模型版本中的 dali.py 文件加载并自动序列化 Python 管线定义。

如果您没有在 config.pbtxt 文件中调整模型路径定义,则应遵循经验法则

  1. 如果您有一个序列化的管线,请将文件命名为 model.dali 并将其放入模型仓库中,

  2. 如果您有一个要自动序列化的管线的 python 定义,请将其命名为 dali.py

提示和技巧:#

  1. 目前,从 Triton 向 DALI 管线传递输入的唯一方法是使用 fn.external_source 算子。因此,您很可能希望使用它将编码后的图像(或任何其他数据)馈送到 DALI 中。

  2. 给您的 fn.external_source 算子与您在 config.pbtxt 中给 Input 的名称相同的名称。

已知限制:#

  1. DALI 的 ImageDecoder 仅接受来自 CPU 的数据 - 在组合您的 DALI 管线时请记住这一点。

  2. Triton 仅接受同构批处理形状。您可以随意用零填充您的编码图像批次

  3. 由于 DALI 的限制,当为 count 大于 1 的 DALI 模型定义实例组时,您可能会观察到异常增加的内存消耗。我们建议为 DALI 模型使用默认实例组。

如何构建?#

Docker 构建 #

使用 docker 构建 DALI 后端非常简单,只需

git clone --recursive https://github.com/triton-inference-server/dali_backend.git
cd dali_backend
docker build -f docker/Dockerfile.release -t tritonserver:dali-latest .

并且 tritonserver:dali-latest 将成为您的新 tritonserver docker 镜像

裸机#

先决条件#

要构建 dali_backend,您需要 CMake 3.17+

使用最新的 DALI 版本#

如果您需要使用比 tritonserver 镜像中提供的 DALI 更新的版本,您可以使用 DALI 的 nightly builds。只需使用 pip 安装您喜欢的任何 DALI 版本(有关如何操作的更多信息,请参阅链接)。在这种情况下,在构建 dali_backend 时,您需要将 -D TRITON_SKIP_DALI_DOWNLOAD=ON 选项传递给您的 CMake 构建。dali_backend 将在您的系统中找到最新安装的 DALI 并使用此特定版本。

构建#

构建 DALI 后端非常简单。要记住的一件事是克隆包含所有子模块的 dali_backend 仓库

git clone --recursive https://github.com/triton-inference-server/dali_backend.git
cd dali_backend
mkdir build
cd build
cmake ..
make

构建过程将生成 unittest 可执行文件。您可以使用它为 DALI 后端运行单元测试