ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Atlas 300V 24G部署YOLO完整指南:从ONNX到OM模型推理

Atlas 300V 24G部署YOLO完整指南:从ONNX到OM模型推理 把一块Atlas 300V 24G插进服务器、跑起YOLO目标检测的完整过程我踩了不少坑也整理了不少经验这里一次性写出来。如果你手里正拿着这块卡或者正准备在昇腾平台上部署YOLO系列模型这篇文章应该能帮你省下至少一周的探索时间。先回答那个被问过很多次的热搜问题Atlas 300V 24G是一张运算加速卡吗答案是肯定的但更准确地说它是一张AI推理加速卡。它不能像游戏显卡那样用来渲染画面也不能像训练卡那样扛大模型训练它的强项是用极低的功耗把已经训练好的模型推送到线上以几十毫秒的延迟跑完一次目标检测。所以如果你是想把我手里的YOLO权重放到线上做实时检测Atlas 300V 24G是合适的硬件如果你是想用它来从头训练一个YOLO那趁早换方案这和CUDA生态的训练卡完全是两码事。这篇文章会覆盖Atlas 300V 24G的硬件定位、YOLO部署的技术路线、模型转换和推理代码怎么写以及实际部署中常见的坑和排查方法适合算法工程师、嵌入式开发者和刚接触昇腾推理平台的读者参考。1. 先搞清楚Atlas 300V 24G的身份1.1 推理加速卡不是训练卡也不是游戏显卡我第一次拿到Atlas 300V的时候第一反应是这不就一块PCIe声卡大小的板子么。安装到服务器上之后系统里多了一个名为Ascend310P的设备节点而不是类似/dev/nvidia0的节点这个细节能帮你在第一时间确认自己拿到的是昇腾生态的硬件而非GPU。昇腾的产品线其实分得很清楚面向训练场景的有Atlas 900/Atlas 800训练服务器以及昇腾910系列芯片面向推理场景的就是Atlas 300系列其中300I系列和300V系列都属于标准PCIe形态的推理卡。300V 24G用的是昇腾310P系列芯片主打高能效比的神经网络推理。意思很清楚它的设计目标就是用最低的电费跑最多的推理请求而不是像训练那样追求极致的浮点算力。从硬件规格上看Atlas 300V 24G的板载内存是24GB对于推理卡来说这个容量已经相当可观。因为推理场景下最常见的瓶颈不是算力而是显存/内存容量限制模型数量和batch size的问题。你想想一个YOLOv5s的模型权重也就十几兆到几十兆单batch推理时中间特征图占用的空间也不会超过几百MB24GB的内存在单模型状态下几乎用不完真正的价值在于它可以同时塞进多个模型、多路过路视频流或者把batch size拉到很大来吃满芯片算力。1.2 24G内存到底意味着什么很多人看到24G第一反应是“能不能拿来跑大模型”。普通大模型比如几B参数的模型光是权重就要好几个GB24G确实可以塞下权重但推理卡不等于训练卡它的算力、带宽、算子支持范围都是为推理场景优化过的。如果你拿它跑那种超大模型的推理即使是能塞进内存帧率也会让你怀疑人生。所以24G的正确用法是单模型多batch推理比如YOLOv8mbs8甚至bs16一次推理充分榨干AI Core的并行度。多模型并行常驻同时加载好几个检测、分类、分割模型按业务请求动态路由。多路视频流分析每一路视频流分配一个检测模型实例24G能支撑几十路同时分析。这里有个很容易误解的概念昇腾语境下板载的24G设备内存不完全等同于我们常说的GPU显存。它更靠近NPU的“统一内存”权重、输入、输出、中间激活值都放在里面。在使用AscendCL的acl.mdl.load_from_file加载模型时模型会被完整加载到设备内存中所以如果内存不够第一个现象就是acl.mdl.load_from_file失败返回类似内存分配失败的报错。1.3 软件栈思路和CUDA完全不一样这是我认为所有从GPU平台迁移过来的开发者都必须建立的心理预期。NVIDIA的生态是CUDA你写好PyTorch代码调用CUDA接口模型在GPU上跑。昇腾这边虽然也兼容PyTorch但它的原生推理链路是模型转换 昇腾推理引擎。举个例子在GPU上你要跑YOLOv5直接把.pt文件加载到PyTorch里就能推理但在Atlas上不行。你至少要走这么一条链路在GPU或CPU环境上把PyTorch模型导出为ONNX。用昇腾的ATC工具把ONNX转换为.om格式离线模型。在昇腾设备上用AscendCL接口加载.om模型并执行推理。为什么会这样设计因为昇腾芯片的算力高度依赖编译期的算子调度和内存规划。离线转换的过程本质上就是老天帮忙把神经网络的计算图做一遍深度优化把算子映射到AI Core上的指令序列、规划好所有中间结果的内存位置、能做融合的算子尽量融合。这样推理的时候就不需要像PyTorch那样边解析边执行性能才能上去。所以如果你之前完全没接触过这种转换式推理框架也不用慌后面我会把整条链路一步一步写清楚。2. YOLO部署的整体技术路线设计2.1 选模型版本和导出方案YOLO家族现在常见的有YOLOv5、YOLOv8两大类。我实际在Atlas 300V 24G上部署过v5和v8体感是如果追求快速上线选YOLOv5s或者YOLOv8n模型小、算子简单、转换成功率高如果追求精度YOLOv8m或者YOLOv5m也都能搞定只是转换时会遇到更多算子兼容性问题。在导出模型之前有一个很重要的点需要先确认你打算把NMS放在模型内部还是放在外部做。原版的YOLOv5 export.py在导出ONNX时会默认把后处理NMS逻辑排除掉输出的ONNX只包含模型推理部分输出三个尺度的特征图YOLOv8也是类似。我的建议是在Atlas上部署时把NMS放到模型外部自己写后处理代码不要试图把NMS集成进OM模型里。原因是昇腾的ATC对NMS这类动态控制的算子支持有限硬要集成进去经常会转换失败就算转换成功性能收益也有限反而给自己埋坑。另一个点是固定输入尺寸。YOLO本身对输入尺寸不敏感640x640、416x416都行。但在Atlas上我强烈建议用固定shape导出ONNX。如果导出的ONNX是动态shape比如input:[1,3,-1,-1]在ATC转换时会遇到很多麻烦。昇腾推理对动态shape的处理方式是“动态框选内存段”或者“多档位缓存”不但生成的OM模型体积更大推理时还可能因为内存复用策略导致性能下降。所以我的方案是训练和实验阶段用动态shape部署转换时固定成640x640即可。2.2 ATC转换与离线模型OM的坑模型转换是整个部署流程里技术含量最高、也是最容易出问题的一环。ATC工具的全称是Ascend Tensor Compiler它接收ONNX、TensorFlow、MindSpore等格式的模型输出昇腾平台的OM模型。转换过程中发生了什么大致有这么几个阶段算子解析把ONNX里的算子解析成计算图。算子映射把ONNX算子映射到昇腾支持的算子。昇腾有自己的算子库遇到的算子如果不在库里转换直接失败需要看报错日志里的算子名和拒绝原因。算子融合把相邻的小算子合并成大算子减少内存读写和kernel启动开销。比如ConvBatchNormReLU这种组合最终会被融合成单个算子。内存规划为所有中间张量分配设备内存地址这也是为什么固定shape能大幅优化内存的原因。指令生成生成AI Core能直接执行的指令序列。转换失败最常见的原因就是算子不支持。比如YOLOv5旧版本中有个Focus算子在一段时间的CANN版本里面直接不支持需要把Focus展开成普通的切片卷积结构才能转。YOLOv8里的某些上采样方式、SiLU激活函数在旧CANN版本里也出现过兼容问题升级CANN版本后基本解决。所以如果你在ATC阶段报错第一件事就是去查报错信息里的Unsupported Op字段看看是哪个算子。针对算子不支持通常有三个处理方向升级CANN版本、修改网络结构避开这个算子、改用更高层的推理框架比如MindX来自动处理部分兼容工作。这些我后面会结合实际问题具体展开。2.3 推理调用层的两种姿势.om模型转换好之后在业务代码里怎么调用它在Atlas 300V 24G上主要走两条路线。第一条路是DataMind/MindX推理引擎。它是昇腾自带的高层推理框架提供Python和C接口通过mxpi插件化的方式处理图像的预处理、模型推理、后处理。优点是封装程度高不需要关心ACL细节适合快速做原型验证缺点是不够透明出了问题不好排查尤其是在自定义后处理逻辑时你得把NMS逻辑嵌到插件里调试体验比较痛苦。第二条路是直接用AscendCL也就是CANN提供的基础推理API。AscendCLAscend Computing Language位于驱动之上是昇腾推理的实际底层接口。它可以这么理解MindX是帮你把菜做好的大厨AscendCL是菜谱和食材你可以自由掌控每一个环节。我个人的建议是在正式业务里直接用AscendCL因为它足够简单、足够稳定典型的推理流程也就几个步骤没有太多封装带来的不确定性。在Python开发时AscendCL对应pyACL模块调用路径大概是acl.init()初始化ACL。acl.rt.set_device(0)选择设备。acl.mdl.load_from_file(yolov5s_bs1.om)加载模型拿到model_id。acl.mdl.create_desc()创建模型描述查询输入输出Tensor的形状、大小。用acl.rt.malloc申请设备内存把图像数据填充到输入Tensor。acl.mdl.execute执行推理。推理完成后用acl.rt.memcpy把输出数据拷回主机内存然后做NMS后处理。听起来不复杂但实际写起来有一堆API细节要守着比如输入要求NCHW格式、图像要从RGB变成BGR还是保持原样要看AIPP配置、数据类型要跟ONNX的一致。这篇文章之后的章节会给出可以直接抄的代码框架。3. 从零到一跑通YOLOv5带参数这一节我按我实际走通的流程来写从环境准备到推理代码运行每一步都给参数、给命令尽量做到照做就能跑起来。3.1 环境准备驱动、固件、CANN的版本匹配这是整个过程中最容易让人崩溃的一步。很多人的卡在服务器上插好也装了驱动但加载模型时各种报错最后发现是驱动、固件、CANN三个组件的版本不一致。一套完整的昇腾推理环境至少包含驱动Ascend HDK Driver操作系统和NPU之间的桥装上之后你才能在/dev/davinci0下看到设备。固件Firmware芯片内部的微码程序通常在驱动包一起的HDK安装包中。CANN Toolkit昇腾计算平台包含ATC、pyACL、算子库等相当于CUDA Toolkit。在CANN 6.0及之后版本驱动和固件打包在Ascend-hdk-310p-npu-driver和Ascend-hdk-310p-npu-firmware两个run安装包里下载时要注意和CANN Toolkit的版本配套官方文档里会给出一个版本配套表。我踩过的一个教训是CANN Toolkit装的是6.3版本而驱动还是5.1的旧驱动结果ATC转换成功但运行时NPU上报E7889错误。所以如果你是新环境安装直接下同期的CDK安装包一次性装好三个组件是最省事的别学我先装个老驱动再手动升级。安装完以后执行npu-smi info查看设备信息能看到类似下面这样的输出------------------------------------------------------------ | NPU | Name | HBM | Power | Temp | Health | ------------------------------------------------------------ | 0 | 310P3 | 24GB | 30W | 48C | OK | ------------------------------------------------------------看到Health为OK、Temperature正常说明硬件和驱动已经就绪。这里注意如果npu-smi显示的内存是23GB或24GB这是正常的因为有一部分内存会被系统保留用于管理。3.2 导出ONNX模型在GPU或本地CPU机器上拿到YOLOv5官方仓库的代码用它的export.py导出ONNX。关键参数如下python export.py --weights yolov5s.pt --include onnx --img-size 640 640 --batch-size 1 --opset 13这里必须强调几个参数的含义--img-size 640 640输入尺寸固定为640x640。--batch-size 1导出单batch版本。如果你计划在Atlas上做多batch推理也可以导出bs4或者bs8性能更高但转换和排查复杂度会上升。新手建议先用bs1跑通全流程再考虑多batch。--opset 13ONNX的算子集版本。太低的opset可能会缺失某些算子太高了昇腾的算子库不一定支持。CANN 6.x推荐opset 11~13之间。默认不导出NMSYOLOv5的export.py会自动把NMS相关的后处理代码从导出计算图中剔除这个要保持默认。输出ONNX里的三个输出分别是80x80、40x40、20x20的特征图。导出后可以用onnx.checker检查下模型结构完整性。我还会习惯性地用onnxruntime跑一张图片验证一下ONNX的输出确认模型本身没问题再进入ATC转换环节。否则后面转换失败还要回头排查是导出的问题还是转换的问题效率太低。3.3 用ATC把ONNX转换成OM环境准备好之后假设CANN Toolkit安装在/usr/local/Ascend/ascend-toolkit切换环境变量source /usr/local/Ascend/ascend-toolkit/set_env.sh然后执行ATC转换atc --modelyolov5s.onnx \ --framework5 \ --outputyolov5s_bs1 \ --soc_versionAscend310P3 \ --input_shapeimages:1,3,640,640 \ --input_formatNCHW \ --logerror这里每个参数都值得展开说说。--framework5表示输入模型是ONNX。这个数字不是随便写的ATC约定框架的编号是固定的ONNX对应5。--output指定输出OM模型的路径前缀生成的文件是yolov5s_bs1.om。--soc_version是最关键的参数它告诉ATC目标芯片是哪个型号。Atlas 300V 24G一般对应Ascend310P3但也可能是Ascend310P1或Ascend310P4我在上一节提到过可以用npu-smi info查看Name那一列确认芯片具体型号后再填。填错了也不一定立刻报错但转换出来跑起来可能会有不明所以的性能下降甚至个别算子输出会有异常。--input_shapeimages:1,3,640,640对应ONNX里的输入名称和shape。ONNX输入的精确名称可以通过onnx.load后打印graph.input查看YOLOv5官方导出的输入名通常是images。这里管住shape大小写别把NCHW写错。--input_formatNCHW和--input_shape里的顺序要对应。大多数PyTorch导出的模型输入都是NCHW但也有部分自定义导出会把通道维度放在最后面这时候要填NHWC。转换成功后会打印类似ATC run success的信息并在当前目录生成.om文件。然后用同一个工具即可开始推理。如果在转换过程中出现了红字报错不要慌把报错信息里[ERROR]下面的内容逐行看一下遇到Unsupported Op就按我之后在第4章里写的方法处理。3.4 pyACL推理代码骨架模型转换完成后开始写推理代码。下面的代码是用pyACL写的一个最简推理框架它的核心逻辑分为初始化、加载模型、准备输入、执行推理、拷贝输出、后处理几个步骤。import acl import numpy as np import cv2 # 初始化ACL ret acl.init() assert ret 0 # 选择设备 ret acl.rt.set_device(0) assert ret 0 # 加载模型 model_path ./yolov5s_bs1.om model_id acl.mdl.load_from_file(model_path) assert model_id 0 # 创建模型描述用于查询输入输出信息 model_desc acl.mdl.create_desc() ret acl.mdl.get_desc(model_desc, model_id) assert ret 0 # 获取输入输出维度信息 input_size acl.mdl.get_input_size_by_index(model_desc, 0) output_num acl.mdl.get_num_outputs(model_desc) output_sizes [acl.mdl.get_output_size_by_index(model_desc, i) for i in range(output_num)] # 申请输入输出设备内存 input_data np.zeros((1, 3, 640, 640), dtypenp.float32) input_ptr acl.util.np_to_ptr(input_data) dev_input, _ acl.rt.malloc(input_size, 2) dev_outputs [] for size in output_sizes: out_ptr, _ acl.rt.malloc(size, 2) dev_outputs.append(out_ptr) # 把输入数据拷到设备内存 acl.rt.memcpy(dev_input, input_size, input_ptr, input_data.nbytes, acl.rt.MEMCPY_DEVICE_TO_DEVICE) # 预处理图像resize 归一化 HWC-CHW image cv2.imread(test.jpg) image cv2.resize(image, (640, 640)) image image / 255.0 # 归一化到0-1 image image[:, :, ::-1].transpose(2, 0, 1).copy() # BGR转RGB, HWC转CHW image np.expand_dims(image, axis0).astype(np.float32) acl.rt.memcpy(dev_input, input_size, acl.util.np_to_ptr(image), image.nbytes, acl.rt.MEMCPY_DEVICE_TO_DEVICE) # 执行推理 output_data np.zeros(sum(output_sizes), dtypenp.float32) output_ptr acl.util.np_to_ptr(output_data) ret acl.mdl.execute(model_id, dev_input, input_size, dev_outputs, output_sizes) assert ret 0 # 把输出拷回主机内存 host_outputs [] for i, ptr in enumerate(dev_outputs): out_buf np.zeros(output_sizes[i], dtypenp.float32) acl.rt.memcpy(acl.util.np_to_ptr(out_buf), out_buf.nbytes, ptr, output_sizes[i], acl.rt.MEMCPY_DEVICE_TO_DEVICE) host_outputs.append(out_buf) # 后处理解析三个尺度的输出做decode NMS # 这里省略NMS详细代码可参考YOLOv5/utils/general.py中的non_max_suppression # 释放资源 acl.rt.free(dev_input) for ptr in dev_outputs: acl.rt.free(ptr) acl.mdl.unload(model_id) acl.rt.reset_device(0) acl.finalize()这段代码最适合用来理解ACL的整体流程但并不是完整的可用工程主要原因有三点第一预处理部分acl.rt.memcpy的设备到设备拷贝写得比较草率实际项目中我们会直接申请设备内存后用acl.util.numpy_to_ptr建立numpy数组和设备内存的映射避免一次多余的拷贝。第二真实推理时需要根据acl.mdl.get_input_data_size动态判断输入Tensor的大小不能写死。第三代码里注释掉的NMS后处理才是整个检测功能的最后一环。建议的做法是把上面这段框架搭好然后在后处理部分参考YOLOv5开源的NMS代码把三个尺度的输出拼接成[batch, 25200, 85]的矩阵再做阈值过滤和NMS。3.5 性能实测和batch调优我用Atlas 300V 24G跑YOLOv5s在640x640输入、batch1时单次推理延迟大概在20毫秒出头等效约40-50 FPS。这个数字受驱动版本、CANN版本、电源状态的影响会有波动但量级是靠谱的。如果把batch提到4单帧平均延迟会更低吞吐量明显提升因为AI Core的执行单元并得更满。很多人问24G内存能跑多大batch。以YOLOv5s为例bs1时模型激活值大概占用设备内存在几百MB量级你甚至可以bs64但实际推理时batch太大反而会因为内存带宽瓶颈导致单batch延迟上升所以具体的最优batch要靠实测。我的做法是用0到64的batch逐步测试记录延迟和吞吐找到拐点一般是拐点前一个值就是当前场景的最佳batch。多batch最终会直接改变你转换OM时的--input_shape参数。假设最优batch是16那转换命令就变成atc --modelyolov5s.onnx \ --framework5 \ --outputyolov5s_bs16 \ --soc_versionAscend310P3 \ --input_shapeimages:16,3,640,640 \ --input_formatNCHW这里有个容易犯的错误多batch推理时输入图像必须padding或者resize到完全相同的尺寸因为固定shape的OM不支持不同尺寸输入。所以如果你要处理一批尺寸不一的视频流帧必须在预处理阶段统一缩放到640x640再组装成batch。4. 常见问题与排查技巧实录部署Atlas的路上一定会有各种奇奇怪怪的报错。我把自己踩过、也帮别人排查过的一些问题整理成速查表按“现象-原因-解法”的形式写出来遇到问题可以先来这里对一下。4.1 高频问题速查表现象可能原因处理方式npu-smi info找不到卡驱动未安装或驱动与系统内核不匹配重新安装与当前内核版本匹配的驱动reboot后再次检查ATC转换报Unsupported OpONNX里含有昇腾算子库不支持的算子先查算子名升级CANN版本修改网络结构替换对应算子把不支持算子放到后处理加载OM模型报设备内存不足设备内存被其他进程占用或者OM模型动态shape导致内存规划过大用npu-smi info看内存占用改用固定shape重新转换模型杀掉多余进程推理结果全为0或全为NaN输入数据格式不对、归一化方式不对、数据类型不匹配确认输入是CHW且归一化到0-1确认numpy数组dtype为float32用onnxruntime对比输出输出特征图尺寸和预期对不上shape理解错误或AIPP配置了裁剪resize却没有匹配找官方YOLOv5输出的shape对齐把AIPP暂时关掉比对输出推理延迟忽高忽低设备功率限制、散热问题、或服务器CPU被打满查看NPU温度检查CPU负载确认没有其他进程在抢设备资源ATC转出来的OM在推理时输出错得离谱ONNX里算子转换正确但数值精度出现误差检查模型转换日志里的低精度告警必要时用FP16代替FP32重新转换用单张图对比原模型输出看到这里你会发现很多问题其实不是Atlas特有的而是所有“硬件加速推理”的通用问题。输入数据格式错了在GPU上跑出来的结果也可能是错的只不过Atlas的排错链路更短报错信息更底层反而更考验对数据流的理解。4.2 实际踩坑记录我印象最深的一次问题是YOLOv5s转换成功后推理结果中检测框的位置和置信度都正确但同类物体重复框特别多NMS没有起到应有的效果。后来排查发现原因出在于ONNX导出时我默认启用了YOLOv5 export.py里一个叫--nms的参数试图把NMS集成进模型。ATC也确实把NMS转换成OM里的一个算子但运行时那个NMS的IoU处理方式和我预期的不一致相当于转换成功了但效果不对。那次以后我彻底改变做法转换模型时一律不包含NMS后处理全部自己写。这样做虽然增加了代码量但控制了每一个步骤出问题时至少知道问题出在自己代码里而不是黑盒的模型内部。另一个坑是AIPP的输入通道顺序。AIPP是昇腾提供的一个硬件预处理模块可以在模型转换时把归一化、resize、色彩转换这些操作放进模型里推理时硬件自动处理。听起来很舒服但它的通道顺序默认是RGB而OpenCV读图出来的是BGR。如果你把AIPP配置了归一化但忘了配置通道顺序推理结果基本上全是错乱的颜色和低置信度看起来完全不像一个检测结果。后来我的经验是要么完全不用AIPP所有预处理都在主机端用OpenCV做完要么用AIPP时把颜色转换、裁剪、归一化全部在配置里写全不要留缺省项。最后一个跟24G内存相关的坑高并发场景下多线程同时去加载模型会出现设备内存碎片化导致后面某个模型加载失败。这个问题的排查很耗时因为一开始不是必现的。定位到问题之后我的解决思路是在服务启动时就把所有需要常驻的模型全部加载完运行过程中不做动态加载卸载避免内存碎片的累积。这也是为什么24G大内存在实际工程中会被派上大用场的原因——你可以把所有业务模型一次性塞进去不用反复加载释放。4.3 从运维视角看这块卡部署推理卡不只是跑代码硬件的安装和运维同样重要。Atlas 300V 24G是一张PCIe接口的标准卡标称功耗不高但长期跑推理时温度不低。我发现如果服务器散热设计不太好的话连续跑几个小时后NPU温度会冲到70度以上这时候推理延迟会有可感知的波动。所以在机箱里给GPU相关风道留好位置对长期稳定性是大有裨益的。另外Atlas的驱动升级比一般的显卡驱动更依赖配套关系。驱动、固件、CANN三个组件的版本必须对齐升级任何一个都要重新查一遍版本配套表。我通常会先把当前版本记录在一份说明文件里再动手升级避免升级一半发现自己装错了配套版本来回折腾浪费时间。还有一点容易被忽略就是CANN Toolkit里自带的工具链和对操作系统的要求。Atlas 300V在Ubuntu 20.04/22.04这类主流Linux发行版上支持良好但在一些比较小众的内核版本上驱动编译可能失败。我在做环境评估时会优先选择官方文档明确支持的操作系统版本而不是拿一个“感觉差不多”的发行版硬上。写在最后一点个人体会整套部署走下来我最想说的一点是Atlas这类硬件加速平台其实没有想象中神秘它的核心链路就是“训练模型-转换格式-离线推理”三步。真正会花大量时间的不是写那一百多行推理代码而是跟算子兼容、环境版本、内存规划这些底层细节较劲。如果你正在做类似的项目建议把心态放平每遇到一个报错就按“先看日志里的算子名再查版本配套最后考虑改图”的顺序去排查基本上都能找到出路。另外再分享一个小技巧在正式部署之前强烈建议先把ONNX模型在onnxruntime里跑通再用ATC转换成的OM模型对比同一张测试图的输出。ONNX的输出可以作为基准如果OM输出和ONNX输出一致那就说明整个模型推理链路没有任何问题后续即使有问题也可以放心地往后处理部分排查。这个习惯帮我省掉了大量无谓的猜测也适用于其他推理加速平台的部署验证。
返回列表