ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Atlas 300V 24G NPU实战:从零跑通YOLOv5推理部署

Atlas 300V 24G NPU实战:从零跑通YOLOv5推理部署 前阵子我拿到一块 Atlas 300V 24G第一反应是这玩意到底算不算运算加速卡答案是肯定的——它不但是加速卡而且是专门为 AI 推理设计的 NPU 加速卡。如果你正在边缘设备上折腾 YOLO 目标检测这块卡和对应的工具链是目前性价比相当高的一条路。这篇文章不打算给你背参数表而是把我从一块全新 Atlas 300V 到真正把 YOLOv5 跑通、再把性能调到能用的整个过程记录下来包括那些官方文档里不会写、但实测下来非常关键的细节。这次的内容适合两类人一类是刚拿到 Atlas 系列设备想快速上手跑模型的新手另一类是在 GPU 上已经跑过 YOLO、想迁移到昇腾 NPU 平台的老手。前者可以顺着文章把环境、转换、推理、后处理的全流程走通后者可以直接跳到第 4 节和第 5 节看我踩过的坑。不管你是哪种我希望你看完以后至少能做到“手里的 Atlas 不再是一块只会跑 demo 的板砖”。1. 先弄明白 Atlas 300V 24G 到底是什么定位很多人听到“300V”第一反应是问这跟 NVIDIA 的显卡是不是一回事能不能直接当显卡用这就是第一个认知误区。它本质上不是显卡而是一张专为神经网络推理设计的 NPU 加速卡所以你需要把它当成一个“AI 推理协处理器”来理解而不是你电脑里那张负责画面渲染的 GPU。1.1 它是推理加速卡不是“台式机显卡”Atlas 300V 24G 基于昇腾 310P 系列芯片INT8 精度下算力在 140 TOPSFP16 算力大概在 70 TFLOPS 左右。只看这个数字你可能会觉得“这不就是一张高端显卡吗”。但实际用起来你会发现它的设计目标非常明确以最低的功耗干完“图片进来、推理结果出去”这件事。它没有普通的显示输出接口也不能直接插到家用主板的 PCIe x16 槽上就开机点亮画面。它的工作方式是整个系统完成推理任务再通过网络或 PCIe 把结果送回 CPU 侧之后由 CPU 上的业务程序去展示或转发。这里有个关于 24G 显存容量的细节值得单独说。24G 指的是卡上集成的 LPDDR4X 内存不是用来当计算机内存的是给权重、中间特征图和推理输入输出准备的。24G 容量在当前的边缘推理卡里算是比较宽裕的意味着你不需要做非常激进的量化就可以把一个较大的检测模型完整塞进去比如用 FP16 推理而不是必须量化到 INT8。1.2 24G 容量到底在什么场景下有意义大容量内存带来的不是参数表里的一个数字而是实际部署时的选择空间。我在实际项目中遇到过两类情况恰好能说明 24G 比 16G 或 8G 的卡更从容。一类是同时加载多路视频流。做视频结构化的时候往往要在一个进程里同时加载检测、跟踪、特征提取好几个模型如果每路视频流还带着各自的预处理缓存和推理队列内存很快就见底。Atlas 300V 24G 支持在同一张卡上创建多个推理上下文把不同模型同时常驻这样不同业务模块不用频繁做模型加载和释放。另一类是使用比较大的输入分辨率。检测小目标时我们经常会放弃 640x640改用 1280x1280 甚至 1536x1536 的输入。图像尺寸每大一倍特征图的内存占用量是四倍地涨。24G 对于这种高分辨率输入来说就比较稳妥我实际在 1280 分辨率下跑过 YOLOv8显存占用大概在 10G 上下余量很足。所以如果你计划做高分辨率检测、多模型加载或者多路并发推理24G 版本是有意义的。2. 为什么是 YOLOAtlas 部署目标检测的典型场景回到标题里的热搜词atlas 部署 yolo。为什么这么多人想在那上面跑 YOLO核心原因是 YOLO 系列模型的推理结构非常规整主干加检测头的设计在 NPU 上很容易被完整编译和优化。要知道NPU 不比 GPU不是所有算子都能高效执行。一个模型能不能在昇腾上跑得顺完全取决于算子转换时能不能被“翻译”成硬件能直接执行的原语。YOLO 系列的 Conv、Concat、Sigmoid、MaxPool 这些基础算子在 CANN 工具链里都有比较好的支持所以部署起来踩坑相对少。2.1 边缘计算场景里 NPU 比 GPU 更合适我之前做过一个工厂质检端的落地项目原来是用一块低功耗 GPU 在工控机里跑目标检测。GPU 的算力不是不够而是整机功耗和散热实在让人头疼。工控机机箱就那么点大GPU 满负荷跑起来风扇噪音先不说输入电压稍不稳整个系统就往下掉。后来换到 Atlas 300V单卡功耗低不少散热压力小性能却能满足实时的要求。从实际测试看在 640x640 输入、FP16 精度下跑 YOLOv5s单路推理大约 3 到 5 毫秒这个速度对绝大多数边缘业务是足够的。而且 NPU 在视频处理上有天然优势。Atlas 300V 支持板载的 DVPP 硬件模块可以直接对视频流做解码、缩放、颜色空间转换不需要占用 CPU 做软件预处理。你可以把整个链路理解为视频流进来NPU 直接接管图像解码和缩放然后送进推理算子最后 CPU 只做后处理和业务逻辑。这让它在边缘设备上的稳定性明显优于传统 CPUGPU 的组合。2.2 值不值得折腾成本与性能的权衡也不能回避另一个问题这套东西比直接用 GPU 便宜吗如果只看单卡硬件价格Atlas 300V 确实有竞争力但你要把整个研发成本也算进去。GPU 生态是现成的PyTorch 写完直接跑昇腾这边要先导出 ONNX再转 OM 模型还要处理 AIPP 配置和结果后处理的算子适配。这部分研发成本在第一个项目里是躲不掉的。我的建议是如果只是自己研究学习不涉及批量采购可以先用官方 MindX SDK 的 demo 跑通验证完再决定要不要深入。如果是商业项目并且对功耗、体积、长期稳定供货有要求那 Atlas 这套方案的价值就体现出来了。我接触到不少智慧安防、电力巡检、工业质检类的项目最后都选择了 Atlas 300V不是因为它在绝对算力上压倒 GPU而是它在“边缘部署”这个特定场景下把功耗、稳定性、供货和成本平衡得更好。3. 部署链路全景从 ONNX 到 OM 再到跑通无论你之前在 GPU 上用什么训练框架落到 Atlas 上推理大多数情况下都要走一遍模型转换流程。整个过程大致是训练框架导出 ONNX再用 ATC 工具把 ONNX 转成 OMOffset Model最后在代码里加载 OM 模型执行推理。这个链路里有三个工具/组件你必须先分清CANN、MindX SDK、ATC。3.1 CANN、MindX SDK 和 ATC 工具分别干什么CANN 是昇腾的计算架构层相当于 CUDA 之于 NVIDIA。它负责把神经网络的计算调度到 NPU 上提供底层的运行环境和 API。你要加载 OM 模型、申请 device 内存、执行推理用的都是 CANN 的 ACLAscendCL接口。MindX SDK 则是基于 CANN 封装出来的更高级的推理开发框架。它把推理任务拆成“数据流图”的节点比如视频解码节点、图像缩放节点、推理节点、模型后处理节点每个节点是一个 Plugin插件之间通过自定义数据格式传数据。你用配置文件把节点串起来就能跑通一个完整的推理流程代码量少很多。但代价是灵活性受限如果你要做的后处理非常定制化SDK 的 Plugin 会变成瓶颈。ATC 是模型转换工具作用是把 ONNX、TensorFlow、MindSpore 等格式的模型转换成 OM 格式。转换过程中会做算子映射、图优化、量化等操作。这个工具是连接训练框架和推理运行时的桥梁也是整个部署链路里最容易出问题的一环。3.2 模型转换前必须处理的三个关键点在真正执行atc命令之前有三个东西要想清楚否则后边全是坑。第一是输入格式。YOLO 模型转 ONNX 的时候默认输入可能是[1, 3, 640, 640]的 NCHW 格式。但 NPU 有些算子更偏向 NHWC 或内部特有的NC1HWC0格式。如果你事后发现推理性能很差可以检查一下 ATC 转换时有没有做格式优化。正常情况下 ATC 会自动选择最优的格式但如果模型里有某些自定义算子锁死了格式性能就会打折。第二是算子的支持度。YOLOv5 和 YOLOv8 导出的 ONNX 里通常会包含一些比较新的算子比如GridSample、MultilevelCropAndResize之类的。ATC 不一定完全支持。我遇到过的最典型情况是模型在 ONNX Runtime 里能跑但 ATC 一转换就报“Unsupported Op”。这种要么改模型结构把不支持的算子拆成多个基础算子要么换对应的模型实现版本要么改后处理逻辑把这些算子的计算拿到 CPU 上做。第三是输入输出的数据精度。默认情况下ATC 转换出来的 OM 模型输入可能是 FP32推理时会多一次数据格式转换开销。如果你能确定自己的预处理可以输出 FP16就在转换时指定--input_fp16_nodes把输入节点设成 FP16能省不少内存带宽。输出精度同理--output_typeFP32一般用于后续直接做后处理省去 CPU 上的反量化。不过这里也要根据自己的场景权衡不是所有模型都适合改成 FP16个别模型对数值精度敏感改了之后检测结果会漂移。3.3 两种调用方式MindX SDK 管线 vs ACL 纯手工接下来是推理代码怎么写的问题。最简单的方式是直接用 MindX SDK把推理过程写成一个 pipeline 配置文件然后代码里去创建和启停这个 pipeline。适合快速验证模型能不能跑通、性能量级大概多少。另一种是用 ACL 手工写推理逻辑。你需要自己处理初始化 ACL、设置 device、加载 OM 模型、创建输入输出数据集、申请并拷贝内存、执行模型、解析输出。代码量会大很多但可控性最强。尤其是当你需要把多个模型的推理结果在同一个进程里做复杂的逻辑关联时手工用 ACL 写反而更清晰。我个人的建议是如果项目时间紧、模型后处理也比较标准直接用 MindX SDK 起步如果后续要做性能优化、做视频流多路并发、做复杂后处理集成尽早换成 ACL 手写。别一开始图省事用了 SDK最后又推倒重来这个转折成本很高。4. 实操YOLOv5 模型在 Atlas 300V 上的部署实录下面这部分是硬核操作直接照着做可以让你手里的 Atlas 300V 把 YOLOv5 跑起来。我用的是 YOLOv5 官方仓库的 ultralytics 版本训练好的权重转成 ONNX再转 OM再写一个 ACL 推理程序完成推理。整个流程我用过很多次是相对稳的一条路线。4.1 导出 ONNX 与手动检查算子第一步当然是训练好 YOLOv5或者直接用官方预训练权重。这里有个建议训练或者导出的 PyTorch 版本不要太新我当时用 PyTorch 1.13 导出时报错较少而 PyTorch 2.0 之后自动导出的 ONNX 有时会带一些新版本算子ATC 转换时要多费点功夫。导出 ONNX 的命令比较简单python export.py --weights yolov5s.pt --include onnx --opset 11这里--opset 11很关键ATC 对 ONNX opset 11 的支持最成熟。只要你的模型结构没有特别新的算子opset 11 完全够用。导完之后别急着转先用 netron 打开 ONNX 看一下重点关注输入节点的数据格式、输出节点的名字和 shape。YOLOv5 原始模型导出的输出一般是一个[1, 25200, 85]的大 Tensor表示 3 个尺度的检测结果全部拼在一起。后面你要根据这个 shape 来写后处理。4.2 ATC 转换的核心命令与实际参数在安装好 CANN 工具包之后首先要 source 环境变量source /usr/local/Ascend/ascend-toolkit/set_env.sh然后执行模型转换。我通常在转换前会先准备一个 AIPP 配置文件用来描述图像的预处理方式这样推理时就不用自己在 CPU 上做 Resize 和归一化可以把这些操作下沉到 NPU 上降低延迟。假设你的 YOLOv5 输入尺寸是 640x640、输入顺序是 RGB、像素范围 0-255模型内部做了归一化那么 AIPP 配置大概是这样的aipp_op { aipp_mode: static input_format: RGB src_image_size_w: 640 src_image_size_h: 640 crop: 0 load_start_pos_h: 0 load_start_pos_w: 0 resize: 1 csc_switch: 0 rbuv_swap_switch: 0 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 }这里var_reci_chn_0三个值都是 1/255作用是把 0-255 的像素值缩放到 0-1。如果你的模型是在归一化之后的数值上训练的这个配置就是对的。如果模型用别的归一化方式比如 ImageNet 的 mean/std则要在 AIPP 里配置相应的均值方差。ATC 转换命令如下atc --modelyolov5s.onnx \ --framework5 \ --outputyolov5s_640 \ --input_shapeimages:1,3,640,640 \ --soc_versionAscend310P3 \ --insert_op_confaipp.cfg \ --output_typeFP32--framework5表示输入是 ONNX 格式--soc_versionAscend310P3是对应 Atlas 300V 使用的昇腾 310P 芯片版本--output_typeFP32表示输出保留 FP32 精度。转换完成后会在当前目录生成yolov5s_640.om。4.3 ACL 推理主流程的代码骨架拿到 OM 模型之后就可以写一个最小可用的 ACL 推理程序。下面是核心流程的 Python 版本使用aclruntimePython 接口适合快速验证import acl import numpy as np # 初始化 ret acl.init() ret acl.rt.set_device(0) # 加载模型 model_path b./yolov5s_640.om model_id, ret acl.mdl.load_from_file(model_path) # 获取模型输入的描述信息分配内存 input_desc acl.mdl.create_desc() ret acl.mdl.get_desc(input_desc, model_id) input_size acl.mdl.get_input_size_by_index(model_id, 0) input_data np.zeros((1, 3, 640, 640), dtypenp.float32) # 把 input_data 拷贝到 device 侧执行模型 # 这部分在 Python 里通常用 acl.mdl.execute 完成 # 实际代码还要处理输出内存申请、数据拷贝、后处理等这里为了展示主要步骤省略了完整的内存管理和错误判断。实际上你还需要调用acl.rt.malloc申请 device 内存用acl.rt.memcpy把输入数据从 host 拷到 device推理完再拷贝回来。完整的代码骨架可以参照 CANN 自带的 samples 目录里的 resnet50 示例换成你的模型路径即可。在正式项目里我建议把整个 ACL 流程封装成一个类包括模型的加载、输入输出 buffer 的申请、推理执行的同步/异步管理。这样后续加多路视频流时只要多创建几个实例就行。4.4 后处理NMS 和解码不能照搬 CUDA 版本这一步是最容易被低估的环节。YOLO 的原始输出是网格坐标的偏移量要转换成最终的目标框坐标需要做 decode、过滤低置信度框、NMS 这几个步骤。你如果直接把 GPU 上的后处理代码搬到 CPU 上用 numpy 实现速度可能会让你想把电脑砸了。因为 640x640 输入的 YOLOv5 输出有 25200 个候选框纯 Python 的循环解码一次要几十毫秒推理本身才几毫秒后处理反而成了瓶颈。我的做法是把后处理尽量向量化用 numpy 数组操作替代循环。比如 sigmoid、坐标解码、阈值过滤、NMS 都用矩阵运算实现。实测下来numpy 版本的向量化后处理在 CPU 上大约 4 到 6 毫秒可以接受。如果你用的是 MindX SDK可以尝试把后处理写成自定义 Plugin把部分解码运算放到 NPU 上执行。但对 YOLO 这种后处理逻辑比较灵活的场景上手成本会比较高我还是更推荐先用 numpy 实现后续如果需要再把耗时最严重的部分换成 C 扩展或者 ACML 算子。NMS 置信度阈值的选取也要注意。在 Atlas 上用 FP16 推理因为数值精度的问题很多小目标的置信度会比 FP32 低一些如果沿用 GPU 上的阈值会漏检。我一般会把置信度阈值从 0.25 降到 0.15 左右再结合后续的业务过滤最终效果和 GPU 上几乎一致。5. 常见问题与排查技巧实录这个部分我直接按问题类型整理每个都是我在实际部署中撞过墙、花过时间排查的。你如果正卡在某一步直接对号入座。5.1 转换报错算子不支持这是新手最常遇到的问题。ATC 转换时报错通常是两种一种是Unsupported Op明确告诉你某个算子不支持另一种是InferShape failed说明某个算子的 shape 推导失败。遇到第一种建议先用 netron 找到报错的算子看它是属于 YOLO 哪个组成部分。如果是像GridSample这种新算子最简单的办法是不用模型里自带的解码层导出 ONNX 时把后处理拿掉只保留 Detect 层之前的卷积输出。这样模型就只剩标准算子转换基本不会出问题后处理放到 CPU 上自己写。遇到第二种通常是某个动态 shape 的算子导致的比如Resize的尺寸不确定。处理方式是固定输入尺寸在导出 ONNX 时就把所有 shape 固化。5.2 性能上不去先查数据通路推理速度比预期慢第一时间想到的应该是数据通路而不是模型本身。Atlas 在推理时输入数据要先从 host 内存拷贝到 device 内存推理完再拷回来。如果你的业务输入是摄像头视频流又没有用 DVPP 做硬件解码和缩放而是用 OpenCV 在 CPU 上做 Resize再同步拷贝到 NPU那么整条链路的时间就是CPU 预处理 拷贝 推理 拷贝。很多人的推理时间只有几毫秒但整帧处理时间却到了几十毫秒问题就出在预处理和拷贝上。解决办法是优先用 DVPP 硬件模块处理图像缩放和格式转换并把 AIPP 配置打开让 NPU 在推理前自动完成归一化减少一次 host-device 往返。另外一个容易忽略的点是输入数据的内存对齐。ACL 要求 device 内存按 32 字节对齐如果你用numpy生成的数组没有对齐拷贝时会多一次转换性能也有损失。创建输入数据时最好用acl.rt.malloc申请并对齐而不是随便 malloc。5.3 推理结果乱先查 AIPP 和后处理如果你看到输出的检测框位置完全错乱或者坐标超出图片范围先别急着怀疑模型转换大概率是 AIPP 配置和后处理的坐标变换不匹配。最典型的坑是图像缩放方式。YOLOv5 在训练时用的是 letterbox 缩放就是等比缩放后填充灰边保持长宽比不变。如果你的 AIPP 配置里用了直接拉伸到 640x640精度会下降而且后处理里如果没有做 letterbox 的坐标映射检测框的位置就会整体偏移。另一种情况是输入顺序。如果训练时用的是 RGB但 AIPP 里写成了 BGR检测结果会明显变差尤其对颜色敏感的目标。排查这类问题我会先用一张单目标的简单图片比如纯色背景下只有一个苹果然后打印出解码后的原始输出 Tensor看看中心的坐标值是否大概在目标区域。如果偏差很大逐层检查预处理和后处理基本上半天内能定位。5.4 环境和固件最容易被忽略的坑最后想专门说一下固件和驱动版本的问题。Atlas 300V 安装在服务器里除了要安装 NPU 驱动和固件还要保证固件版本和 CANN 版本匹配。我遇到过两次奇怪的故障一次是推理结果时好时坏另一次是模型加载时偶发失败最后查了半天发现是固件版本太旧和 CANN 不兼容。排查思路很简单安装驱动后执行如下命令看一下固件版本npu-smi info再对照当前安装的 CANN 版本去官方文档确认对应的固件版本范围。升级固件前做好备份因为固件升级过程会短暂让 NPU 离线在线业务要做好保护。另外环境变量也是重灾区。每次开新终端都要记得source /usr/local/Ascend/ascend-toolkit/set_env.sh否则 Python 导入 ACL 库时会报找不到动态库的错。我建议把这个 source 写进.bashrc里否则你很可能在半夜调试时被一个 ModuleNotFoundError 折磨半小时。说到底Atlas 300V 这套东西只要过了算子转换和后处理这两座大山后续的部署就会顺畅很多。我自己从第一次接触昇腾到现在最大的体会是别拿它当“另一张 GPU”它是为特定场景优化的推理卡按它的规则来性能和稳定性都会超出预期硬要把 CUDA 生态的习惯搬过来就会处处碰壁。如果你手头有这块卡或者正要为项目选型不妨先按这篇文章的流程走一遍跑通一个模型之后再决定要不要全面迁移。
返回列表