ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Atlas 300V 24G部署YOLO实战:从ONNX转OM到AscendCL推理全流程

Atlas 300V 24G部署YOLO实战:从ONNX转OM到AscendCL推理全流程 1. 先搞明白Atlas 300V 24G是块什么卡1.1 它不是显卡却总被当成显卡用很多人拿到Atlas 300V 24G的第一反应是“这玩意儿是不是类似RTX 3090的东西”实际上这个理解从一开始就走偏了。Atlas 300V 24G是昇腾生态里一款面向AI推理场景的加速卡核心处理器是昇腾310P系列NPU不是通用GPU。那“24G”是什么呢是板载的24GB显存准确说是DDR4/LPDDR4X之类的内存颗粒。这个容量在推理卡里算比较宽裕的能从容吃下当前主流的检测模型、分割模型甚至一些轻量级多模态模型。但你要真拿它跑训练会非常难受因为从硬件设计到软件栈它压根没往训练这个方向做。这块卡的典型形态是一张半高半长的标准PCIe卡插在服务器或者边缘盒子里面没有显示输出口没有风扇直吹也问题不大部分被动散热型号。它要干的事情很纯粹把训练好的模型拿过来安安静静、低功耗地把推理跑起来单卡功耗一般在70W到90W之间跟一块动辄350W的旗舰游戏卡完全不是一个路子。1.2 产品定位与适用场景昇腾推理卡有几个常见系列Atlas 300I系列和Atlas 300V系列是最容易遇到的。300I主打通用的推理加速适合边缘服务器、智能盒子300V系列则更偏视频和图像分析场景像平安城市、智慧园区、工业质检这类以视频流为入口的业务是300V的主场。300V 24G这个规格在显存上做了加大意味着你可以同时加载更大的模型或者在同一个NPU上驻留多个模型实例服务多路业务。所以第一个问题的答案Atlas 300V 24G是运算加速卡吗严格说它是AI推理加速卡不是通用计算卡也不是图形显卡。它的“运算加速”范围限定在深度学习的推理算子比如卷积、归一化、池化、矩阵乘这类。想拿它跑CUDA程序、图形渲染或者通用并行计算门都没有。搞清楚这层定位再去谈“atlas部署yolo”你才知道后面每一步为什么要那么做。2. 部署YOLO的整体思路先从GPU思维里跳出来2.1 部署链路PyTorch模型在NPU上的“翻译”过程如果你玩过GPU上的YOLO部署对这条链路应该很熟PyTorch权重导出为ONNX再用TensorRT做引擎优化最后在GPU上跑。昇腾部署的思路骨架很像但工具链换了一整套PyTorch权重导出为ONNX再用ATCAscend Tensor Compiler转换成OM离线模型最后通过AscendCL或者MindX SDK加载执行。这个过程里ONNX相当于中间语言ATC负责把ONNX里的算子翻译成昇腾NPU能执行的指令。为什么中间要隔一层ONNX因为昇腾不可能为每个深度学习框架写一套编译器ONNX是目前生态兼容性最好的模型交换格式PyTorch、TensorFlow都有稳定的导出工具选它做枢轴省事又稳妥。注意这里的翻译不是机械的逐算子对照而是包含算子融合、内存复用、指令调度等大量优化过程。ATC转换出来的OM模型形态上类似GPU世界的TensorRT engine是一个已经编排好的执行包NPU直接照着跑就行。2.2 能不能直接在卡上跑PyTorch模型有人会问不转ONNX行不行PyTorch直接用行不行答案是能跑但有条件。昇腾官方提供了torch_npu插件让PyTorch在训练和推理时可以把张量放到NPU上执行。但用torch_npu跑YOLO本质上是PyTorch调用了NPU算子中间还是走了一层昇腾的算子适配。性能上和先转OM再用AscendCL加载跑往往有不小差距因为ATC在离线阶段做了非常充分的静态优化而在线模式下很多优化做不了。所以我个人的建议是如果做产品化部署、追求性能和稳定性一定要走ONNX转OM这条路如果只是开发阶段调试、快速验证算法效果可以直接用torch_npu凑合跑一下。两种方式的工具链要求不一样下面正文里我更多以生产部署视角来讲。2.3 整条部署链路的组成模块我们来看一张完整的部署拓扑脑图模型侧PyTorch/YOLOv5/YOLOv8 权重 ↓ export.py 导出 中间层ONNX 文件 ↓ ATCAscend Tensor Compiler 运行侧OM 离线模型 ↓ AscendCL / MindX SDK 调用 硬件侧Atlas 300V 24G310P NPU看起来不复杂但每一层都有自己的坑。比如ONNX导出时如果模型里有特殊算子没有注册导出就直接报错ATC转换时如果算子不支持又得想办法绕到了运行侧还得处理好图像预处理、输出解码、NMS这些后处理逻辑。后面我逐个环节说细一点。3. 部署实操从环境搭建到YOLOv5真正跑起来3.1 环境准备与版本对齐这是很多新手第一道坎也是我见过翻车最频繁的地方。昇腾整个软件栈由驱动、固件、CANN工具包、配套框架插件组成版本之间存在严格的对应关系。我在一次项目里就因为Driver版本和CANN版本不匹配折腾了整整一天最后发现就是版本错位导致NPU初始化失败。以我当时跑通YOLOv5的较稳定组合为例列个表给大家参考组件版本建议说明操作系统Ubuntu 20.04 / 22.04 x86_64或aarch64较老的内核需要确认适配昇腾NPU驱动 固件23.0.3及以上版本必须匹配CANNCANN Toolkit7.0.0或更高包含ATC、AscendCL运行库PyTorch1.11.0或2.x需匹配torch_npu需要安装对应版本torch_npu插件torch_npu与CANN配套用于PyTorch在线推理/迁移验证模型YOLOv5 v7.0 / YOLOv8后续示例基于YOLOv5拿到一张新的Atlas卡第一步是装驱动。驱动和固件安装包可以从昇腾社区下载安装过程没有太多需要自定义的东西按默认走就行。安装完成后用npu-smi info命令能看到NPU状态这一步成功说明底层打通了接下来装CANN才有意义。提示npu-smi是昇腾自己的NPU状态查询工具类似GPU下的nvidia-smi。建议先用它确认卡能被系统识别再继续后面的步骤。常见问题多半发生在系统内核版本兼容性上比较新的Ubuntu内核有时候会对不上驱动支持列表。CANN安装也简单下载社区版toolkit包后解压、执行安装脚本、设置环境变量就算安装完成。但你一定要做一件事检查环境变量是否正确生效。我当时被坑过的地方就在这儿——以为装好了结果shell里没有source环境变量文件命令都找不到。装完CANN记得执行source /usr/local/Ascend/ascend-toolkit/set_env.sh如果你希望每次登录都自动加载可以把这一行追加到~/.bashrc里。3.2 PyTorch导出ONNX模型环境就绪后先从模型侧出发。以YOLOv5 v7.0为例官方仓库自带导出脚本。你需要先把权重下载下来比如yolov5s.pt然后执行python export.py --weights yolov5s.pt --include onnx --dynamic False --img-size 640 640这里有个值得注意的选择动态shape还是静态shape。从部署稳定性角度我强烈建议静态shape。原因是动态shape在NPU上不仅转换耗时更长推理阶段还可能因为动态shape导致图重编译或次优调度性能明显不如固定shape的静态图。Atlas 300V 24G这种推理卡应用场景里图像尺寸往往就是固定的比如640x640、960x960没必要为了“灵活”牺牲性能。导出后你会得到一个yolov5s.onnx文件。导出过程如果报算子不支持的错误多半是模型里用了较新的算子可以尝试升级torch版本或者修改导出脚本里的算子映射。绝大多数主流YOLO变体都不会有大问题。3.3 ATC工具转换OM模型拿到ONNX后用ATC做离线转换。命令看起来长其实每一项都有道理。atc --modelyolov5s.onnx --framework5 --outputyolov5s_om \ --input_shapeimages:1,3,640,640 \ --soc_versionAscend310P3 \ --insert_op_confaipp_yolov5s.cfg \ --output_typeFP32几个关键参数挨个说明framework5表示输入模型是ONNX格式ATC内部要靠这个区分模型来源。soc_versionAscend310P3则是指定NPU芯片型号Atlas 300V 24G对应的是昇腾310P系列如果你不确定具体型号可以用npu-smi info查看或者查阅产品手册确定该填Ascend310P3还是别的。填错了会直接转换失败或者生成一个当前卡跑不了的模型。insert_op_conf导入的是AIPP预处理配置。AIPP是啥简单说它可以把图像预处理比如缩放、减均值、除以255、色域转换从CPU侧搬到NPU侧让NPU在加载输入数据时直接完成预处理。这样做的好处是减少CPU和NPU之间的数据搬运次数提升整体流水线效率。我当时使用的aipp配置大概长这样aipp_op { aipp_mode: static input_format: RGB888_U8 src_image_size_w: 640 src_image_size_h: 640 csc_switch: true rbuv_swap_switch: false min_chn_0: 0 min_chn_1: 0 min_chn_2: 0 var_reci_chn_0: 0.003921569 var_reci_chn_1: 0.003921569 var_reci_chn_2: 0.003921569 }这段配置的含义是输入是RGB 8bit图像尺寸640x640AIPP会把每个像素的RGB值减去最小值0再乘以1/255完成归一化。如果你的模型训练时用的归一化方式不是单纯除以255就要相应调整这个配置否则推理精度会掉得你怀疑人生。转换成功后会生成一个yolov5s_om.om文件。到了这一步模型侧的工作就算是干完了接下来是写推理代码。3.4 用AscendCL写一个最简推理DemoAscendCL是昇腾提供的一组C语言API也有Python接口。它的设计风格和CUDA runtime有不少神似的地方比如概念上有Context、Stream、Device熟悉GPU编程的人学起来比较顺。一个最基本的推理流程用Python实现大概是这样的import numpy as np from ais_bench.infer import InferSession # 创建推理会话指定device 0加载om模型 session InferSession(device_id0, model_pathyolov5s_om.om) # 构造一个batch为1的输入dtype为float32shape为 [1,3,640,640] fake_input np.random.randn(1, 3, 640, 640).astype(np.float32) # 模型推理 outputs session.infer(feeds[fake_input]) print(len(outputs), outputs[0].shape)如果你不想直接用底层API昇腾还提供了一套MindX SDK负责把推理、图像解码、后处理等环节封装成插件流水线业务代码可以写得更少。但底子还是AscendCL建议有一定基础后再考虑SDK。上面这段代码里的InferSession是昇腾社区开源出来的一个Python推理封装内部处理了设备初始化、模型加载、输入输出内存分配这类繁琐的东西。实际项目中可以直接找ais_bench这个工具它自带离线批处理推理能力特别适合先用它验证模型转换结果。拿到模型输出之后真正的检测结果还需要做一整套后处理解析特征图、计算边界框坐标、置信度过滤、NMS去重。因为YOLOv5的输出头比较复杂这里不能直接偷懒需要自己写。后处理可以往下放你可以选择在Python里跑也可以放在C侧。从效率角度考虑生产环境建议用CPython作为原型验证完全够用。下面我贴一段后处理的核心逻辑参考基于YOLOv5的输出格式def post_process(outputs, conf_thres0.25, iou_thres0.45, img_shape(640, 640)): # 第一个输出通常是 [1, 25200, 85] predictions outputs[0][0] # [25200, 85] # 前4列是box坐标第5列是objectness6~85是80类得分 boxes predictions[:, :4].copy() scores predictions[:, 4:5] * predictions[:, 5:] final_boxes, final_scores, final_classes [], [], [] # 对每个类别做阈值过滤和NMS class_num scores.shape[1] for cls_id in range(class_num): cls_score scores[:, cls_id] keep np.where(cls_score conf_thres)[0] if len(keep) 0: continue # 按分数排序后做NMS ... return final_boxes, final_scores, final_classes说白了模型部署到这里推理部分只是把输入塞进NPU、取出输出大量逻辑权重仍然在你的业务代码上。画好这个边界后续调优才不至于混成一团。3.5 性能测试看一下24G显存卡的底力模型能出结果了接下来自然关心速度。Atlas 300V 24G跑YOLOv5s分辨率640x640纯推理耗时大概在5毫秒到8毫秒之间折算成吞吐大约是每秒120帧到200帧。当然这个数字受batch、图像复杂度、NPU频率、CANN版本影响不能当绝对值但量级可以作为参考。要压出极限吞吐最简单的一个手段是加大batch。由于Atlas 300V 24G显存足够大一个batch塞16张甚至32张640x640的图像完全没有压力。模型转换时使用动态batch配置就能在推理时灵活调整batch大小。实践里我们在batch8时性能收益最明显再往上走性能涨幅变缓边际收益递减。批量推理时图像预处理、数据搬运、模型推理、后处理这四者要尽量流水线化。单线程串着跑肯定会浪费NPU的计算能力正确姿势是把前处理和后处理放到独立线程里让NPU尽量不停机。4. 部署中的高频坑与排查技巧4.1 版本不匹配是最隐蔽的问题昇腾的硬件和软件绑定很紧密Driver、Firmware、CANN、torch_npu必须满足配套关系。我遇到过一次CANN 7.0.0装了但驱动还是老版本跑demo时直接报设备初始化的错误。解决起来倒不难就是从官方配套表里按版本重新装驱动。给个建议装之前先确定你想用哪个CANN版本然后严格按配套表找对应驱动和固件。不要图新稳定性优先。很多人一上来装最新版结果固件升级出幺蛾子。4.2 AIPP配置不当导致精度垮掉如果你发现转换后的OM模型跑出来的框明显不对置信度几乎为零九成是AIPP配置和训练时的预处理不一致。YOLOv5训练默认用COCO数据集预处理是resize到640后除以255没有减均值和方差所以在AIPP里我配了var_reci_chn_0: 0.003921569这就是1/255的小数表示。如果你的模型是迁移学习或者自定义数据集这个值必须改成和训练一致的逻辑。排查窍门先在平台上用PyTorch跑一张固定图片得到标准输出再把同一张图片喂给OM模型对比两边的框和置信度。若差异巨大按“预处理-推理-后处理”三个环节逐段debug。4.3 图像缩放方式不统一YOLO系列通常用letterbox也就是等比缩放后填充灰边把图像变成640x640。这个操作如果在AIPP层做配置复杂度会上升如果不做就要在CPU侧处理好再送进NPU。常见坑是用户直接粗暴resize成640x640破坏了长宽比导致小目标检测效果骤降。最稳妥的办法CPU侧用opencv做letterbox算好填充偏移量然后把预处理后的数据送到NPU推理后处理时再把这些偏移量映射回原图坐标。AIPP适合简单像素级操作复杂几何变换建议别用它。4.4 错误日志怎么看交付阶段最烦的问题就是设备报错但看不懂日志。昇腾相关的日志默认存放在~/ascend/log/目录下里面会有plog进程日志和slog系统日志。报错时先看plog错误码通常很明确如果定位不清再开debug级别日志看详细流程。另外两个常用命令npu-smi info npu-smi info -t board -i 0第一条看NPU利用率、温度、显存占用第二条看板卡固件信息。排查设备异常时这两条命令基本够用。5. 从YOLOv5移植到YOLOv8的额外注意点我自己的多数项目还停留在YOLOv5上但YOLOv8这两年也成了不少新项目的主角。如果你想在Atlas 300V 24G上部署YOLOv8流程主体和YOLOv5完全一致区别主要在导出和输出解析上。YOLOv8的模型结构里有部分C2f模块和fasternet类结构的算子导出ONNX时有一定概率出现算子兼容性问题。遇到这种情况先升级CANN到较新版本因为算子支持一直在扩充如果还不行可以把不支持的子结构替换成等价算子再导出。输出解析上YOLOv8去掉了objectness分支每个anchor只输出边界框和类别得分。这意味着后处理里少乘一个置信度逻辑反而更简单了。其他NMS之类的套路完全一致。从性能角度看YOLOv8s在300V 24G上的推理速度对比YOLOv5s略慢一些毕竟模型结构更重实测大概会慢10%到15%。如果业务对延迟敏感继续用YOLOv5可能更合适如果追求精度YOLOv8值得那一点性能代价。6. 最后落地的几点经验与建议在Atlas 300V 24G上部署YOLO整个流程走下来我的建议是第一步别急着上产品化先用官方镜像和示例跑通端到端第二步把模型转换脚本、后处理模块、性能测试工具沉淀成团队内部模板后面换模型就能快速复用第三步再考虑容器化、K8s调度、多路视频流并发等更复杂的事情。这套卡对视频流推理场景确实友好24G显存意味着你可以在一个NPU上驻留多个模型或大batch并发对多路摄像头业务的支撑能力比想象中好。但因为它的软件生态和GPU差异很明显无论你多熟悉PyTorch和TensorRT都得预留出至少一周的学习缓冲时间。如果让我再提一条最想强调的经验那就是尽量在CANN和驱动版本确定以后创建一套标准的Docker镜像并且在镜像里把所有模型转换工具链锁死版本。昇腾软件栈的版本耦合度比较高镜像一旦验证通过后续直接复用能省掉大量环境重装的烦恼。项目部署到客户现场时一个干净的镜像比一百行部署文档都管用。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进