AI0000020
摘要:人车目标检测竞赛主要考察目标检测算法与 TPU 部署推理,主要考察算法选型与调 优,面向算能 TPU 迁移部署与推理加速两项能力;主要考核目标是算法效果(mAP)与推 理性能(单张图片推理时间)。针对这些要求,笔者从算法选型,算法优化,模型移植,推 理加速四步来实现该竞赛。首先在模型选型上权衡效果与性能,最终选择了 YOLOv7 标准模 型作为算法基础;然后使用了优化 anchor,数据增强等手段对该模型进行了效果优化;之后 根据算能平台 TPU 的特性,对模型进行部分修改完成了模型迁移;同时针对算能 TPU 的特 性,采用多 batch 方式优化推理速度,同时使用 numba 对 python 后处理部分进行加速。最 终算法部署在算能 TPU 上后取得了精度与性能兼顾的结果。
1. 算法选型
在目标确定后算法选型是最关键的一步,正确的算法选型能够节省大量的优化与部署时间。 因此需要根据竞赛目标与要求进行合理的算法选型。
1.1 类型选择
2D 检测在深度学习计算机视觉领域属于比较成熟的子领域,其算法经过多年迭代已经 有了很多成熟经典的结构。根据模型结构可以划分为三大类:RCNN,YOLO,Transformer。 参考 COCO 数据集的模型排行榜可以发现,RCNN 在模型效果上已经较为落后;YOLO 系列 效果居中;Transformer 系列一骑绝尘目前处于绝对领先的位置。
但是考虑到该竞赛不单单要求模型精度,同时还对部署与推理速度有要求。 Transformer 模型目前虽然效果领先,但是其推理性能相比前两者则大幅落后;同时其训练 也有一定要求,一般在更大的数据集上才能体现其能力。但是该竞赛进提供了 2885 张训 练数据,规模较小,比较难发挥 Transformer 的能力。同时训练这类模型对显存要求较高 (大于 16G),考虑到本人显卡局限性,遂放弃 Transformer 类算法。
在 RCNN 于 YOLO 的算法对比上,YOLO 在精度于性能两个方面均领先 RCNN。并且 考虑到 YOLO 以 conv 为主的模型结构在部署时的算子友好性也远超 RCNN;因此选择 YOLO 系算法作本次竞赛的算法基础。
1.2 算法选择
YOLO 系算法发展很快,目前也与很多成熟的算法可供选择,比如:YOLOv5,YOLOR, YOLOX,YOLOv7 等算法。截止该竞赛算法选型时,针对上述候选模型进行了精度与性能的 对比,YOLOv7 的数据明显处于榜首位置。
同时 YOLOv7 有多个不同版本,其中精度与计算量正相关,因此需要考虑这两个指标在竞赛 中的重要性来进行选择。经过测试性能,使用 1280 分辨率输入的模型使用 TPU 推理单张图 片时间超过 400ms;显然不符合要求。YOLOv7 以略差的精度在性能上大幅领先 YOLOv7- X,因此最终选择 YOLOv7 作为基础模型。
2. 算法优化
在模型训练时,对于提供的 2885 张图片进行划分,随机抽取其中 577 张作为验证集, 其余 2308 张作为训练集。首先针对该数据集,使用 YOLOv7 标准模型进行训练,发现 40 个 epoch 就能获得接近最优的精度。
然后基于标准模型进行优化,首先是针对配置中的 anchor size 进行优化,因为在该竞 赛任务中有很多小目标,如:远处的车辆,路边的自行车和行人等,box 的大小比较小。因 此使用 k-means 算法对 2885 张图片与对应的 label 进行了计算,计算出了针对训练数据的 anchor size,替换掉了原来的 anchor size;然后重新训练模型,mAP 提升了 1%。
由于训练数据只有 2000 多张图,数据规模不大,因此考虑使用数据增强的方法来提升 模型的泛化能力。这里借鉴了 YOLOX 的数据增强方法,主要使用 Masico 和 MixUp 方法度 数据进行增强,并且需要在训练的最后阶段关闭数据增强保持正常的数据分布。因此改为 50 个 epoch,前 30 轮打开数据增强,后 20 轮关闭进行训练,mAP 提升 3%。
通过对验证集的结果看,汽车的 mAP 较高,自行车和行人则略低。因此考虑对 mAP 较 低的两类 box 做针对性数据增强,针对这两类目标执行随机仿射变换操作用来提升这两类 的精度,同时也需要在最后阶段关闭该数据增强。因此改为 80 个 epoch,前 30 轮打开普通 数据增强,之后 30 轮打开类别数据增强,最后 20 轮关闭数据增强,mAP 提升 1%。
3. 模型移植
在训练好模型后,首先将模型导出为 TorchScript 模型,并对模型中可以合并的 Layer 进行合并,如 Conv + BatchNorm 等。在合并完成,导出 ONNX 模型,在导出 ONNX 模型 时固定输入的 shape,之后用 onnxsim 对 ONNX 模型进行图优化,在形状固定后对一写 Layer 进行常量传播优化。之后使用 bmneto 转换为 BModel 模型即可使用 sophon SDK 执 行推理。
3.1 转换问题
在获得 ONNX 模型后使用 bmneto 工具转换为 BModel 模型。在执行该步骤时遇到过 模型转换失败的问题(segment fault)。经过向工作人员咨询,BM1684 目前不支持 NMS 算子。本来该模型导出的 ONNX 时端到端模型,包含了 NMS 算子;因为不支持的问题需 要删除该算子,因此在导出模型时删掉了 NMS 算子,重复上述流程成功得到了 BModel 模 型。
同时因为模型中的 NMS 算子被删除,因此需要在推理时增加后处理环节,在模型推 理结束后增加 NMS 操作来完成整个模型的计算流程。
3.2 推理实现
SDK 示例中的 YOLOX 下的代码,分别实现了 C++版本和 Python 版本,首先测试发现 基于原始代码的实现 Python 版本性能略领先 C++版本,猜测可能是 numpy 的使用导致, 因此选择使用 Python 来实现算法推理。在推理代码实现时,后处理部分有一些参数需要选 择,下文介绍在这些参数选择带来的精度提升。
首先是 NMS 使用普通 NMS 还是 class-ware 的 NMS。针对训练数据进行分析可以发现, 当人骑自行车时,box 会有明显的重叠,因此这类情况需要 NMS 能够针对类别做;因此选 择 class-ware 的 NMS。对于 B 数据的实际提交来看,该选项的 mAP 为 76.83,而使用普通 NMS 的 mAP 为 70.92,具备明显优势。
同时 NMS 还有 2 个超参数:detection_threshold 和 nms_threshold。考虑到本次检测场 景有大量小目标,因此调低 detection_threshold 对于小目标的检测会有明显的提升,其副作 用是会导致 NMS 处理时间增加,一般 5ms 以内。因此将此阈值设置为较小值 0.001,相比 0,01 时的 mAP=76.83 提升到了 mAP=77.28。同时考虑到交通场景目标密集的情况较多, 因此考虑提升 nms_threshold 来提升对密集场景下目标的检测效果,将该值从 0.6 提升到 0.7, mAP 也从 77.28 提升到了 78.21。
4. 推理加速
在推理代码实现完成,超参数调整完毕后;模型精度确定,此时需要降低模型推理耗时。 首先考虑的是加大 batch 数提升并行度以利用 TPU 的算力。经过最高可以利用 4-batch 进 行推理,在 4-batch 推理时推理耗时平均为 318ms,1-batch 推理时平均耗时为 88ms。因 此 4-batch 单张图片平均耗时为 79ms,相比 1-batch 降低了 9ms,具有明显提升,因此代
码采用了 4-batch 模式进行推理。
为了提升模型精度,detection_threshold 和 nms_threshol 都进行了调整,让尽量多的检
测结果进入到 NMS 的 valid 部分,提升了 NMS 函数的计算量,导致 NMS 函数时间增加。 虽然 NMS 函数使用 numpy 实现,但是其中迭代部分使用 Python 中的 whlie 实现,经过 Profile 为该函数的性能热点部分,经过统计平均 NMS 的耗时为 5.9ms。
针对 while 循环的耗时问题,可以采用 JIT 的形式将循环变为 naïve code 执行来降低运 算时间,因此采用 numba 对 NMS 进行了 JIT,结果发现有明显的性能提升,但是首次执行 需要计算编译时间有超过 1000ms,显然无法接受。针对该问题使用了 numba 提供的 AOT 能力,指定 NMS 函数的输入类型并对其进行预编译得到一个动态库,在推理时直接 import 从预编译的库中导入函数即可。统计测试平均 NMS 耗时为 2.7ms,提升 3.2ms。
经过优化,单张图片的处理时间从 94ms 降低到 82ms。
5. 总结
之前竞赛在算子层面了解了算能 TPU 的设备架构与 okkernel 的开发流程。 本次竞赛让 我全流程的完成了一个算法从选型,训练,精度优化,部署落地,性能优化的链路,让我学 习了算能平台 SDK 的使用,掌握学习了模型转换工具的使用,Python Sophon API 的使用。 作为一个深度学习框架工程师从业者,感觉 Sophon SDK 整套工具链成熟度非常高,模型迁 移与部署流程简单,文档清晰,示例丰富,是一套非常好用的 DL 部署工具。