拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

基于昇腾Atlas 300V实现YOLO模型部署:从ONNX转换到ACL推理的完整实践

基于昇腾Atlas 300V实现YOLO模型部署:从ONNX转换到ACL推理的完整实践 Atlas在深度学习圈子里已经不算陌生了但说实话真正上手跑过一遍的人还是不多。我最初接触它是为了给公司的视觉检测项目换一套国产方案手里的GPU资源紧张采购周期又长于是把目光转向了华为昇腾的Atlas产品线。整个折腾过程从硬件认知到环境部署再到YOLO模型迁移落地中间踩了不少坑也攒了不少经验。这篇文章我就把从零开始到跑通YOLO的完整过程写出来给同样在评估或刚上手Atlas的朋友一个参考。文章不是翻译官方文档而是把我实际操作的命令、参数配置、报错日志和排查思路都整理进去既讲清楚每一步在做什么也说清楚为什么要这么做。如果你正准备在Atlas上部署YOLO系列模型或者正在纠结Atlas 300V这个运算加速卡到底能干什么那这篇文章应该能帮你省下不少试错的时间。1. 整体设计思路与方案选型1.1 Atlas到底是个什么角色很多人第一次听到Atlas容易把它跟专用NPU芯片、整机服务器、甚至开发板这几个概念弄混。简单来说Atlas是华为昇腾面向AI推理和训练场景的全系列产品品牌包含了从板卡、模组、服务器到集群的完整体系。我们日常聊得最多的Atlas 300V就是一张PCIe接口的AI推理加速卡核心芯片是昇腾310P系列专门用来跑深度学习模型的推理任务。这里要厘清一个基本定位Atlas 300V不是显卡不能像NVIDIA的GeForce那样直接用来玩游戏或者做通用计算。它是运算加速卡核心作用是承接训练好的模型推理请求把模型推断的延迟压下来把吞吐提上去。你可以把它理解成一条专门为AI计算设计的生产线虽然不能做所有事情但是做推理这件事它效率很高。从实际项目的角度来说如果公司已有的服务大量依赖PyTorch或者TensorFlow做在线推理CPU扛不住又抢不到GPU资源或者有国产化替代的硬性要求那Atlas系列尤其是300V这个档位的加速卡就是一个很值得研究的方案。它的性价比在特定场景下确实能打尤其是多路视频流分析、目标检测这类推理密集型任务。1.2 为什么选300V而不是其他型号昇腾产品线里推理卡有300I系列和300V系列训练卡有910系列。300V的定位比较特殊它功耗更低体积更小不需要额外供电或者只需要单槽供电适合那种机箱空间有限、功耗预算卡的比较紧的服务器。具体到300V 24G这个版本最直观的优势就是24GB的大显存。做YOLO系列模型部署的时候显存大小直接决定了你能跑多大的batch、能不能把整个模型塞进显存里。举个例子一个YOLOv5m模型转成FP16的OM模型大约占300MB到500MB显存但如果用YOLOv8x这种大模型FP16精度下可能要占用1GB以上。24GB显存意味着在单卡上同时跑多个模型或者开大batch是绰绰有余的这对线上服务的并发能力非常关键。另外300V支持PCIe Gen4协议理论上数据吞吐带宽更高。实际跑推理的时候数据从内存拷贝到显存的耗时会有明显下降。我在部署YOLO的时候输入图像预处理后通过ACL接口拷贝到Device侧整个过程在300V上明显比之前的300I流畅很多。如果你的服务器主板支持PCIe 4.0那这个优势还能进一步放大。1.3 推理部署的技术路线选择在Atlas上做YOLO部署技术路线大体有三种一是直接用MindSpore重写模型重新训练再导出二是用ONNX模型通过ATC工具转成昇腾的OM格式然后基于ACL推理三是通过MindX推理引擎的YOLOV4等预置模型或者用昇腾的TensorFlow/PyTorch适配框架直接跑。我最后选了第二条路PyTorch训练导出ONNX再转OM。理由很直接第一项目里原有模型是用PyTorch训练的重写成本太高第二ONNX是目前最通用的模型交换格式网上资料多排错相对容易第三ATC转换工具已经比较成熟YOLO这种结构相对标准的模型转换成功率很高。这种路线相当于把PyTorch生态和昇腾生态串起来前端的训练、调参完全不受影响后端的推理部署则全部落在昇腾平台上。对大部分现有项目来说这是迁移成本最低、见效最快的方式。2. 环境准备与工具链解析2.1 硬件连接与系统识别先把硬件上的事情说清楚。Atlas 300V插到服务器PCIe插槽后首先要确认系统能不能正确识别到设备。开机后在终端执行lspci命令如果能看出一行包含Huawei或者昇腾310P字样的设备信息那就说明硬件被系统正常发现了。如果lspci里看不到优先排查两个方向一是PCIe插槽是否物理接触良好尤其是一些服务器插槽带固定卡扣没按到底容易接触不良二是BIOS里PCIe的相关设置部分服务器默认关闭了PCIe热插拔或者对非标准设备支持不全在BIOS里把PCIe链路速率设置成Gen3或Gen4自适应然后在启动项里确认Above 4G Decoding选项是开启的。这两个问题如果没处理好后面装驱动会一直报硬件不可用。系统层面建议使用Ubuntu 20.04或者22.04这是昇腾官方支持最完善的两个版本。内核版本不能太老否则驱动编译会出各种莫名错误。我自己的环境是Ubuntu 22.04内核5.15整体兼容性很好安装过程基本一路顺下来。2.2 CANN工具包为什么要装CANN是昇腾计算架构的软件栈全称是Compute Architecture for Neural Networks。类比一下如果我们把Atlas 300V比作一台高性能跑车那CANN就是给这台车配的油路、电控和驾驶系统。它包含了驱动、运行环境、算子库、图编译引擎以及应用开发接口ACL没有它你的加速卡就是一块砖头。CANN的版本选择要特别留意。不同版本的CANN对应不同的驱动版本和固件版本三者必须严格匹配才能正常工作。官方文档里每一版都有对应的配套表建议直接查表操作不要凭感觉组合。我自己最开始图省事装了新版的CANN却用了旧版驱动结果npu-smi信息一直显示异常重装了两遍才排查出是版本不匹配。安装CANN的时候有两个路径一个是普通用户安装一个是root安装。如果服务器是多人共用的建议装在自定义目录下比如/home/yourname/Ascend这样不会污染系统全局环境也方便后面切换版本。装完后一定要source一下set_env.sh脚本把环境变量加载进当前shell否则命令都找不到。这一步很容易被忽略很多人装完发现atc命令不存在其实只是环境变量没生效。2.3 驱动固件安装的正确次序驱动和固件的安装顺序不能乱。昇腾官方的要求是先装固件再装驱动最后装CANN工具包。这个顺序一旦反了轻则重新加载驱动模块重则系统崩溃需要重装。固件安装包通常是.run文件安装命令很简单就是加一个--full参数执行全量安装。装完以后重启机器让固件真正生效。然后装驱动同样是.run文件安装完成后用npu-smi info命令验证如果能输出设备信息列表包含芯片型号和显存大小说明驱动工作正常。有一个我踩过的坑Ubuntu系统如果开启了Secure Boot驱动安装后模块无法被加载。报错信息往往是insmod失败或者Operation not permitted。解决的方法有两种要么在BIOS里关闭Secure Boot要么给驱动模块签名。对开发环境来说直接关掉Secure Boot最省事但生产环境要谨慎评估安全性。2.4 开发环境与容器化部署的选择环境装好了以后还有一个选择摆在面前直接在宿主机上做开发还是用Docker容器。我强烈建议用Docker。理由有三点第一CANN版本升级或者切换时容器环境隔离能帮你省去大量重复安装的时间第二不同项目可能需要不同的CANN版本容器可以各自独立互不干扰第三交付给运维时一个打包好的镜像远比一堆安装命令更可靠。昇腾官方提供了带CANN的镜像包里面有配套的驱动和软件栈拉取后只需在启动容器时挂载设备。启动命令大致是这样的docker run -it \ --name atals_yolo \ --device/dev/davinci0 \ --device/dev/davinci_manager \ --device/dev/hisi_hdc \ -v /usr/local/Ascend/driver:/usr/local/Ascend/driver \ -v /data/atals_project:/workspace \ ascend-ai:latest /bin/bash这里的核心是--device参数把昇腾设备映射进容器-v把驱动目录挂载进去缺失任何一项容器内部都无法访问NPU。进去以后在容器里再source一遍CANN的环境变量脚本然后就可以正常使用atc和ACL接口了。如果启动时提示找不到设备先检查宿主机上npu-smi是否正常再检查容器启动参数里的设备映射是否齐全。3. 模型转换与YOLO部署实操3.1 从PyTorch模型导出ONNX在Atlas上部署YOLO第一步是把PyTorch的权重文件转成ONNX。这一步看着简单实际上有很多细节。以YOLOv5为例官方仓库的export.py可以直接导出ONNX但要注意导出的Opset版本。ATC转换工具对ONNX的算子支持有版本范围当前版本的CANN建议使用Opset 11到13之间的版本太新或太旧都会出现算子不支持的情况。如果你用的是YOLOv8Ultralytics也提供了导出命令。有一个常见问题默认导出的ONNX文件带了后处理部分比如NMS层但ATC对动态NMS这类算子的支持并不完美转换时容易报错。我的做法是导出时额外加参数把端到端部分剥离掉只保留Backbone和Head的特征输出后处理放到推理代码里用Pytho实现。这样做的好处是模型转换更容易成功坏处就是推理代码要自己多写一点NMS逻辑但可控性更强。导出ONNX时还需要注意输入尺寸。YOLO模型一般支持任意尺寸输入但ONNX导出的静态图会固定输入尺寸。如果你希望推理时支持动态尺寸需要在导出时设置dynamic_axes给输入输出维度指定动态轴。不过ATC工具对动态尺寸的支持有限动态分辨率会带来额外的性能开销所以我建议直接固定输入尺寸比如640x640实在需要多尺寸就导多个OM模型推理时按需加载。这个思路在实际部署里更稳妥。3.2 ATC工具转换OM模型拿到ONNX文件后接下来就是用ATC工具把它转成昇腾专用的OM格式。ATC工具的核心命令是atc一个参数非常多的命令行工具。我总结了一个基础模板可以直接套用atc --modelyolov5s.onnx \ --framework5 \ --outputyolov5s_640 \ --input_shapeimages:1,3,640,640 \ --soc_versionAscend310P3 \ --input_formatNCHW \ --output_typeFP16 \ --loginfo逐个解释一下关键参数。--model指定输入模型文件--framework5表示ONNX格式这个数字不能记错4是Caffe3是MindSpore。--output是输出文件名。--input_shape定义了输入张量的形状这跟ONNX导出时的输入名和尺寸必须严格一致。--soc_version要根据你的卡来填300V对应的昇腾310P系列通常填写Ascend310P3或者Ascend310P4具体以驱动信息为准填错了会直接报错。--output_typeFP16是精度设置。FP16相比FP32推理速度更快显存占用更低检测精度基本不会受影响。我验证过YOLOv8m在FP16和FP32两种精度下mAP差异不到0.5%但FP16的推理延迟下降了将近40%。在检测场景完全可以接受但如果你做的是精度敏感的医疗影像之类的推理任务建议先评估再决定。转换过程会输出大量日志如果顺利的话最后会看到生成SUCCESS的提示。如果中途报错大多数情况是算子不支持。日志里会明确指出是哪个算子、在模型哪一层常见的有GridSample、DeformConv等。遇到这种情况要么回ONNX导出时修改对应层的实现要么在ATC命令中加入--op_type_map参数做算子映射。更实用的办法是先在MindStudio里打开ONNX模型做算子可视化检查找出不支持的红点再针对性处理。3.3 NPU推理主流程与ACL接口调用模型转换成功之后就要写推理代码了。昇腾的推理接口叫ACL全称Ascend Computing Language。它的编程模型跟CUDA很像有Device概念有显存申请释放有数据在Host和Device之间的拷贝。如果你写过CUDA程序上手ACL会非常快。整个推理流程大概分这么几步初始化ACL、加载OM模型、申请输入输出内存、准备输入数据、执行模型、取回输出结果、释放资源。下面这一段是核心流程的骨架代码import acl import numpy as np # 初始化ACL ret acl.init() ret acl.rt.set_device(0) # 加载模型 model_id 0 ret acl.mdl.load_from_file(yolov5s_640.om, model_id) # 获取模型输入输出信息 input_desc acl.mdl.create_desc() output_desc acl.mdl.create_desc() acl.mdl.get_desc(input_desc, model_id, 0) acl.mdl.get_desc(output_desc, model_id, 0) input_size acl.mdl.get_desc_size(input_desc) output_size acl.mdl.get_desc_size(output_desc) # 申请Device侧内存 input_buffer, input_ptr acl.rt.malloc(input_size, 2 * 1024 * 1024) output_buffer, output_ptr acl.rt.malloc(output_size, 2 * 1024 * 1024) # 拷贝输入数据到Device acl.rt.memcpy(input_ptr, input_size, img_input.tobytes(), input_size, acl.rt.MEMCPY_HOST_TO_DEVICE) # 执行推理 acl.mdl.execute(model_id, input_ptr, input_size, output_ptr, output_size) # 取回输出 output_data acl.util.bytes_to_ptr(acl.rt.memcpy(output_buffer, output_size, output_ptr, output_size, acl.rt.MEMCPY_DEVICE_TO_HOST))这段代码是我精简过的版本实际项目中还要加上输入图像的预处理resize、归一化、通道转换、输出的后处理解码检测框、NMS、标签映射但整体骨架就是上面的思路理解清楚每一步的作用剩下的填充只是时间问题。有一点要特别提醒ACL接口的输入输出数据格式跟PyTorch不完全一样。PyTorch的Tensor是NHWC还是NCHW要看模型本身OM模型的输入必须和ATC转换时指定的--input_format一致否则推理结果的维度是反的检测框会乱套。我最初跑通以后发现坐标完全不对排查了很久才想到是输入图像在做预处理的时候通道顺序用了RGB但模型训练时用的是BGR导致检测完全失效。3.4 后处理逻辑在CPU侧实现ONNX导出时把NMS等后处理剥离掉意味着推理出来的原始输出是模型的三个Head结果需要我们在代码里自己解析。YOLOv5和YOLOv8的输出结构略有不同。YOLOv5的输出形状是(1, 25200, 85)其中25200是三个尺度特征图的候选框总数85是4个坐标、1个置信度、80个类别概率。YOLOv8的输出则是(1, 84, 8400)8400是候选框总数84是4个坐标加80个类别概率。解析逻辑一般从后处理函数开始先从输出中取出所有候选框过滤掉置信度低的做类别筛选再执行NMS。NMS的实现不难但要考虑性能。如果直接写双重循环做IoU计算在Python里跑会比较慢尤其是候选框数量大的时候。我这里推荐一种思路先把所有候选框按置信度排序然后从高到低遍历用numpy的向量化运算计算IoU矩阵一次性过滤掉重叠框。实测下来处理一帧图像的NMS耗时在5毫秒以内可以满足实时性要求。还有一个工程细节后处理是在CPU侧执行的。推理完成后数据从Device侧拷回Host侧后处理代码用的是普通的numpy不涉及NPU操作。如果后处理太慢拖了整体延迟可以考虑多线程处理或者把图像批次加大提升并行度。我的项目里是4路视频流同时推理每路视频的帧率在25fps左右NMS处理耗时完全跟得上。3.5 推理性能调优的几个方向模型跑通之后接下来就是性能优化。我整理过一套调优流程先看耗时分布再做针对性优化。用程序的计时接口分别统计预处理耗时、H2D拷贝耗时、NPU推理耗时、D2H拷贝耗时、后处理耗时找出瓶颈再动手。在Atlas 300V上部署YOLO最有效的优化手段是开启IO复用也就是输入输出内存一次性申请好后循环使用避免每帧都重新malloc和释放。内存分配在CPU侧开销不大但频繁的系统调用会造成抖动影响稳定性。另一个方向是使用ACL的同步执行模式还是异步执行模式。异步模式下可以在一帧推理的同时对下一帧做预处理利用设备侧和主机侧的重叠执行来隐藏部分延迟。还有一个容易被忽略的性能点图像的预处理在CPU上做还是在Device上做。CANN提供了AIPPArtificial Intelligence Pre-Processing模块可以在ATC转换时配置输入图像的预处理参数比如resize、减均值、除方差让数据不经过CPU预处理直接从图像数据转换为模型输入格式。使用AIPP后H2D拷贝的数据量会减少CPU占用也会下降。我实际测试过启用AIPP后单帧端到端延迟降低了将近15%而且CPU占用率明显下降对同时运行多个服务进程的服务器很有意义。4. 常见问题与排查技巧实录4.1 设备无法正常初始化的排查一套环境装下来大家遇到最多的就是ACL初始化失败。在调用acl.init()之后紧接着调用acl.rt.set_device(0)时报错Device 0 is unavailable之类的提示。这个问题通常有三个原因驱动没装好、固件版本不匹配、设备权限不足。先做快速自检在终端执行npu-smi info看能否正常显示设备信息。如果显示文件或目录不存在说明驱动模块没加载用lsmod检查davinci相关模块是否在列表中不在就手动加载。如果能显示信息但ACL初始化依然失败考虑是权限问题。检查当前用户是否在HwHiAiUser用户组中如果不在用usermod -aG HwHiAiUser yourname加进去然后重新登录。如果你用了Docker容器还需要额外排查设备映射。容器内调用ACL时报权限错误多半是启动容器时没有把/dev/davinci_manager这个设备映射进去这个设备主要负责芯片管理和资源分配少了它内部的ACL就找不到NPU。这种场景下在宿主机上npu-smi是正常的很容易误导人要注意区分。4.2 OM模型加载失败或推理结果异常的定位思路OM模型加载时报错按我的经验八成是模型转换时soc_version填错了。很多型号都要精确匹配到某个子版本比如Ascend310P3和Ascend310P4虽然看着相似但算子库并不完全一致。确认方法是在npu-smi info的输出中查看芯片名称或者在CANN安装目录下执行npu-smi info -t board查看版本信息然后和模型转换时填入的soc_version一一对照。推理结果异常这块我遇到过一个非常隐蔽的问题OM模型推理输出全是0或崩溃。排查发现是输入数据的类型不对。PyTorch模型的输入是float32但ATC转换时指定了FP16精度如果推理代码里传入的numpy数组类型是float64或者uint8ACL接口不会自动做类型转换拷贝到Device侧的数据直接解析错误。建议每次推理前对输入数据做一次强制转换np.float32是基本功这点在CUDA编程里也有类似要求但在Atlas上更严格。还有一个问题是推理结果时好时坏同一张图多跑几次结果不一样。遇到这个问题先看是不是内存越界。ACL的mvnc接口不检查边界如果申请的Model输入缓冲区大小与实际数据不符可能覆盖到其他内存区域。我记得有一次把图像宽高搞混输入数据内存超过申请的内存大小结果就是偶发性的推理错乱。后来通过日志打印malloc的size和实际拷贝的bytes一对比就发现了问题。4.3 性能达不到预期的常见瓶颈很多朋友在刚上手Atlas的时候会用AI芯片的单卡算力去估算推理性能结果实测差距很大。性能不到预期的原因主要集中在预处理瓶颈、CPU侧后处理耗时和模型频繁加载三个地方。预处理瓶颈是指图像读取、resize、归一化这些操作全部在CPU上执行如果图像分辨率大帧率高CPU忙不过来NPU只能空转等待。解决办法是前面提到的AIPP把预处理放到NPU的AICPU上执行释放CPU资源同时减少H2D拷贝的数据量。CPU侧后处理耗时的优化方向主要是NMS逻辑前面写了用numpy向量化替代循环。如果用了大量Python的自定义Operator还要考虑是否算子过于复杂不便于向量化。有时候把NMS的逻辑用Cython改写性能也会提升一个档次。不过项目早期优先保证逻辑正确再考虑这一层优化。模型频繁加载是指每次推理前都把OM模型重新加载一遍这个操作开销很大甚至可能比推理本身还慢。ACL提供了模型常驻机制初始化时加载一次多次推理循环复用同一个model_id。这个属于基础用法但我在一些开源代码仓库里见到过反复加载模型的写法确实存在这种问题。模式上注意一下就好不用刻意优化。4.4 多模型并发与多路流场景的注意事项如果你的场景需要同时跑多个模型比如一个检测模型一个分类模型串联工作或多个视频流各自推理在Atlas 300V上需要做并发设计。ACL是支持多模型并发加载的只要显存足够可以加载多个OM模型每个模型独立执行。但要注意NPU的算力是共享的如果两个模型同时跑彼此会争抢算力资源导致各自的推理延迟都有上升。这跟GPU上多任务并发是一样的道理。如果对延迟敏感可以采用任务调度策略把不同优先级的模型放到不同的Stream里用ACL的事件同步机制做交错执行。不过这对系统的复杂度有要求简单场景下建议串行执行把batch加大来提升整体吞吐逻辑上更清晰。多路视频流场景还有一个容易忽视的问题推理任务的显存碎片。每个Stream跑久了不断申请释放内存可能导致显存碎片化虽然单次显存申请不超过24G但碎片多了之后容易触发显存分配失败。我的做法是在初始化阶段一次性把所有可能需要的内存都申请好比如一个推理池接口内部预分配若干份输入输出缓冲配合一个简单的队列做分配回收管理这样既保证了并发度又避免了碎片问题。5. 部署后的稳定性与交付经验环境终于调通了推理精度也正常了性能测试也在可接受范围内了但离真正的上线还有一段路。稳定性这件事我是吃过亏的。跑Demo的时候一切正常一上生产就崩崩溃原因往往不在模型本身而在外围的资源管理和异常处理。第一点是内存泄漏问题。ACL接口申请到的内存如果在推理循环中不断分配而不释放正常运行几小时就会把系统内存或者显存吃光。使用Python进行开发时尤其要注意虽然Python有垃圾回收机制但ACL的内存是由C侧库管理的Python层做不到自动释放。我在代码里加了显存和内存的监控每执行1000帧打印一次使用量很快就能排查出是否有泄漏。线上环境建议设置周期性的健康检查脚本用npu-smi info监控显存占用超过阈值自动告警并重启容器。第二点是模型运行时的温度控制。昇腾芯片在高负载下会发热如果服务器散热不好芯片温度过高会触发降频甚至保护性停机。300V这个卡本身功耗不高但如果在密闭机箱里满载跑温度还是比较可观的。有条件的话用npu-smi query命令定时记录芯片温度或者在业务高峰期关注日志里有没有温度告警。第三点是灰度发布和回滚方案。我在交付项目的时候不会直接把新版模型替换掉线上的旧模型而是新老模型同时部署用流量切换来控制调用比例。确认新模型稳定运行一到两天再逐步把流量全部切过去。一旦新模型出现问题按下回滚开关就能切回旧模型。这种方案对在线服务很重要昇腾的ACL接口支持动态卸载和加载模型虽然是重量级操作但作为回滚手段足够了因为回滚本来就不要求秒级完成。说实话昇腾这套生态还在不断成熟的过程中跟CUDA相比确实有一些地方不够顺手算子覆盖、调试工具、社区资料都还有成长空间。但在我实际跑通并上线维护的这段时间里它的推理性能、稳定性、性价比在推理场景下是能满足生产要求的。尤其是国产化趋势下尽早积累昇腾相关的部署经验对团队和个人来说都是一个加分项。就以我自己为例从对Atlas一窍不通到完成YOLOv5和YOLOv8两个模型的迁移上线前后花了大概两周真正卡住我的不是模型转换也不是推理接口而是环境版本匹配和硬件识别这些底层问题。这篇内容把那些坑都提前标出来了希望能帮你少走这些弯路。后面如果你们也碰到具体的算子兼容或者推理精度对不上的问题欢迎多交流毕竟这类实际踩坑记录往往比官方文档更有参考价值。
返回列表