PointPillars 是一种快速 E2E DL 网络,用于 3D 点云中的对象检测。它利用 PointNet 来学习以垂直列(支柱)组织的点云的表示。大量实验表明,PointPillars 在速度和准确性方面都大大优于以前的方法 。在本文档中,我们介绍了如何使用英特尔® OpenVINO在英特尔平台上优化PointPillars。
PointPillars
下图展示了PointPillars网络的主要步骤(组件):
- PFN
- 预处理:将点云原始数据转换为 stacked pillars 和 pillar index;
- PFE:它使用 stacked pillars 来学习一组特征;
- 散射:它将 pillars 特征散射回 CNN 的 2D 伪图像。
- Backbone(2D CNN)
- RPN:它利用 CNN 从 2D 伪图像中提取特征。
- SSD
- 3D 边界框预测:SSD 使用来自 Backbone 的特征来预测对象的 3D 边界框;
- 后处理:使用 NMS 算法过滤边界。
OpenVINO™ 工具套件
OpenVINO 是英特尔的 AI 开发工具包,用于快速开发应用程序和解决方案,解决各种任务,包括人类视觉仿真、自动语音识别、自然语言处理、推荐系统等。
该工具包基于最新一代的人工神经网络(包括 CNN、循环网络和基于注意力的网络),可跨英特尔®硬件扩展计算机视觉和非视觉工作负载,从而最大限度地提高性能。它通过从边缘到云端部署的高性能、AI 和 DL 推理来加速应用程序。
本工作中使用的 OpenVINO™ 工具套件的发行版(版本)为 2021.3,https://docs.openvinotoolkit.org/2021.3/openvino_docs_install_guides_installing_openvino_linux.html
第 11 代英特尔®酷睿™ 处理器 (Tiger Lake)
本工作使用了第 11 代英特尔®酷睿™ 处理器 (Tiger Lake) 的两个 SKU:
- Intel® Core ™ i7-1185GRE 处理器
- Intel® Core ™ i7-1165G7 处理器
本文档中提供的所有测试结果均来自以下 2 个平台:
性能评估结果
通过以下章节中介绍的优化方法,可以在 Intel® Core™ i7-1185GRE 处理器(禁用 Turbo 模式)上实现 11.1 FPS 的吞吐量和 154.7 ms 的延迟。
不同硬件平台上的性能评估结果如表2所示。
PFE FP16、RPN INT8* – 请参阅“量化为 INT8”部分
平衡模式( Balanced Mode )** – 请参阅“平衡模式( Balanced Mode )”部分
430%*** – 我们通过 Ubuntu*20.04 上的“top”工具获取 CPU 负载。 ‘top’ 将 cpu 负载显示为单个 CPU 的百分比。在多核系统上,百分比可以大于 100%。在 Intel® Core™ i7-1165G7 或 Intel® Core™ i7-1185GRE 中,有 4 个物理核心,每个核心有 2 个线程,因此总共有 8 个逻辑核心,因此最高负载为 8×100% = 800 %。
源代码迁移
我们利用开源项目 OpenPCDet ,它是 OpenMMLab 的子项目。 OpenPCDet 框架支持多种 3D 点云(例如,激光雷达生成的点云)中的对象检测模型,包括 PointPillars。
原始的 OpenPCDet 由官方发布的 PyTorch* 实现(我们在 PyTorch 1.7.1+CPU 上进行了验证)。并且,在PointPillars管道中,它包含以下组件:
原始源代码无法在Intel ®架构上运行,因为它们是在CUDA 环境中开发的。因此,为了在没有CUDA环境的通用CPU上支持OpenPCDet,我们对源代码进行了迁移,如下所述。
PyTorch* 源代码的迁移
如下例所示,“.cuda()”出现在原始源代码中。它们需要在迁移中删除。
- model.cuda()
+ device = 'cpu'
+ model.to(device)
- self.anchors = [x.cuda() for x in anchors]
+ self.anchors = [x for x in anchors]
- batch_dict[key] = torch.from_numpy(val).float().cuda()
+ batch_dict[key] = torch.from_numpy(val).float()
迁移到 C++ 源代码
原始源代码中的CUDA内核需要替换为标准C++。
CUDA编译器被C++编译器取代。
- from torch.utils.cpp_extension import BuildExtension, CUDAExtension
+ from torch.utils.cpp_extension import BuildExtension, CppExtension
CUDA源代码从编译配置文件中删除。
sources=[
'src/pointnet2_api.cpp',
'src/ball_query.cpp',
- 'src/ball_query_gpu.cu',
'src/group_points.cpp',
- 'src/group_points_gpu.cu',
'src/sampling.cpp',
- 'src/sampling_gpu.cu',
'src/interpolate.cpp',
- 'src/interpolate_gpu.cu',
],
删除了对 CUDA 内核的调用。
- nmsLauncher(boxes_data, mask_data, boxes_num, nms_overlap_thresh);
- nmsNormalLauncher(boxes_data, mask_data, boxes_num, nms_overlap_thresh);
在PointPillars中,只有NMS算法被实现为CUDA内核。我们只需要用C++手动重写即可。
+void nmsLauncher(const float *boxes, bool *mask,
+ const int boxes_num, const float nms_overlap_thresh){
+ int col_idx = 0;
+ int row_idx = 0;+
+ float block_boxes[7];
+ mask[0] = false;
+ for (col_idx = 0; col_idx < boxes_num; col_idx++){
+ if (mask[col_idx]==true)
+ continue;
+ block_boxes[0] = boxes[col_idx * 7 + 0];
+ block_boxes[1] = boxes[col_idx * 7 + 1];
+ block_boxes[2] = boxes[col_idx * 7 + 2];
+ block_boxes[3] = boxes[col_idx * 7 + 3];
+ block_boxes[4] = boxes[col_idx * 7 + 4];
+ block_boxes[5] = boxes[col_idx * 7 + 5];
+ block_boxes[6] = boxes[col_idx * 7 + 6];
+
+ for (row_idx = col_idx + 1; row_idx < boxes_num; row_idx++){
+ const float *cur_box = boxes + row_idx * 7;
+ bool drop = 0;
+
+ if (iou_bev(cur_box, block_boxes) > nms_overlap_thresh){
+ drop = true;
+ mask[row_idx] = drop;
+ }
+ }
+ }
+}
Intel® Core™ 处理器 (Tiger Lake)的分析
源代码迁移完成后,我们在Intel® Core™ i7-1165G7处理器上运行并收集PointPillars网络的性能数据,软硬件配置如表2所示。
如图3所示,两种NN模型(RPN 和 PFE)是最耗时的组件,占总延迟的 92%。因此,我们需要重点关注这些模型的优化。
使用 OpenVINO™ 工具套件实施
作为 OpenVINO™ 工具包的一部分,MO 是一个跨平台命令行工具,可促进训练和部署环境之间的转换、执行静态模型分析并调整 NN 模型以在端点目标设备上实现最佳执行。在Intel ®架构处理器上运行之前,神经网络模型可以通过 MO 进行优化。
MO将NN模型转换为IR格式,可以用IE读取、加载和推断。 IE 提供跨多种受支持的 Intel ®架构处理器的统一 API。 IR 格式使用一对文件来描述 NN 模型:
- 描述神经网络拓扑的 .xml 文件;
- .bin 文件包含权重和偏差的二进制数据。
NN 模型到 IR 的转换
在本节中,我们将展示如何将 PointPillars 中使用的 NN 模型从 PyTorch* 转换为 IR 格式,并将它们集成到处理管道中。
由于 MO 不支持从 PyTorch* 直接转换为 IR 格式,因此我们需要将模型从 PyTorch* 转换为 ONNX 格式作为中间步骤(如图 5 所示)。
ONNX 是一个开放的生态系统,使人工智能开发人员能够随着项目的发展选择正确的工具。 ONNX 为 AI 模型(包括深度学习和传统机器学习)提供开源格式。它定义了可扩展的计算图模型,以及内置运算符和标准数据类型的定义。
正如前面章节中提到的,PointPillars 中使用了两种神经网络模型:PFE 和 RPN。我们需要将它们转换为 IR 格式。
从 PyTorch* 到 ONNX 的转换
我们利用开源项目 SmallMunich ,按照 https://github.com/SmallMunich/nutonomy_pointpillars#onnx-ir-generate中的说明将神经网络模型从 PyTorch* 转换为 ONNX 。
SmallMunich 也是 PointPillars 基于 Pytorch* 的代码库。此外,它还实现了 ONNX 转换。通过运行 onnx_model_generate(),我们从 PyTorch* 模型获取 ONNX 模型(pfe.onnx 和 rpn.onnx)。
从 ONNX 到 IR 的转换
运行 MO 将pfe.onnx转换为 IR 格式 (FP16):
$ cd <install_folder>/openvino_2021/deployment_tools/model_optimizer
$ python mo.py --input_model <input_folder>/pfe.onnx --input pillar_x,pillar_y,pillar_z,pillar_i,num_points_per_pillar,x_sub_shaped,y_sub_shaped,mask --
input_shape=[1,1,12000,100],[1,1,12000,100],[1,1,12000,100],[1,1,12000,100],[1,12000],[1,1,12000,100],[1,1,12000,100],[1,1,12000,100] -o <output_folder> --data_type=FP16
输出如下:
Model Optimizer arguments:
Common parameters:
- Path to the Input Model: <your_input_folder>/pfe.onnx
- Path for generated IR: <your_output_folder>
- IR output name: pfe
- Log level: ERROR
- Batch: Not specified, inherited from the model
- Input layers: pillar_x,pillar_y,pillar_z,pillar_i,num_points_per_pillar,x_sub_shaped,y_sub_shaped,mask
- Output layers: Not specified, inherited from the model
- Input shapes: [1,1,12000,100],[1,1,12000,100],[1,1,12000,100],[1,1,12000,100],[1,12000],[1,1,12000,100],[1,1,12000,100],[1,1,12000,100]
- Mean values: Not specified
- Scale values: Not specified
- Scale factor: Not specified
- Precision of IR: FP16
- Enable fusing: True
- Enable grouped convolutions fusing: True
- Move mean values to preprocess section: None
- Reverse input channels: False
ONNX specific parameters:
- Inference Engine found in: <install_folder>/openvino_2021/python/python3.8/openvino
Inference Engine version: 2.1.2021.3.0-2787-60059f2c755-releases/2021/3
Model Optimizer version: 2021.3.0-2787-60059f2c755-releases/2021/3
[ SUCCESS ] Generated IR version 10 model.
[ SUCCESS ] XML file: <your_output_folder>/pfe.xml
[ SUCCESS ] BIN file: <your_output_folder>/pfe.bin
[ SUCCESS ] Total execution time: 3.67 seconds.
[ SUCCESS ] Memory consumed: 281 MB.
运行 MO 将rpn.onnx转换为 IR 格式 (FP16):
$ cd <install_folder>/openvino_2021/deployment_tools/model_optimizer
$ python mo.py --input_model <input_folder>/rpn.onnx -o <output_folder> --input_shape=[1,64,496,432] --data_type=FP16
输出如下:
Model Optimizer arguments:
Common parameters:
- Path to the Input Model: <your_intput_folder>/rpn.onnx
- Path for generated IR: <your_output_folder>
- IR output name: rpn
- Log level: ERROR
- Batch: Not specified, inherited from the model
- Input layers: Not specified, inherited from the model
- Output layers: Not specified, inherited from the model
- Input shapes: [1,64,496,432]
- Mean values: Not specified
- Scale values: Not specified
- Scale factor: Not specified
- Precision of IR: FP16
- Enable fusing: True
- Enable grouped convolutions fusing: True
- Move mean values to preprocess section: None
- Reverse input channels: False
ONNX specific parameters:
- Inference Engine found in: <install_folder>/openvino_2021/python/python3.8/openvino
Inference Engine version: 2.1.2021.3.0-2787-60059f2c755-releases/2021/3
Model Optimizer version: 2021.3.0-2787-60059f2c755-releases/2021/3
[ SUCCESS ] Generated IR version 10 model.
[ SUCCESS ] XML file: <your_output_folder>/rpn.xml
[ SUCCESS ] BIN file: <your_output_folder>/rpn.bin
[ SUCCESS ] Total execution time: 7.25 seconds.
[ SUCCESS ] Memory consumed: 384 MB.
最后,我们得到 IR 格式文件作为 MO 的输出,用于 PointPillars 中使用的两个 NN 模型。
- PFE模型:pfe.xml和pfe.bin;
- RPN 模型:rpn.xml和rpn.bin。
使用基准工具验证
我们使用 Benchmark Tool 通过运行每个 NN 模型来评估吞吐量(以 FPS 为单位)和延迟(以毫秒为单位)。评估结果为进一步优化提供指导。
Benchmark Tool 是 OpenVINO™ 工具套件提供的软件。它创建模拟输入来评估神经网络模型的性能。该软件支持两种操作模式:“同步”和“异步”(默认模式)。
我们使用以下命令评估 Intel® Core™ i7-1165G7 处理器中 CPU 和 iGPU 的吞吐量和延迟:
$ cd ~/intel/openvino_2021/deployment_tools/tools/benchmark_tool/
$ python3 benchmark_app.py -m <folder>/pfe.xml -d CPU -api sync
$ python3 benchmark_app.py -m <folder>/rpn.xml -d CPU -api sync
$ python3 benchmark_app.py -m <folder>/pfe.xml -d GPU -api sync
$ python3 benchmark_app.py -m <folder>/rpn.xml -d GPU -api sync
从表3和表4的评价结果可以看出:
- 对于两种神经网络模型,iGPU 在吞吐量和延迟方面均优于 CPU;
- 由 MO 转换和优化的 NN 模型(IR 格式)的处理速度可以显着加快。
量化为 INT8
为了进一步加速推理,我们使用 POT 将 RPN 模型从 FP16 转换为 INT8 分辨率(而 PFE 模型的工作仍在进行中)。
POT 旨在通过特殊过程(例如训练后量化)加速 NN 模型的推理,而无需重新训练或微调 NN 模型。因此,不需要额外的训练数据集或进一步处理。
POT 的示例代码可以在 OpenVINO™ 工具包中找到:
<install_folder>/openvino_2021/deployment_tools/tools/post_training_optimization_toolkit/sample
附件 A显示了我们工作中使用的 Python* 脚本。
在以下脚本中,我们选择算法“DefaultQuantization”(没有精度检查器)和预设参数“性能”(权重和激活的对称量化)。
algorithms = [
{
'name': 'DefaultQuantization',
'params': {
'target_device': 'CPU',
'preset': 'performance',
'stat_subset_size': 100
}
}
]
在以下脚本中,_getitem_()是最重要的函数,POT 调用它来处理输入数据集。
img是输入数据集。我们从 SmallMunich 评估管道中获得 RPN 模型的输入(输入是 [1, 64, 496, 432] 的矩阵)。
准确性检查器使用注释来验证预测结果是否与注释相同。然而,当我们选择算法‘DefaultQuantization’时,准确性检查器会跳过比较。因此,我们只需要提供与 RPN 推理的输出形状完全相同的全零注释。
def __getitem__(self, index):
...
img = np.fromfile(image_path, dtype=np.float32).reshape(64,496,432)
annotation_np = {'184': np.zeros((1, 248, 216, 14), dtype=np.float32),
'185': np.zeros((1,248,216,2), dtype=np.float32),
'187': np.zeros((1,248,216,4), dtype=np.float32)}
annotation = (index, annotation_np)
return annotation, img
运行以下 POT 脚本后,我们将获得具有 INT8 分辨率的 IR 格式的 RPN 模型(rpn.xml和rpn.bin)。
INFO:compression.statistics.collector:Start computing statistics for algorithms : DefaultQuantization
INFO:compression.statistics.collector:Computing statistics finished
INFO:compression.pipeline.pipeline:Start algorithm: DefaultQuantization
INFO:compression.algorithms.quantization.default.algorithm:Start computing statistics for algorithm : ActivationChannelAlignment
INFO:compression.algorithms.quantization.default.algorithm:Computing statistics finished
INFO:compression.algorithms.quantization.default.algorithm:Start computing statistics for algorithms : MinMaxQuantization,FastBiasCorrection
INFO:compression.algorithms.quantization.default.algorithm:Computing statistics finished
INFO:compression.pipeline.pipeline:Finished: DefaultQuantization
表 5 显示 量化可以将 RPN 模型权重的文件大小从 9.2 MB (FP16) 显着减小到 5.2 MB (INT8)。
使用 Benchmark Tool,我们在 Intel® Core™ i7-1165G7 处理器的 CPU 和 iGPU 上评估了两种不同格式和分辨率的 NN 模型,性能结果如表 6 所示。 PFE 模型的研究仍在进行中,我们决定在 PointPillars 网络的处理管道中使用 PFE (FP16) 和 RPN (INT8) 模型。
我们还在 Intel® Core™ i7-1165G7 处理器的 iGPU 上并行运行 PFE (FP16) 和 RPN (INT8) 模型的基准工具。表 7 中显示的结果可以为我们提供有关并行化管道的一些指导。
将 IE 集成到 PointPillars Pipeline
如图 7 所示,我们将两个 NN 模型的 IR 文件集成到 SmallMunich 管道中。
- PFE模型:pfe.xml和pfe.bin;
- RPN 模型:rpn.xml和rpn.bin。
除了 C++ API 之外,OpenVINO™ 工具套件还提供 Python* API 来调用 IE。
将 IE 集成到 PointPillars 管道中包括图 8 中所示的以下步骤。
- 创建IE Core来管理可用设备并读取网络对象;
- 读取MO创建的IR格式的NN模型(支持.xml格式);
- 配置输入和输出;
- 将NN模型加载到设备上;
- 创建推理请求;
- 准备输入;
- 推理;
- 处理输出。
IE的初始化
我们创建 IE 核心实例来处理 PFE 和 RPN 推理。
ie = IECore()
加载NN模型并推理
集成的主要思想是将PyTorch* 的forward()函数替换为OpenVINO™ 工具套件的infer()函数(Python* API)。
PFE模型
首先,将网络的拓扑和权重读取到内存中。
model_file_pfe = "<your_folder>/pfe.xml"
model_weight_pfe = "<your_folder>/pfe.bin"
net = openvino_ie.read_network(model_file_pfe, model_weight_pfe)
然后,我们需要告诉IE输入blob的信息并加载网络到设备。
对于 PFE 模型,每个点云帧的输入形状都是可变的,因为每个帧中的柱子数量不同。因此,有两种对输入进行整形的方法:静态输入(Static Input Shape) 和动态输入 (Dynamic Input Shape )。我们的工作中使用的是前一种方法。
- 静态输入
调用load_network()将模型加载到GPU。在 pfe.xml 文件中,它将输入形状定义为 [[1,1,12000,100], [1,1,12000,100], [1,1,12000,100],[1,1,12000 ,100],[1,12000],[1,1,12000,100],[1,1,12000,100],[1,1,12000,100]]
exec_net = openvino_ie.load_network(network=self.net_pfe, device_name="GPU")
然后,我们利用零填充将输入 blob 的形状设置为 12000。根据源代码中的原始配置,这意味着我们预计最多有 12000 个柱子作为网络的输入。
len_padding = 12000 - pillar_len
pillarx_pad = np.pad(pillarx, ((0,0),(0,0),(0,len_padding),(0,0)),'constant',constant_values=0)
pillary_pad = np.pad(pillary, ((0,0),(0,0),(0,len_padding),(0,0)),'constant',constant_values=0)
pillarz_pad = np.pad(pillarz, ((0,0),(0,0),(0,len_padding),(0,0)),'constant',constant_values=0)
pillari_pad = np.pad(pillari, ((0,0),(0,0),(0,len_padding),(0,0)),'constant',constant_values=0)
例如,填充前的点云帧形状如下:
pillar_x: 1,1,6815,100
pillar_y: 1,1,6815,100
pillar_z: 1,1,6815,100
pillar_i: 1,1,6815,100
num_points_per_pillar: 1,6815
x_sub_shaped: 1,1,6815,100
y_sub_shaped: 1,1,6815,100
mask: 1,1,6815,100
padding后点云框的形状变成如下:
pillar_x: 1,1,12000,100
pillar_y: 1,1,12000,100
pillar_z: 1,1,12000,100
pillar_i: 1,1,12000,100
num_points_per_pillar: 1,12000
x_sub_shaped: 1,1,12000,100
y_sub_shaped: 1,1,12000,100
mask: 1,1,12000,100
最后,我们将每帧的静态形状的输入 blob 传递给infer() 。
res = exec_net.infer(inputs={'pillar_x': pillar_x,
'pillar_y': pillar_y,
'pillar_z': pillar_z,
'pillar_i': pillar_i,
'num_points_per_pillar': num_points,
'x_sub_shaped': x_sub_shaped,
'y_sub_shaped': y_sub_shaped,
'mask': mask})
- 动态输入
与静态输入形状不同,我们需要在每一帧上调用load_network(),因为输入 blob 的 形状逐帧变化。
在调用infer()之前,我们需要重塑点云每一帧的输入参数,如果输入形状发生变化,则调用load_network() 。 load_network()每帧需要相当长的时间(在 iGPU 中通常为 3~4 秒),因为它需要动态 OpenCL 编译处理。
exec_net.reshape({'pillar_x':pillar_x.shape,
'pillar_y':pillar_y.shape,
'pillar_z':pillar_z.shape,
'pillar_i':pillar_i.shape,
'num_points_per_pillar':num_points.shape,
'x_sub_shaped':x_sub_shaped.shape,
'y_sub_shaped':y_sub_shaped.shape,
'mask':mask.shape})
exec_net = ie.load_network(network=net, device_name="GPU")
res = exec_net.infer(inputs={'pillar_x': pillar_x,
'pillar_y': pillar_y,
'pillar_z': pillar_z,
'pillar_i': pillar_i,
'num_points_per_pillar': num_points,
'x_sub_shaped': x_sub_shaped,
'y_sub_shaped': y_sub_shaped,
'mask': mask})
与动态输入相比,静态输入可以显着减少 NN 模型加载时间,特别是对于 iGPU,因为在初始化时只需要load_network()一次。
然而,静态输入可能会导致更长的推理时间,因为神经网络模型的尺寸大于动态输入的尺寸。
RPN模型
对于 RPN 模型,其输入形状固定为 [1, 64, 496, 432],因此我们只需要在初始化时调用load_network()一次。
model_file = "<your folder>/rpn.xml"
model_weight = "<your folder>/rpn.bin"
net = ie.read_network(model_file, model_weight)
exec_net = ie.load_network(network=ie.net, device_name="GPU")
下一步是为每一帧调用infer() 。
input_blob = next(iter(net.input_info))
res = exec_net.infer(inputs={input_blob: spatial_features})
准确性评估
我们使用 KITTI 3D 对象检测数据集来评估 NN 模型的准确性。如表 8 所示,该数据集共有三个难度级别。
将 IE 集成到 SmallMunich 的 PointPillars 管道中后,我们可以评估三个难度级别的准确性。如表 10 所示,与 Pytorch* 原始模型相比,使用 IR 模型和静态输入形状的准确性没有损失。还表明,将 RPN 模型量化为 INT8 仅导致不到 1% 的精度损失。
表 9 显示了 KITTI 测试 3D 检测基准上的汽车 AP 结果。
从 SmallMunich 迁移到 OpenPCDet
OpenPCDet 用作演示应用程序。由于我们使用 SmallMunich 生成 ONNX 模型,因此我们不仅需要将 NN 模型(PFE 和 RPN)迁移,还需要将非 DL 处理从 SmallMunich 代码库迁移到 OpenPCDet 代码库。
SmallMunich 和 OpenPCDet 大约 90% 的源代码是相同的。它们的主要区别在于功能,包括锚点生成、边界框生成、后处理中的框过滤和 NMS。
将源代码从 SmallMunich 迁移到 OpenPCDet 后,OpenPCDet 管道可以生成与 SmallMunich 相同的结果。
管道剖析
我们在 KITTI 3D 对象检测数据集上运行管道。 3D 点由Velodyne HDL-64E(64 通道激光雷达)捕获。它以每秒 10 帧的速度旋转,每帧捕获大约 100K 点。我们通过基于 SmallMunich [9] 代码库的create_reduced_point_cloud()函数将点数减少到每帧 20K 左右。
我们评估了第 5.3 节优化的管道在英特尔®酷睿™ i7-1165G7 处理器上的延迟,结果总结在表 10 中。
我们通过图 11 中的条形图进一步可视化总延迟。它表明 OpenVINO™ 工具套件的 NN 模型优化和量化可以显着加速管道处理。
三种管道模式
如图 12 所示,与原始实现相比,PFE 和 RPN 推断的延迟均显着降低。接下来,我们考虑根据不同的性能目标进一步优化管道。
我们开发了以下提到的管道模式来满足不同的性能目标:
- 延迟模式( Latency Mode):实现最短的延迟;
- 吞吐量模式(Throughput Mode):达到最大吞吐量;
- 平衡模式(Balanced Mode):实现延迟和吞吐量之间的权衡。
延迟模式( Latency Mode)
在这种模式下,管道中的每个步骤在上一个步骤完成后立即开始运行,即这些步骤按以下顺序无缝执行:
- T0时,开始预处理;
- T1时,调用infer()让IE对PFE模型进行推理;
- T2时,Scattering将3D特征图映射到2D伪图像;
- T3时,调用infer()让IE对RPN模型进行推理;
- 在T4,开始后处理。
如图 13 所示,在一个特定时间,管道仅使用 CPU 或 iGPU。这两个处理器专门用于一帧的处理。这些步骤按顺序无缝执行,从而实现最短的延迟。
吞吐量模式(Throughput Mode)
OpenVINO™ 工具套件中的 IE 能够使用API async_infer()进行异步处理。这意味着一帧的步骤可以与另一帧的步骤并行完成。
在此模式下,主线程在 CPU 上运行,处理预处理、散射和后处理。使用 PFE 和 RPN 模型的推理在 IE 使用async_infer()自动创建的独立线程上运行,并且这些线程在 iGPU 中运行。
我们以第N帧为例来解释图14中的处理。
- T0时,主线程开始处理第N帧的预处理;
- 在T1时刻,一旦IE通知第(N-1)帧的PFE推理完成,主线程就启动2个作业:
- 第N帧的PFE推断;
- 第(N-1)帧的散射;
- 在 T2 时,当第 (N-1) 帧的散射完成时,主线程启动 3 个作业:
- 第(N-1)帧的RPN推断;
- 对第(N-2)帧进行后处理;
根据分析,此时第 (N-2) 帧的 RPN 推理已经完成,这意味着同一帧的后处理可以开始。 - 对第(N+1)帧进行预处理;
第 (N-2) 帧的后处理完成后开始。
- 在T4,一旦IE通知第N帧的PFE推理完成,主线程就开始2个作业:
- 第 (N+1) 帧的 PFE 推断;
- 第N帧的散射;
- 在 T5,当第 N 帧 的散射完成时,主线程启动 3 个作业:
- 第N帧的RPN推断;
- 第(N-1)帧的后处理
根据分析,此时第(N-1)帧的RPN推断已经完成,这意味着同一帧的后处理可以开始。 - 对第(N+2)帧进行预处理;
第 (N-1) 帧的后处理完成后开始。
- T7时,当IE通知第(N+1)帧的PFE推理完成后,主线程开始对同一帧进行散射,随后在T8时对第N帧进行后处理。
与延迟模式相比,吞吐量模式的主要思想是最大化 iGPU 中 PFE 和 RPN 推理的并行化,以实现最大吞吐量。 iGPU的负载相当高,平均在95%左右。
然而,这会增加每帧(例如,第 N 帧)的延迟,因为:
- 当 PFE 和 RPN 在 iGPU 中并行运行时,它们的推理延迟都会增加;
- 在第 N 帧的管道中,有两个等待期:
- PFE推理必须等待第(N-1)帧的PFE推理完成,从T1到T2;
- 后处理必须等待第(N+1)帧的散射完成,从T7到T9。
增加的延迟是 PFE 和 RPN 推理并行化所付出的代价。
平衡模式(Balanced Mode)
该模式用于实现延迟和吞吐量之间的权衡。
我们以第N帧为例来解释图15中的处理。
- T0时,主线程开始处理第N帧的预处理;
- 在T1时,主线程调用async_infer()要求IE对第N帧运行PFE推理;
- T2时,一旦通知第(N-1)帧RPN推理完成,主线程就开始第(N-1)帧的后处理;
- T3时,一旦通知第N帧的PFE推理完成,主线程就开始第N帧的分散;
- 在T4,分散完成后,主线程启动2个作业:
- 第N帧的RPN推断;
- 对第(N+1)帧进行预处理;
- 在T6,一旦通知第N帧的RPN推理完成,主线程就开始对第N帧的后处理。
由于 PFE 和 RPN 推理的并行化程度较低,iGPU 的负载为 86%(吞吐量模式下为 95%)。
性能评估结果
我们评估了在英特尔® 酷睿™ i7-1185GRE 处理器上通过 OpenVINO™ 工具套件优化的 PointPillars 管道的三种模式。结果总结于表11中。
不同的管道模式是根据PointPillars管道的每个步骤(组件)的分析结果来设计的。结果表明,更高的并行化可以获得更好的吞吐量,但延迟更差。
分析结果显示 PFE 推理花费的时间最长,因此我们针对吞吐量模式和平衡模式将管道的其余步骤与 PFE 推理并行。
文章评论