博客算法工具链【玩转 J6 算法工具链:模型量化实战篇】PIDNet 量化篇

【玩转 J6 算法工具链:模型量化实战篇】PIDNet 量化篇

YCJ2026-10-08
18
0

【玩转 J6 算法工具链:模型量化实战篇】PIDNet 量化篇

本文以开源语义分割模型 PIDNet-S 为例,完整介绍如何使用地平线 J6 算法工具链完成一次 PTQ 模型量化与部署。

整个流程如下:

本文主要关注模型转换和性能验证流程,不展开语义分割精度评测及后处理实现。


一、模型准备与 ONNX 导出

PIDNet 是一个面向实时语义分割任务的三分支网络,其中 P、I、D 三个分支分别负责细节信息、上下文信息以及边界信息。官方提供了 PIDNet-S、PIDNet-M、PIDNet-L 三种规模的模型,以及 Cityscapes、CamVid 等数据集上的训练权重。本文选用体量相对较小的 PIDNet-S Cityscapes 模型作为量化对象。

1.1 拉取源码

首先拉取 PIDNet 官方源码:

工程主要目录如下:

为了保证后续实验可复现,也可以记录当前源码版本:


1.2 下载模型权重

本文使用:

即 PIDNet-S 在 Cityscapes 验证集上的模型。

官方 README 当前说明,原有部分 Google Drive 单文件链接已经失效,建议从 README 中提供的统一权重目录下载模型。官方评测 PIDNet-S 时使用的文件名同样是:

下载后将模型放到:

需要注意,本文使用的是 Cityscapes 完整分割模型权重,而不是:

后者只是 ImageNet 预训练权重。


1.3 构建推理模型

PIDNet 训练阶段默认会输出多个结果,包括主分割结果以及辅助分支输出。

而真正部署时,我们只需要最终的主分割输出。

官方 tools/custom.py 中进行实际图片推理时,也是使用:
构建 augment=False 的预测模型,而不是使用训练状态下的多输出模型。

因此,在导出 ONNX 时同样采用这一方式。

本文最终 ONNX 的输入固定为:

输入数据格式:

创建 export_onnx.py:

执行:

正常情况下输入为:

模型主输出为:

这里输出不是 640×640 是正常现象。

PIDNet 原始推理流程就是先得到低分辨率的 segmentation logits,再在模型外通过 bilinear interpolation 将输出恢复到原图尺寸,最后执行 ArgMax 获得类别结果。

因此本文没有将:

的 Resize 和 ArgMax 放进 ONNX,而是保留原始网络主干输出:

这样也可以避免将不必要的后处理计算加入 HBM 性能统计。


1.4 验证 ONNX

导出以后不要急着进入 PTQ,先使用 ONNX Runtime 确认模型能够正常执行:

预期结果类似:

到这里,浮点 ONNX 模型准备完成。


二、准备 Calibration 数据

PTQ 量化需要使用一批 Calibration 数据统计模型各层 Activation 的数据分布,并由此确定量化参数。

地平线工具链要求校准数据经过与原始浮点模型一致的数据预处理,并保证数据类型、Shape 和 Layout 与 input_type_train、input_shape、input_layout_train 对应。官方文档建议校准样本来自训练集或验证集,并支持通过 numpy.save() 保存为 .npy 数据供校准过程读取。

本文采用:

因此,我们需要在工具链外提前完成 PIDNet 的完整图片预处理,再将最终 Tensor 保存为 .npy。

2.1 PIDNet 输入预处理

PIDNet 官方图片推理使用的 Normalize 参数为:

完整处理过程为:

这里和常规 RGB 输入配置最大的区别在于:

featuremap 校准数据已经是模型真正需要的 Tensor,因此不能只保存 0~255 的 RGB 图片数据。


2.2 Calibration 数据生成

下面给出一个核心版本的校准集生成脚本:

执行:

生成:

每个 .npy 都应该满足:

可以随机检查一个:

需要注意:

Normalize 之后 Tensor 出现负数是正常现象。

例如黑色像素经过 Normalize 后大约为:

所以不要再按照 0~255 判断 Calibration Tensor 是否正确。

一个很重要的原则

Calibration 数据的前处理应该和 ONNX 浮点模型实际推理使用的前处理保持一致。

也就是说:

如果两边 preprocessing 不一致,那么工具链统计得到的 Activation 分布本身就是错误的,后续再怎么调 Calibration 方法都没有意义。


三、配置 YAML 并进行 PTQ 编译

Calibration 数据准备完成以后,就可以编写 hb_compile 使用的 YAML 配置文件。

本文使用如下配置:

下面分别看一下几个关键配置。


3.1 模型配置

其中:

表示目标平台为 J6E。

地平线工具链中:

不同 J6 产品需要根据对应平台修改 march。官方当前文档中同样将 nash-e 对应 J6E。

最终模型会生成到:

最终 HBM 名称为:


3.2 输入配置

本文最重要的一部分是:

因为 Calibration 数据已经完成:

所以工具链不再负责输入预处理。

