1.概述
最终,PIDNet的精度超过了所有具有相似推理速度的现有模型,在Cityscapes和CamVid两个主流的道路场景解析数据集上实现了最佳的推理速度和准确度的平衡。其中:
基于RTX 3090,PIDNet-S在Cityscapes数据集上的推理速度为 93.2 FPS(输入大小为1024x2048),mIOU 为0. 786;
基于RTX 3090,CamVid数据集上的推理速度为 153.7 FPS(输入大小为720x960),同时 mIOU 为 0.801。

鉴于PIDNet模型精度和性能的优异表现,本文将介绍PIDNet_S网络在地平线征程5计算平台(J5)的部署流程。
2.性能精度指标

deeplabv3plus_efficientnetm2参考算法数据来源:地平线参考算法版本发布帖
3.环境准备
3.1 代码库下载与解压
权重下载链接在README.md中
3.2 数据集准备
本文将基于Cityscapes 数据集进行开发。
- 下载 Cityscapes 数据集之前需要在 官方网站 注册一个账号:

然后在 下载页面 下载需要的数据集文件, 这里我们只需要下载 gtFine_trainvaltest.zip 和 leftImg8bit_trainvaltest.zip 两个文件。
将 gtFine_trainvaltest.zip 和 leftImg8bit_trainvaltest.zip 解压至PIDNet-main/data/cityscapes文件夹下,目录结构如下所示:
3.3 环境部署
本文将基于地平线算法工具链的OE1.1.68的Docker进行开发。
- 访问地平线开发者社区中的OpenExplorer算法工具链 版本发布帖,下载OE1.1.68的GPU Docker:

- 参考用户手册完成Docker环境的部署:

此外,还需要在OE1.1.68的Docker中安装以下两个库:
4. PIDNet网络结构

PIDNet 网络主要由P、I、D分支、Pag、PAPPM和Bag模块组成,下面将分别对其进行介绍。
4.1 P、I、D分支
P、I、D分支具有互补的职责:
比例分支P负责解析和保留高分辨率特征图中的详细信息;
积分分支I负责聚合局部和全局的上下文信息以捕获远距离依赖;
微分分支D负责提取高频特征以预测边界区域。
PIDNet采用了级联残差块作为Backbone。此外,为了更加高效,作者将 P、I 和 D 分支的深度设置为较浅、适中和较深。因此可以通过加深和加宽模型来生成不同大小的 PIDNet 模型,即PIDNet-S、PIDNet-M和PIDNet-L。
- 代码路径:PIDNet-main/models/pidnet.py
4.2 Pag:选择性学习高级语义信息
在PIDNet中,I分支的网络深度相对于P和D分支较深,其提供的丰富而准确的语义信息对于P和D分支的细节解析和边界检测至关重要。PIDNet将I分支视为其它两个分支的备份,并使其能够向它们提供所需要的信息。为了P分支能更好地融合I分支的语义信息,引入了Pag模块。
作用:
Pixel-attention-guided fusion, Pag, 即像素注意力引导模块,就是将比例P和积分I分支的特征利用一个注意力机制进行交互增强。
结构:
模型结构如下图所示:

4.3 PAPPM:上下文的快速聚合
Pyramid Pooling Module, PPM,主要用于构建全局场景的先验信息。PPM首先对不同尺度的特征图进行池化操作,然后将不同尺度的池化特征图进行拼接,形成局部和全局上下文的表示。虽然PPM能够很好地嵌入上下文信息,但它的计算过程无法并行化,非常耗费计算时间,而且对于轻量级模型来说,PPM 包含的每个尺度的通道数太多,可能会超过这些模型的表示能力。
作用:
Parallel Aggregation PPM, PAPPM,即并行聚合PPM,基于DAPPM中修改了其中的连接,使其可以并行化,并且将每个尺度的通道数量从128减少至96,提升了计算速度。
结构:

4.4 Bag :平衡细节特征和上下文
给定ADB提取的边界特征,使用边界注意力来指导细节(P)和上下文(I)表示的融合,即设计了一个边界注意力引导的融合模块(Boundary-attention-guided,Bag)。
作用:
上下文分支I在语义上是准确的,但它丢失了太多的空间和几何细节,尤其是对于边界区域和小目标。细节特征分支P更好地保留了空间细节,所以迫使模型沿着边界区域更多地信任细节分支P,并利用上下文特征来填充其他区域。
结构:

5. 浮点训练
本文没有重新进行浮点训练流程,直接使用了官方代码库链接中提供的PIDNet的pt。
6. 浮点模型精度验证
浮点模型训练完成以后,通过指定训练好的TEST.MODEL_FILE 来评估模型在验证集/测试集上的精度。在官方代码库提供的链接中下载PIDNet_S在CityScapes验证集下的权重PIDNet_S_Cityscapes_val.pt,将其放在PIDNet-main/pretrained_models/cityscapes目录下。
然后运行以下命令:
命令运行结束后,在终端会打印PIDNet_S在CityScapes验证集下的精度,如下所示:

