多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Atlas 300V 24G上跑通YOLOv8:从PyTorch到OM的完整部署实践

Atlas 300V 24G上跑通YOLOv8:从PyTorch到OM的完整部署实践 第一次拿到 Atlas 300V 24G 这块卡的时候我盯着产品页愣了好一会儿它到底算不算一张运算加速卡这个困惑我在不少群里都见过做视觉的朋友第一次接触这张卡问的最多的就是“YOLO怎么跑上去”。毕竟平常跑 YOLO 都是拿 GPU 一把梭突然换成昇腾这套 NPU 工具链初期很容易被文档和版本折腾到怀疑人生。这篇文章把我从 PyTorch 权重最终把 YOLOv8 跑在 Atlas 300V 24G 上的完整链路捋一遍中间踩过的坑、验证过的方式、以及后来在生产环境里的调优思路都会写清楚。适合准备在昇腾环境里部署目标检测模型的算法工程师和运维同学也适合刚拿到卡还不知道从哪下手的初学者。1. 一张24GB显存的推理卡先把它和GPU的边界划清楚1.1 Atlas 300V 24G 的定位与核心规格先正面回答那个被搜索了很多次的问题Atlas 300V 24G 是运算加速卡吗是但它不是我们熟悉的通用计算 GPU。它是一张AI 推理加速卡核心是昇腾 310P 系列的 AI 处理器主打数据中心场景下的视频分析、目标检测、图像分类这一类推理任务。它和训练卡最大的区别在于它的设计目标不是反向传播、自动求导而是把训练好的模型以最高性价比跑起来。规格方面网上的说法各不一样我以官方 datasheet 为准强调几个印象最深的点显存 24GB这在推理卡里算是很宽裕的能装下不少大模型和多路输入。INT8 算力大约在 140 TOPS 级别FP16 大约 70 TFLOPS 级别这是官方宣传的峰值实际业务里能跑出一半以上就算不错。功耗很低几十瓦的量级不需要外接供电PCIe 槽供电就够。半高半长卡普通服务器里能塞进去这对边缘机房和老旧服务器非常友好。具体到 YOLO 场景我实际体会是这卡的舒适区就是 YOLOv5/YOLOv8 这种结构规整、算子常见的 CNN 检测模型。你把分辨率固定在 640x640跑 FP16 或者 INT8都能获得相当能打的吞吐。1.2 它不是GPU编程模型和算子边界完全不同第一次在 Atlas 上跑 YOLO 的人最容易踩的坑就是习惯性用 GPU 的思路去套。你在 PyTorch 里写的 model(x)底层走的是 CUDA 那一套生态到了昇腾这里完全行不通。两张卡跑同一个模型的路径是完全不一样的GPUPyTorch 模型 → CUDA / TensorRT直接在显存里执行。AtlasPyTorch 模型 → 导出 ONNX → 用 ATC 工具转换为 .om 格式 → 通过 CANN 的 ACL 接口在 NPU 上执行。也就是说Atlas 不是把 PyTorch 模型拿来就能直接跑的。它需要一次离线编译转换把模型里的算子转换成昇腾达芬奇架构上能执行的指令。转换这件事既是昇腾最大的门槛也是它性能好的原因——提前把算子和内存布局都优化好了推理时不需要像 Python 解释器那样按算子调度。还有一个必须提前有心理预期的点算子支持有边界。GPU 生态里做了一些非常花哨的自定义算子的话转换时大概率会碰到“算子不支持”的报错。YOLO 这种主流模型基本没有问题但如果你的模型里加了很冷门的注意力模块或者奇怪的自定义层就要做好改结构或者把部分计算挪到 CPU 的心理准备。1.3 先确认你的场景到底买推理卡还是训练卡看到 24GB 显存很多人第一反应是拿来训练。我的建议是如果是公司选型别拿 Atlas 300V 24G 当训练卡用。它是推理加速卡训练生态相对有限你在 PyTorch 里跑通一个训练脚本和在 Atlas 上训练是两码事。适合它的场景非常明确已经训练好的 YOLO 模型要部署到服务器上做实时推理。多路视频流接入做目标检测、流量统计、工业质检。单机功耗和体积受限塞不进大功率 GPU 的机房。我自己的项目就是典型的第二个场景十几路 RTSP 视频流每路跑 YOLOv8s 做检测。之前用 GPU 方案功耗高、卡位紧张换到 Atlas 300V 24G 之后一张卡就能扛下来整体功耗降了一大截。这就是这块卡最核心的价值。2. PyTorch到OMYOLO上山的完整链路长什么样2.1 四个关键环节PyTorch、ONNX、OM、ACL整个部署链路其实可以压缩成一张地图你要做的就是在四个环节之间把模型和接口串起来。第一个环节是 PyTorch。这是模型诞生的地方YOLOv8 也好 YOLOv5 也好训练完得到的 .pt 权重不是最终交付物而是中间产物。我们要做的是把它导出成 ONNX。第二个环节是 ONNX。它相当于一个中间语言的模型文件不依赖任何具体框架。昇腾的 ATC 工具本身不认识 .pt 文件但认识 ONNX所以导出的时候建议用 opset 11 到 17 之间的版本太老会导致一些算子转不出来太新可能昇腾算子库还没跟上。第三个环节是 OM 模型。这是昇腾自己的模型格式。ATC 工具读取 ONNX经过算子匹配、图编译、内存布局优化之后输出一个 .om 文件。这个文件才是后面推理真正加载的东西.om 是跟着硬件绑定的换一张不同型号的卡大概率要重新转换。第四个环节是 ACLAscendCL。它是昇腾的编程接口类似 CUDA 的作用。你的推理程序通过 ACL 加载 .om 模型把图像数据从 CPU 拷贝到 NPU执行推理再把结果取回来。这四个环节一旦理清楚你后面遇到任何报错都能快速定位是转换阶段的问题还是推理阶段的问题。2.2 三种落地方式怎么选ACL直写、MindX SDK、MindSpore Lite我在部署的过程中发现昇腾生态里至少有三条路可以把 YOLO 跑起来新手很容易被它们搞糊涂我放在一起对比一下。方式适合场景优点缺点纯 ACL 编程对性能和流程控制要求高完全可控可自定义后处理性能调优空间最大代码量大图像解码、缩放、后处理全要自己写MindX SDK快速出 demo视频流接入pipeline 插件化解码、缩放、推理、后处理都封装好了定制灵活性差出问题需要翻插件源码MindSpore Lite整条链路都在 MindSpore 生态对 MindSpore 模型支持好其他框架转过来反而绕路不值得我个人的建议非常明确先用纯 ACL 把流程跑通再决定要不要往 MindX SDK 上靠。纯 ACL 的好处是你能看清楚每一步到底发生了什么。图像是怎么进 DVPP 的模型输入输出到底是什么 shape后处理的数据是从哪个指针取出来的。这些东西在 MindX SDK 里都被封装成了黑盒一旦出现精度不对或者性能不达标你连排查的方向都没有。等你的 ACL 版本跑通了如果业务是接视频流可以再试试 MindX SDK 的 pipeline通常能把代码量缩短一半以上。但至少你心里有底知道后面每个插件背后大概做了什么。3. 环境搭建驱动、固件、CANN的版本配合3.1 一套能跑通的安装顺序环境搭建阶段是最容易让人想摔键盘的环节尤其是第一次装昇腾环境。驱动版本、固件版本、CANN 版本、MindX SDK 版本四者之间必须有明确的匹配关系一旦不匹配轻则功能异常重则 driver 都起不来。经过我自己装机和帮同事排查的经验最稳妥的顺序是这样的先确认操作系统内核版本Ubuntu 20.04/22.04 是支持度最高的系统。安装 GPU 无关的依赖工具比如 gcc、make、linux-header驱动安装时要用 dkms 编译内核模块缺少头文件会直接失败。安装昇腾 NPU 驱动和固件drvier firmware。这一步决定 NPU 在系统层面能不能被识别。用 npu-smi info 验证卡是否正常。安装 CANN toolkit它提供 ATC 转换工具、ACL 运行时、算子库。最后按需安装 MindX SDK它依赖 CANN 环境。顺序反了会出问题。最常见的是先装了 CANN 再补驱动导致环境变量和库文件版本混乱后面跑 ACL 程序的时候报一些特别诡异的库加载错误。安装驱动的时候有一个细节容易被忽略驱动和固件是两个东西需要分别安装。很多人只装了驱动不装固件结果 npu-smi 能看见卡但一跑推理就报“Device is not ready”。我自己第一次就踩在这个坑里后来老老实实把固件补上才正常。3.2 npu-smi 和宿主机验证手段驱动装完之后第一件事永远是执行这条命令npu-smi info正常的输出能看到卡的型号、芯片温度、算力占用率、显存占用等信息。如果这里报错或者看不到卡后面就不用继续了先把环境解决干净。这里有一个判断环境的技巧当你装好了 CANN 之后再跑 npu-smi有时候会遇到命令行工具版本对不上的问题。这是因为 CANN 自带的 npu-smi 和驱动自带的版本不一致导致的以驱动自带版本为准即可。装完 CANN 后还需要做一件事就是 source 环境变量source /usr/local/Ascend/ascend-toolkit/set_env.sh建议直接写进 ~/.bashrc 里不然每次新开终端都找不到 atc 和 acl 的命令。验证 ATC 是否存在atc --version能正常打印版本号就说明 CANN 工具链基本就绪了。3.3 Docker环境下的设备映射生产环境里大家大概率会用 Docker 部署这里坑更多。昇腾官方提供了专门的容器运行时基于 Docker 再加一层ascend-docker-runtime。启动容器时除了普通挂载还必须把 NPU 设备映射进去至少包括这些设备节点/dev/davinci0 /dev/davinci_manager /dev/hisi_hdc /dev/devmm_svm同时需要把 CANN 和 MindX SDK 的安装目录挂载进容器。很多容器启动之后报open /dev/davinci0 failed几乎都是因为设备节点没映射全而不是代码问题。还有一个新手容易忽略的点容器内的环境变量。set_env.sh 的路径在不同版本里有差异不要默认容器里已经有了建议启动命令里显式 source或者做进镜像的 Dockerfile 里。如果你只是想在本地先验证一下我觉得甚至可以先不折腾 Docker直接在物理机上装好环境跑通一个 hello world再考虑容器化。环境干净的时候排错会容易很多。4. YOLOv8转OM静态shape、动态shape与精度对齐4.1 导出ONNX时提前埋雷的几个细节环境准备好之后最重要的一步就是把 YOLOv8 导出成 ONNX。官方仓库其实已经提供了 export.py大多数情况下一条命令就能搞定python export.py --weights yolov8s.pt --include onnx --opset 12但有几个细节如果不注意后面 ATC 转换会很难受。第一是 opset 版本。我建议锁定在 11 到 17 之间opset 太老会导致 Split、Slice 这些常见算子导出形态比较怪ATC 转换时报错率明显上升。opset 太新昇腾算子库不一定匹配反而引入了兼容性风险。第二是动态轴。导出时如果 ONNX 带了动态 batch 或者动态分辨率后续 ATC 转换时处理方式会复杂不少。如果你确定推理时只用固定分辨率 640x640导出时直接把动态轴固定死后面会省很多事。第三是输出节点。YOLOv8 导出 ONNX 后输出是三个尺度的特征图shape 分别是 [1,84,80,80]、[1,84,40,40]、[1,84,20,20]。这里的 84 是 4 个框坐标加 80 个 COCO 类别置信度。这三个输出的顺序和你后处理代码里读取的顺序要保持一致否则检出来的框是乱飞的。4.2 ATC转换的常用命令与soc_version确认ONNX 拿到手后下一步是用 ATC 转成 .om。最基本的命令长这样atc --modelyolov8s.onnx \ --framework5 \ --outputyolov8s_bs1 \ --input_shapeimages:1,3,640,640 \ --soc_versionAscend310P3 \ --loginfo这里要注意几个参数--framework5表示输入是 ONNX这个参数经常有人写错。--input_shape里的images是 ONNX 输入节点的名字如果你的模型输入叫别的名字先用工具看一下再写。--soc_version必须写对。Atlas 300V 24G 对应的芯片版本一般是 Ascend310P3不确定的话可以看 npu-smi 输出或者去 CANN 的文档查对应关系。写错了转换过程和推理过程都会报错。转换过程会打印日志看到success字样就说明 .om 生成好了。如果报错把日志级别设为 info根据报错定位是算子不支持还是 shape 推导失败。4.3 动态shape什么时候需要怎么处理如果业务上确实需要一张图多大就送多大不能固定 640x640那就要用动态 shape 方案。ATC 转换时启用动态 shape大致命令是这样atc --modelyolov8s.onnx \ --framework5 \ --outputyolov8s_dym \ --input_shapeimages:-1,3,-1,-1 \ --dynamic-shape \ --dynamic-dims640,640;960,960;1280,1280;1920,1920 \ --soc_versionAscend310P3为什么这里要限制一个 dims 集合而不是允许任意分辨率因为 NPU 在编译模型时需要提前确定内存布局和板块划分你列出的每一个分辨率组合都会单独生成一份优化版本运行时会根据实际输入形状选择最接近的那份。列太多会显著增加模型体积和编译时间列太少则覆盖不了实际场景。我的建议是如果你只有固定分辨率需求就不要碰动态 shape。它引入的问题比解决的还多最明显的是动态 shape 下单帧时延会比静态 shape 高一些因为内存布局的优化做不彻底。4.4 AIPP与精度对齐为什么检测结果对不上模型转换完之后第一件要做的事不是直接上生产而是精度对齐。拿同一张测试图分别在 PyTorch 里跑出结果再在 Atlas 上跑对比检测框是不是一致。如果你发现 Atlas 出来的结果是全 0 或者坐标乱飞八成是输入预处理的问题。这里有两种策略我分别说清楚。策略一不做 AIPPONNX 里保留归一化。PyTorch 训练时输入是 float32范围是 0 到 1。导出 ONNX 时归一化层跟着网络走推理时你在 CPU 侧先把图像转成 float32、除以 255再喂给模型。这种方式的优点是代码简单和 PyTorch 完全一致缺点是 CPU 预处理压力大且 h2d 拷贝的数据量是 uint8 的 3 倍以上。策略二用 AIPP 做预处理输入直接喂 uint8 数据。AIPP 是昇腾专门用来做图像预处理的模块在 ATC 转换时通过配置文件挂载推理时图像原始数据直接进卡在卡上完成色域转换、缩放这些操作。用 AIPP 的时候网络结构里就不能再带归一化否则就相当于归一化了两次精度必崩。同时要仔细核对 RGB 还是 BGR 顺序、mean 和 min/max 这几个参数。最常见的错误就是把 RGB 顺序当成 BGR 喂进去出来的框基本是乱的。我个人的部署经验是先用策略一跑通功能确认模型本身没问题再切到策略二去优化性能。这样一次只引入一个变量排查起来不会两头怀疑。5. 推理代码实战ACL从初始化到出框5.1 最小推理流程的骨架ACL 推理有一个比较固定的代码骨架我把核心流程记在下面。这里用概念性伪代码接口名在 C 和 Python 里是一样的1. aclInit() 2. aclrtSetDevice(0) 3. aclrtCreateContext(ctx, 0) 4. aclrtCreateStream(stream) 5. aclmdlLoadFromFile(yolov8s.om, modelId) 6. aclmdlGetDesc(modelDesc, modelId) 7. 根据模型描述申请输入输出内存映射aclDataBuffer 8. 把预处理好的图像数据拷贝到输入内存 9. aclmdlExecuteAsync(modelId, inputs, outputs, stream) 10. aclrtSynchronizeStream(stream) 11. 从输出内存取出推理结果做后处理这套流程就是一个 Augmented 版本的“加载模型-喂数据-拿结果”和 CUDA 的流程在思路上是相通的但接口名和内存管理方式完全不同。第一次写 ACL 代码的时候最好先跑通官方的 sample比如 resnet-50 分类样例。把 sample 的代码改成加载你的 .om输入换成自己准备的二进制数据输出打印出来这样能快速建立对 ACL 内存模型的感觉再来写 YOLO 后处理就不慌了。5.2 输入图像处理DVPP和自研预处理之间的选择模型输入定了 640x640但摄像头出来的图往往是 1080p 甚至更高分辨率所以图像处理是不可避免的。这部分的性能直接影响整条链路时延。Atlas 上的硬件图像处理单元叫 DVPP它负责解码和缩放。流程通常是JPEG 图片用 JPEGD 解码成 YUV420SP 格式VPC 模块做缩放和格式转换输出 640x640 的 YUV 图或者 RGB 图数据直接进模型输入 buffer如果用 OpenCV 在 CPU 上做读图、resize、归一化也能跑通但 CPU 占用率会飙升。在单路测试时感觉不明显一旦到了多路并发CPU 打满之后帧率就上不去了这时候你可能会误以为是 NPU 算力不够其实瓶颈在预处理。所以生产环境我强烈建议走 DVPP。代价是你的代码复杂度会上升因为 YUV420SP 这种格式不像 RGB 那么直觉调试起来也更麻烦。我的做法是封装一层图像处理工具类输入图像路径输出模型输入数据内部先走 DVPP如果失败再回退到 OpenCV开发和上线两不误。5.3 输出解析和后处理别把v8当v5写结果拿回来之后就是后处理阶段。这个阶段最容易犯的错误是把 YOLOv8 当成 YOLOv5 来解析。YOLOv5 导出的 ONNX输出通常是 [1,25200,85]也就是所有预测框一次性给出来每个框有 85 个值其中前 5 个是中心坐标、宽高、objectness后面 80 个是类别得分。做 NMS 之前需要先用 objectness 过滤一遍。YOLOv8 不一样它是 anchor-free 的没有 objectness 这一项。输出是三个尺度的特征图后处理要做的是把三个图拼成一个 [1,84,8400] 的矩阵其中 8400 是 80x80 加 40x40 加 20x20 的总和。前 4 个通道是已经解码过的框参数后面 80 个通道直接就是类别置信度。所以 v8 后处理的逻辑是把三个输出 reshape 后拼成一个 84x8400 的矩阵。前 4 行解析成检测框坐标。对后面 80 个类别取最大值作为该框的置信度同时记录类别 id。用置信度阈值我一般用 0.25过滤低分框。做 NMSIOU 阈值 0.45去掉重复框。如果你写过 v5 的后处理直接平移逻辑到 v8 上出来的检测框一定是乱的因为坐标解码方式完全不同。这是我见过的最常见的一类精度异常。6. 性能调优从单路跑通到多路并发6.1 batch与时延/吞吐的关系模型跑通之后就该聊性能了。这里第一个需要建立的概念是单帧时延和整体吞吐是两个互相打架的指标。batch1 的时候单帧时延最低因为只需要等一张图的算完。但吞吐量上不去因为 NPU 的算力没有吃满大量时间浪费在等待数据搬运上。batch16 甚至 32 的时候吞吐量会好看很多因为模型在卡上可以并行处理更多输入。但单帧时延会上升因为要攒够一批数据才开始推理。如果你做的是实时视频流检测用户感知强烈的是单帧时延那可以 batch1 配合多 stream 并发如果你做的是离线批量图片处理追求单位时间处理数量那就调大 batch。我在 Atlas 300V 24G 上跑 YOLOv8s 的经验范围大概是这样FP16、batch1、640x640 下单帧时延在 10ms 上下浮动切到 INT8 之后会进一步降低。批量推理时吞吐能到几百 FPS 级别。不同环境和不同算力切分配置下差异很大这个数字仅供参考关键是理解趋势。6.2 多Stream并发处理多路视频的正确姿势实际业务里很少只跑一路视频十几路甚至几十路才是常态。这时候不是简单地把视频帧串行地送进模型而是要用多 Stream 并发。ACL 里一个 stream 相当于一条推理流水线可以在一条线程上独立执行。我的做法是每个视频流对应一个线程线程内申请一个 stream。每路图像进来后走 DVPP 预处理进模型推理后处理输出结果。整个流程在各自 stream 里走互不阻塞。同时要注意对同一路视频做丢帧策略不能因为某一帧后处理慢了就无限排队。多路并发时NPU 的利用率才能真正上来这也是 Atlas 300V 24G 这类推理卡的价值所在。如果只有单路其实跑在 CPU 上也能凑合。6.3 优化顺序先看瓶颈在哪再动手调性能调优最忌讳的就是盲目调参。每次改一个变量先看效果再决定下一步。我的调优顺序一般是这样的用 npu-smi info 看 NPU 利用率。如果利用率很低说明不是算力瓶颈问题在数据加载或者预处理。看 CPU 占用率。如果 CPU 满得不行说明前处理和后处理拖了后腿对策是把预处理切到 DVPP后处理做多线程加速。再调 batch 和 stream 并发数找到时延和吞吐的平衡点。最后才考虑切换到 INT8 模型用 AMCT 量化工具做精度校准。这条链路走下来百分之八十的性能问题都能定位到而不是一股脑去加卡或者换更高的算力硬件。7. 踩坑记录与最终的几条实操建议7.1 高频报错与排查链路最后把这几个月遇到的典型报错整理成一张排查表方便大家按图索骥。报错现象根因解决办法open /dev/davinci0 failedDocker 设备节点没映射全检查运行容器时是否映射了 davinci 相关设备E19999: Inner Error昇腾外层统一错误具体原因被吞了开启详细日志日志级别设为 info 再复现model not support operator xxxONNX 里有昇腾不支持的算子升级 CANN 版本或将不支持算子改在 CPU 侧执行推理结果全 0AIPP 配置或输入数据格式错误先关掉 AIPP 跑裸数据验证模型本身检测框乱飞RGB/BGR 顺序错误或归一化重复逐项核对图像预处理参数多路并发时卡死stream 创建过多或线程安全问题控制并发数检查是否共用同一个 context如果你碰到报错我的建议是先看日志。昇腾的日志默认在~/ascend/log目录也可以通过下面两个环境变量控制级别和路径export ASCEND_GLOBAL_LOG_LEVEL1 export ASCEND_SLOG_PRINT_TO_STDOUT1日志里面会明确告诉你算子在哪个节点失败了或者内存申请在哪一步出了问题。很多人一看到 E19999 就慌了其实打开日志后大部分问题都写得很清楚。7.2 从零开始的最短路径规划如果你也是第一次在 Atlas 300V 24G 上部署 YOLO我建议按这条最短路径来走能少走很多弯路物理机装好驱动、固件、CANN跑通 npu-smi 和官方 resnet 样例。用官方 YOLOv8 仓库导出 ONNX用 ATC 转成固定分辨率的 .om。用官方 sample 改出一个最小的推理程序先不管 DVPP直接用 OpenCV 读图喂进去。确认检测结果正确之后再用 DVPP 替换图像预处理。性能不达标时逐步加 batch、加 stream、切 INT8。这套路径里每一步的验证目标都很清晰不会出现那种把环境、模型、代码三个问题混在一起一次全都炸出来的情况。说说我个人用下来的最大感受。Atlas 300V 24G 是一张很有特点的卡算力规格对推理完全够用显存也宽裕功耗还低尤其适合多路视频流这种长期跑的业务。但它的门槛不在硬件本身而在工具链。CANN 的文档虽然细致但对新人并不算友好很多经验要靠踩坑换。如果你能耐心把 ATC 转换和 ACL 接口这两关过了后面的性能表现其实相当能打。最后再分享一个习惯每次改模型或者改推理代码之前我都会先保存一份转换配置和验证脚本方便快速回归。昇腾这条工具链的坑多但绝大多数都是能提前避开的关键是别心急一步一个脚印验证。
返回列表