模型实际接收到的数据就是:

地平线官方文档中说明,featuremap 输入每个数据使用 float32 表示;同时,当 input_type_rt 和 input_type_train 设置一致时,工具链会采用直通方式,不额外插入颜色空间转换。

因此,这里有一个非常重要的对应关系:

千万不要出现:

否则相当于对输入做了两遍归一化。


3.3 Calibration 配置

本文采用:

cal_data_dir 指向前面生成的:

校准数据目录。

官方工具链要求该目录中的校准数据与模型输入配置保持一致。

本文采用:

并设置:

其核心思想是:

不直接使用绝对最大值作为量化范围,而是通过 percentile 排除极少量异常值对量化范围的影响。

官方 quant_config 文档中,max_percentile 的合法范围为:
默认值为 1.0,0.99995 也是官方配置示例中使用的典型取值之一。

不同 OpenExplorer 版本对 Calibration 参数的 YAML 组织方式可能存在差异,本文使用的是当前实验环境中的配置方式,实际使用时建议同时参考对应版本的 OpenExplorer 文档。


3.4 编译配置

这里:

表示以降低单帧推理延迟为主要编译目标。

表示模型使用单个 BPU Core。

本文主要关注模型本身的单帧 latency,因此采用:

作为测试条件。


四、模型量化与编译

YAML 配置完成以后,就可以正式进行模型转换。

假设配置文件名称为:

执行:

也可以简写:

这是地平线 PTQ 工具链标准的模型转换方式。

整个过程并不是简单地:

而是会经历多个阶段:

官方文档中对应的主要产物包括:

本文编译完成后可以在:

看到类似:

如果最终:

正常生成,则说明整个 PTQ 量化和编译流程已经完成。

但这里需要明确:

HBM 能生成,只代表模型完成了转换,并不代表精度和性能一定满足要求。

后续仍然应该分别进行:

本文先继续进行板端性能测试。


五、J6 板端性能测试

模型最终部署性能建议直接在真实 J6 开发板上进行测试。

地平线提供:

工具,可以直接查看 HBM 信息、执行推理以及进行性能测试。官方工具定位也是直接在开发板上评估模型实际推理性能。

首先将:

拷贝到 J6 板端。


5.1 查看模型信息

可以先执行:

用于确认:

  • 模型名称

  • 输入节点

  • 输入 Shape

  • 输入数据类型

  • 输出节点

  • 输出 Shape

对于本文模型,重点确认:

模型信息确认正常后再进行性能测试。


5.2 测试模型 Latency

执行:

其中:

表示固定使用 Core 0。

表示单线程串行推理。

表示执行 200 帧模型推理。

在评估模型单帧 Latency 时,建议采用:

避免将多线程吞吐能力和单帧 latency 混在一起。地平线官方快速上手文档同样建议,评测 latency 时使用单线程串行推理,即将 thread_num 设置为 1。
执行结束后,hrt_model_exec 会给出模型:
  • 最小 latency

  • 最大 latency

  • 平均 latency

  • FPS

  • 总推理时间

等性能信息。

官方 perf 模式默认也会自动构造随机输入,因此如果当前目的只是单纯测量模型计算性能,并不需要额外准备真实图片。

5.3 查看详细 Profile

如果模型性能没有达到预期,还可以进一步增加:

例如:

工具运行完成以后会生成相应的 profiler 数据,可以继续分析:

  • BPU 子图执行时间

  • 调度开销

  • 算子执行情况

官方工具文档也支持通过 profile_path 输出详细性能信息。

因此,如果最后模型整体 latency 偏高,不建议立即去改网络。

可以先明确:

再决定是否需要:

  • 调整模型结构

  • 调整量化精度

  • 修改编译配置

  • 优化特殊算子

  • 减少高精度节点

  • 重新设计部分网络结构


六、总结

至此,一个完整的 PIDNet-S J6 PTQ 部署流程就已经跑通:

整个过程中,我认为最值得注意的是三个地方。

第一,部署使用的网络结构应该和训练网络区分开。PIDNet 训练时存在辅助输出,而部署时只需要最终主分割输出,因此 ONNX 导出使用 get_pred_model() 更加合适。
第二,Calibration 数据必须和浮点模型真实输入完全一致。本文选择 featuremap 输入,因此 RGB 转换、/255、mean/std Normalize 等操作全部提前在数据准备脚本中完成,YAML 中不再重复做 preprocessing。
第三,模型成功生成 HBM 并不等于部署完成。真正的部署结果最终仍然要看两件事:
本文先完成了 PIDNet 从开源 PyTorch 模型到 J6 HBM,以及板端性能测试的完整流程。后续如果模型量化精度下降,还可以继续结合 hb_verifier、量化敏感度分析、INT16/FP16 混合精度等工具和策略进一步进行定位和优化。

参考资料

本文部分内容参考:

  • PIDNet 官方开源工程及 README。

  • 地平线 OpenExplorer 算法工具链官方文档。

算法工具链
技术深度解析征程6
评论0
0/600