7. Calibration
浮点模型训练完成且已经满足精度要求后,那么我们就可以开始模型的量化编译流程了。
7.1 模型改造
在做QAT前,需要对浮点模型做必要的改造,除了要在模型输入前后插入分别 QuantStub和 DequantStub这样的基本操作外,还要对fx模式下模型中不支持的结构进行替换。
7.1.1 插入 QuantStub和 DequantStub节点
7.1.2 替换fx模式下不支持的结构
改动1:
修改Pagfm类中的forward函数,将torch.sum()函数中的keepdim参数配置为True:
J5硬件平台对算子支持为4维支持,所以在做sum计算时需要配置keepdim参数为True保证输出Tensor为4维,否则在calibration时会报错。
改动2:
修改Pagfm类中的forward函数,替换(1-x)的计算为(x+(-1))*(-1):
模型中如果存在(1-x)这种第一个数是常量的计算,fx模式下的逻辑是不自动进行算子替换,所以需要对类似的计算做一下替换,即将(1-x)的操作修改为(x+(-1))*(-1)。
改动3:
和改动2类似,修改Light_Bag类中的forward函数,替换(1-x)的计算为(x+(-1))*(-1):
改动4:
和改动2类似,修改DDFMv2类中的forward函数,替换(1-x)的计算为(x+(-1))*(-1):
7.1.3 calibration.py脚本
模型改造完成以后,在PIDNet-main/tools文件夹下创建calibration.py脚本来做模型的Calibration,代码如下所示:
模型高精度输出详细配置方式见附录。
7.2 Calibration命令
完成上述准备工作后,运行calibration命令:
8. 精度验证
8.1 精度验证脚本编写及推理代码修改
8.1.1 精度验证脚本编写
为了简化精度验证的步骤,在PIDNet-main/tools文件夹下创建predictor.py脚本来做float模型、calibration模型、量化模型的精度验证,代码如下所示:
8.1.2 推理代码修改
运行predictor.py脚本前,需要修改config文件和推理代码:
修改PIDNet-main/configs/cityscapes/pidnet_small_cityscapes.yaml中TEST字段中的OUTPUT_INDEX为0;
修改PIDNet-main/datasets/base_dataset.py中inference函数的代码,如下:
8.2 Calibration精度验证
完成上述准备工作后,运行精度验证脚本:
命令运行结束后,在终端会打印Calibration模型在CityScapes验证集下的精度,如下所示:

可以看出,calibration模型的精度(MeanIU 0.7871)是浮点模型的精度(MeanIU 0.7876)的99.94%,满足精度要求,那么我们就直接走量化编译的流程。 如果calibration模型的精度比浮点模型精度低很多,建议参考手册继续进行量化感知训练。
8.3 量化模型精度验证
calibration模型满足精度要求后,我们首先使用convert_fx接口将其转化为量化模型,然后运行predictor.py 脚本验证其在CityScapes验证集下的精度,如下所示:
命令运行结束后,在终端会打印量化模型在CityScapes验证集下的精度,如下所示:

可以看出,量化模型的精度(MeanIU 0.7874)是浮点模型的精度(MeanIU 0.7876)的99.975%,满足量化精度要求,下面我们直接进行编译。
如果量化模型精度不及浮点模型精度的99%,建议使用地平线QAT中的精度debug工具来进行精度问题排查和调优。
9. 模型编译及板端性能实测
9.1 编译脚本编写
在PIDNet-main/tools文件夹下创建compile_perf.py脚本来实现模型的编译、静态perf和模型结构可视化,代码如下所示:
9.2 编译命令及产出物
完成脚本创建后,运行编译命令:
运行完成后,在model_path下会有以下产出物:
9.3 板端性能实测
运行结果:

10. 单帧可视化
为了便于查看不同阶段的模型推理单帧图像的可视化结果,编写了PIDNet-main/tools/infer.py脚本来实现浮点模型、calibration模型和量化模型单帧推理结果的可视化,infer.py脚本代码如下:
参数介绍:
--a:模型规格,默认pidnet-s
--c:是否加载预训练模型,默认True
--stage ,-s:配置推理模型的阶段,可选float,calib or int_infer,默认”float“
--p:load浮点模型的路径
--r:输入图像路径,默认是'./input_sample'
--t:输入图像的格式,默认是“.png”
可视化运行命令:
可视化结果:

11. 附录
问题1
prepare_qat_fx报错:

解决办法:
答:修改torch.sum算子的keepdim参数为True,对应7.1.2节改动1。
问题2
calibration时forward报错:

**报错原因:** fx目前的逻辑是如果减法中的被减数是常量,就不自动进行算子替换了。
解决办法:
答:把减法改成加法,对应7.1.2节改动2,改动3,改动4。
问题3
Calibration 模型forward报错:

报错原因:
int32高精度层配置错误,导致中间层的conv出现了int32的输入
解决办法:
打印浮点模型,查看模型尾部conv的name,如下所示:

可以看出,输出层的name是final_layer.conv2,那么我们就对应修改prepare_qat_fx中的配置:

如何确定final_layer.conv2已被成功配置为了int32输出?
答:打印calibration model,查看final_layer的conv2算子是否有(activation_post_process): FakeQuantize,如果没有,那么已经成功配置为了int32输出。下面为成功配置为int32高精度输出的情况:

12. 参考资料
https://zhuanlan.zhihu.com/p/626535727

