ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Atlas 300V部署YOLOv5全流程:从模型转换到推理加速

Atlas 300V部署YOLOv5全流程:从模型转换到推理加速 我一直觉得搞AI部署的人尤其是做边缘端和推理加速的手上没摸过一两块像样的加速卡职业生涯都不算完整。前段时间我正好从渠道拿到一张Atlas 300V 24G加速卡配合手头一个YOLOv5检测项目前前后后折腾了一周。这中间踩了不少坑也把整个流程跑通了今天就把这套从零开始部署YOLO的完整过程连同我对这张卡的理解一次性写清楚。这篇内容不单单是告诉你“怎么装驱动、怎么跑模型”更重要的是讲清楚Atlas 300V到底是什么定位、和训练卡有什么区别、24G显存严格说叫内存到底能干什么以及为什么YOLO这类检测模型在Atlas上的部署链路和GPU上完全不一样。不管你是刚入门AI推理的小白还是已经在用GPU做部署、想了解国产算力方案的老手这篇文章都能给你一个相对完整的实操参考。1. 先搞清楚Atlas是什么以及“Atlas 300V 24G”到底是不是运算加速卡在真正动手部署之前我花了不少时间研究Atlas产品线的命名规则。说实话华为昇腾这个产品线的命名早期确实有点混乱很多人在网上问“Atlas 300V 24G是运算加速卡吗”说明大家都被绕晕了。这一点必须第一句话说清楚Atlas 300V 24G是运算加速卡而且是专门做推理的加速卡不是训练卡。它属于华为昇腾推理系列主打的是高能效比的AI推理场景比如视频分析、目标检测、图像分类这些。1.1 一张图理清Atlas产品线的分类逻辑Atlas系列产品这里只说硬件形态不谈云服务大致可以分成几个方向我整理成表格帮大家快速建立坐标系产品形态典型型号核心定位适用场景加速卡插卡式Atlas 300V、Atlas 300I Pro、Atlas 300T挂载在服务器上做AI加速数据中心推理、视频分析、边缘服务器智能边缘服务器Atlas 500 A2、Atlas 800整机交付自带算力和管理交通、园区、工业视觉等边缘场景开发者套件Atlas 200 DK小开发板适合学习验证入门学习、算法验证、小型原型如果你电脑里塞了一张Atlas 300V本质上就是给服务器插了一块AI推理加速卡。它的内部核心是昇腾AI处理器不同型号对应不同规格的AI Core数量、内存容量和算力大小。Atlas 300V 24G这个型号光看命名就能读出几个关键信息300V代表它的系列定位是视频分析加强版Video24G代表板载内存是24GB。这里要特别说明一下很多人习惯把显卡的显存概念带过来但昇腾卡的内存不叫显存官方文档里一般叫“内存”。虽然有24G但它跟NVIDIA RTX 3090那24G GDDR6X显存的设计哲学完全不是一回事。昇腾更强调的是内存带宽和算力的匹配以及多卡、多路视频解码时的稳定性。1.2 Atlas 300V和GPU推理卡的核心差异在实际部署中我深刻体会到Atlas 300V和普通NVIDIA显卡在以下几个层面的差异这也直接影响了后续部署方案的选型第一架构思路不同。GPU走的是SIMT单指令多线程那一套靠海量线程并行掩盖延迟昇腾走的是达芬奇架构里面分成了AI Core、AI CPU和Ctrl CPUAI Core内部又有Cube、Vector、Scalar三种单元。这两种架构没有绝对的谁优谁劣但决定了你在做算子优化时的思路完全不一样。第二推理卡更看重“整个数据处理流水线”。Atlas 300V内置了DVPP数字视觉预处理模块可以硬解码视频、做图片缩放、抠图、色域转换这些操作都不占用AI Core算力。我实测下来用DVPP做视频解码和缩放比直接用CPU软解再送模型推理要省掉一大截延迟。这点在做YOLO实时检测的时候特别香因为YOLO的预处理链路里缩放、归一化、通道变换这种操作又碎又频繁。第三软件栈是自成一派的。昇腾的软件栈核心是CANNCompute Architecture for Neural Networks相当于昇腾的“CUDA”。所有上层的推理框架比如MindSpore、ONNX Runtime、OpenCV的DNN模块最后都要落到CANN这一层来调动硬件算力。这意味着你之前习惯的“装个CUDA、配个cuDNN、然后直接跑”的路子在昇腾上行不通你必须重新走一遍“模型转换”的流程。弄清楚了这三点你就明白了为什么“能不能用来跑YOLO”这个看似简单的问题答案却是“能但要换一套玩法”。GPU上最常见的流程是直接用PyTorch加载.pt权重做推理或者用TensorRT加速Atlas上则必须先把PyTorch模型导出成ONNX再通过ATC工具转成昇腾的.om离线模型最后基于ACLAscend Computing Language的Python或C接口来调用。这个流程刚开始会觉得繁琐但用顺了之后它的推理性能和稳定性确实很能打。2. 部署YOLO前的核心准备CANN、固件与驱动的完整搭配任何加速卡都离不开软件栈Atlas 300V也一样。但昇腾的软件安装有个特别容易让人崩溃的点版本匹配极其严格。我一开始就踩了个大坑——驱动版本和CANN版本对不上导致npu-smi昇腾的显卡状态查询命令类似于NVIDIA的nvidia-smi能识别到卡但一跑样例就报错。所以这一节我重点把环境准备讲透这部分没做好后面全是白费。2.1 理清CANN、驱动、固件三者之间的关系先打一个生活化的比方。硬件卡是“毛坯房”驱动是“水电管道”CANN是“精装修方案”。驱动负责让操作系统能够识别并控制这张卡固件负责卡的底层微码运行而CANN是你真正调用算力做AI推理的软件平台。三者的版本有一个兼容矩阵不存在“最新的一定好用”这个说法。我最终采用的版本组合是操作系统Ubuntu 20.04.6 LTS64位驱动版本Ascend-hdk-310p-npu-driver_23.0.rc3_linux-aarch64.run注意服务器是aarch64架构x86要用x86_64的包固件版本Ascend-hdk-310p-npu-firmware_23.0.rc3.runCANN版本CANN 6.3.RC3安装顺序是先装驱动再装固件最后装CANN。这个顺序不能颠倒因为固件升级需要依赖驱动先加载起来。2.2 驱动与固件安装的完整实操命令假设你已经拿到了root权限并且把安装包上传到了服务器上整个过程如下# 1. 安装驱动 chmod x Ascend-hdk-310p-npu-driver_23.0.rc3_linux-aarch64.run ./Ascend-hdk-310p-npu-driver_23.0.rc3_linux-aarch64.run --full # 2. 安装固件驱动装完之后才能装 chmod x Ascend-hdk-310p-npu-firmware_23.0.rc3.run ./Ascend-hdk-310p-npu-firmware_23.0.rc3.run --full # 3. 验证是否安装成功 npu-smi info如果第二条命令执行后输出类似下图这样的信息能看到芯片名称、内存容量、温度、功耗这些字段说明驱动和固件已经正常工作了-------------------------------------------------------------------------------------------- | npu-smi 23.0.rc3 Version: 23.0.rc3 | ------------------------------------------------------------------------------------------ | NPU Name | Health | Power(W) Temp(C) HugepagesUsage(page) | | Chip | Bus-Id | AICore(%) Memory-Usage(MB) | | 300V | OK | 12.5 42 0 | | 310P3 | 0000:31:00.0 | 0 1658 / 24576 | ------------------------------------------------------------------------------------------看到“Health: OK”是最低标准。如果这里显示异常先进BIOS确认一下卡是否被正确识别再看一下服务器是否开启了Above 4G Decoding后面讲常见问题时会细说。2.3 CANN开发套件的安装与环境变量配置CANN的安装同样是一个.run包安装过程不是解压就完事它涉及到一些系统依赖的检查和环境变量的配置。# 1. 安装CANN工具包 chmod x Ascend-cann-toolkit_6.3.RC3_linux-aarch64.run ./Ascend-cann-toolkit_6.3.RC3_linux-aarch64.run --install # 2. 配置环境变量建议写入~/.bashrc这样每次登录自动生效 echo source /usr/local/Ascend/ascend-toolkit/set_env.sh ~/.bashrc source ~/.bashrc这里有一个小细节值得重点说如果你用的是Python 3.9以上的版本需要额外安装CANN配套的Python API包也就是“Ascend-cann-pyhon”系列。我一开始装的是Python 3.10结果导入ais_bench、aclruntime这些库时各种报找不到模块最后查资料发现是CANN自带的Python绑定是基于3.7/3.9编译的3.10需要装额外适配包。建议直接用Python 3.9省心很多。环境变量配好后快速验证一下CANN是否能正常导入python3 -c import acl; print(acl.__version__)如果打印出版本号恭喜你已经有继续往下走的资格了。否则回到上一步逐一排查。3. 把YOLOv5从PyTorch权重转换为昇腾离线模型这应该是全流程中“最昇腾”的一步也是坑最多的一步。很多第一次接触Atlas的人在这里卡了至少两三天因为PyTorch的权重文件.pt、.pth昇腾原生不认识必须转换成.om格式。整个链路是PyTorch权重 - ONNX - OM。前一步是通用导出后一步是昇腾的ATC工具完成。3.1 导出ONNX需要修改YOLOv5源码的几处关键点YOLOv5官方仓库本身支持导出ONNX但是直接导出的模型在转换OM时往往会出问题原因在于模型输出层。YOLOv5的原始输出是一个或多个尺度的特征图张量后续的NMS非极大值抑制是在PyTorch的代码里完成的但ASCEND的推理流程希望把“模型输出”和“后处理”尽量分离或者直接利用昇腾的融合算子进行后处理。为了部署方便我选择在导出ONNX时把输出层的结构改得更简洁一些。我用的YOLOv5版本是v6.0这个版本的代码结构最稳定网上资料也多修改的核心在models/yolo.py的Detect类forward方法里。原始输出是一个list包含三个尺度的检测结果。我在导出的临时脚本里把它们concat成一个张量并调整维度顺序# 在yolo.py的Detect.forward中导出ONNX时走这个分支 if self.export: # 原输出: list of [batch, 3, 80, 80, 85] x 3 # 需要变成: [batch, 3 * num_anchors, 85] 这种格式 z torch.cat(z, 1) # 把所有尺度的预测结果在anchor维度拼接 return z.view(batch, -1, 85) # 85 80类 4个坐标 1个objectness这里为什么要拼接成一个张量因为ONNX的转换器对“多个动态输出”的支持没有“单输出”那么友好而且后续ATC做算子融合的时候一个张量输出的处理效率更高。当然如果你有特殊的NMS需求也可以保留多输出但对应要修改ATC的配置文件复杂度上去了不少。导出命令很简单python3 export.py --weights yolov5s.pt --include onnx --img-size 640 640 --batch-size 1验证一下生成的ONNX模型python3 -c import onnx; m onnx.load(yolov5s.onnx); onnx.checker.check_model(m); print(OK)输出OK代表模型结构没问题但如果报错多半是operatorset版本过高或者某个算子不兼容。我建议在export.py里确认一下torch.onnx.export里的opset_version一般设12比较稳妥太高了ATC不一定认。3.2 ATC工具转换核心参数与踩坑记录拿到ONNX之后下一步就是用ATCAscend Tensor Compiler把它转成.om模型。ATC的路径一般在$ASCEND_TOOLKIT_HOME/atc/bin/atc配置好环境变量后可以直接执行。我的转换命令如下atc --modelyolov5s.onnx \ --framework5 \ --outputyolov5s_bs1 \ --input_shapeimages:1,3,640,640 \ --soc_versionAscend310P3 \ --insert_op_confaipp_yolov5.cfg \ --output_typeFP32 \ --loginfo这里有几个参数我得详细解释--framework55代表ONNX这个是ATC约定好的编号不能记错。--soc_versionAscend310P3这是最关键的一个参数必须和你的硬件匹配。Atlas 300V 24G的核心芯片是Ascend 310P3很多人就在这一步报错提示EI0001: Value of soc_version is invalid。解决方式是先用npu-smi info确认芯片型号再对应修改。--insert_op_confaipp_yolov5.cfg这个配置文件用于图像预处理。AI Core执行推理时数据会先经过DVPP预处理。我原本以为AIPP只是做一个归一化实际上它还能做图片缩放和通道转换。如果是用DVPP做缩放AIPP里就不要再做resize否则两次缩放会导致精度偏差。我的配置文件核心内容如下aipp_op { aipp_mode: static input_format: RGB888_U8 src_image_size_w: 640 src_image_size_h: 640 crop: false mean_chn_0: 0 mean_chn_1: 0 mean_chn_2: 0 var_reci_chn_0: 0.003921569 var_reci_chn_1: 0.003921569 var_reci_chn_2: 0.003921569 }这里把均值设成0方差设成1/255也就是把像素值从0-255归一化到0-1。如果你要使用ImageNet的均值[0.485, 0.456, 0.406]和方差这里也可以设置但要注意YOLOv5官方训练时只用到了/255归一化所以不要画蛇添足。转换成功的标志是输出日志里有build module success并且目录下生成了yolov5s_bs1.om文件。如果没有成功最常见的原因是算子不支持。我遇到的情况是ONNX里带有GridSample算子较高版本的YOLOv5用到了一些新算子ATC不支持。解决方式是回退到v6.0版本或者手动去除这些算子。3.3 关于batch size的选择思路在ATC转换时--input_shape里的batch size一旦固定运行时的batch size就不能改了。你可能会问为什么不直接设置动态batch理论上可以加--dynamic_batch_size参数但我实际测试下来动态batch会导致推理性能下降而且多batch时显存分配不稳定。做边缘端部署时稳定性优先于灵活性我建议直接固定batch size为1。如果确实需要提高吞吐优先考虑多路Stream并发而不是动态batch后面讲性能调优时细说。4. 基于ACL的Python推理代码从读图到输出检测框有了.om模型下一步就是在Atlas上跑推理。昇腾官方提供了ACLAscend Computing Language的Python接口但我更推荐直接用官方开源的ais_bench工具它封装好了很多底层细节你只需要关心输入输出。不过为了让你对原理有更深的理解我把自己的推理代码核心逻辑拆解一遍读完你就能明白整个调用链路上发生了什么事。4.1 初始化与模型加载的关键步骤ACL的推理流程可以拆成五步初始化、设备管理、上下文创建、模型加载、数据搬运。对应代码如下import acl import numpy as np # 1. 初始化ACL ret acl.init() assert ret 0 # 2. 设置设备0号设备 ret acl.rt.set_device(0) assert ret 0 # 3. 创建上下文可以理解为一个“运行空间” context, ret acl.rt.create_context(0) assert ret 0 # 4. 加载om模型 model_id, ret acl.mdl.load_from_file(yolov5s_bs1.om) assert ret 0 # 5. 获取模型描述信息用于分配输入输出内存 model_desc acl.mdl.create_desc() ret acl.mdl.get_desc(model_desc, model_id) # 模型输入大小 input_size acl.mdl.get_input_size_by_index(model_desc, 0) # 模型输出大小 output_size acl.mdl.get_output_size_by_index(model_desc, 0) # 分配设备内存 input_ptr, ret acl.rt.malloc(input_size, 2) # 2表示内存对齐单位2M output_ptr, ret acl.rt.malloc(output_size, 2)这里有两个容易出问题的点。第一acl.rt.malloc的第二个参数是内存对齐单位官方建议给2大家照做就行不要改成别的。第二输入输出数据的传输需要先把设备内存地址和numpy数组建立一个“绑定”关系ACL里通过acl.rt.memcpy完成整体流程稍微繁琐。为了方便调用我把它封装成一个简单的推理类。下面这版代码我在改造后用起来相对顺手class AscendYOLO: def __init__(self, om_path): # 上述初始化加载模型的逻辑 pass def infer(self, input_data): # input_data: 经过预处理后的np.ndarray, shape (1,3,640,640) # 把数据拷贝到设备端 acl.rt.memcpy(input_ptr, input_size, input_data.ctypes.data, input_size, acl.rt.MEMCPY_DEVICE_TO_DEVICE) # 执行推理 ret acl.mdl.execute(model_id, [input_ptr], [output_ptr]) assert ret 0 # 把输出从设备拷贝回主机 output_data np.zeros(output_size, dtypenp.uint8) acl.rt.memcpy(output_data.ctypes.data, output_size, output_ptr, output_size, acl.rt.MEMCPY_DEVICE_TO_HOST) return output_data然后别忘了在程序退出时释放资源acl.mdl.unload(model_id) acl.rt.destroy_context(context) acl.rt.reset_device(0) acl.finalize()很多人写起来忘了释放平时看不出来跑长时间服务的时候内存会慢慢涨最后OOM。这点必须养成习惯。4.2 图像预处理千万不要直接copy YOLOv5的预处理代码YOLOv5官方推理时预处理用了letterbox操作——把长边缩放到640同时给短边填充灰色114,114,114。这个操作在GPU上就是一个简单的resizecopy但在Atlas上你如果用Python的OpenCV去逐帧做letterbox速度会非常慢而且CPU占用率极高。如果要追求极致性能应该把缩放和填充操作下沉到DVPP硬件模块完成通过pyav或者官方dvpp接口传入原始图片数据让DVPP完成JPEG解码、缩放、像素格式转换然后直接得到模型输入。但DVPP的API相对底层上手成本高。所以我推荐一个折中方案主路上使用ONNX导出的预处理转换即先在Python里做一次resize到640x640不保持比例然后归一化实测精度损失很小但速度提升了三到五倍。如果场景对精度要求极为苛刻再上DVPP也不会晚。具体到推理代码我做了这样的预处理def preprocess(image_rgb): # image_rgb: HWC, uint8, RGB img_resized cv2.resize(image_rgb, (640, 640), interpolationcv2.INTER_LINEAR) img_norm img_resized.astype(np.float32) / 255.0 # HWC - CHW and add batch dim img_nchw np.transpose(img_norm, (2, 0, 1))[None, ...] return np.ascontiguousarray(img_nchw, dtypenp.float32)这里再提醒一下如果你在ATC转换时的AIPP配置里已经写了归一化参数那么在Python侧就不能再做/255了否则等于归一化两次。我建议把“归一化”这个操作完全交给AIPP硬件去做Python端只做resize和通道转换。这样速度更快代码也更干净。4.3 后处理把输出张量变成检测框模型输出的shape是(1, 25200, 85)其中25200是三个尺度的总anchor数80x80 40x40 20x2085是80个类别加上x,y,w,h,obj。这一步要做的操作和PyTorch端后处理基本一致把center-x, center-y, width, height还原成左上角和右下角坐标。过滤掉objectness置信度低的框阈值我一般设0.25。按类别做NMSIoU阈值设0.45。还原到原始图片尺寸。NMS这一步如果在CPU上用Python循环做一个batch就要几十毫秒非常拖后腿。ACL本身没有提供NMS的Python接口但CANN中有一个“DETECTION_POSTPROCESS”的融合算子可以用不过配置复杂。我在实际项目里选择的是用OpenCV的cv2.dnn.NMSBoxes来替代速度还不错在CPU上大约每张图5-10毫秒。import cv2 def postprocess(pred, conf_thres0.25, iou_thres0.45): pred pred[0] # (25200, 85) boxes [] scores [] class_ids [] for i in range(pred.shape[0]): obj_conf pred[i, 4] if obj_conf conf_thres: continue classes_scores pred[i, 5:] class_id np.argmax(classes_scores) class_score classes_scores[class_id] final_score obj_conf * class_score if final_score conf_thres: continue # 将cxcywh转换为xyxy cx, cy, w, h pred[i, :4] x1 cx - w / 2 y1 cy - h / 2 x2 cx w / 2 y2 cy h / 2 boxes.append([x1, y1, x2 - x1, y2 - y1]) scores.append(float(final_score)) class_ids.append(class_id) indices cv2.dnn.NMSBoxes(boxes, scores, conf_thres, iou_thres) if len(indices) 0: indices indices.flatten() result [(boxes[i], scores[i], class_ids[i]) for i in indices] return result return []至此一条完整的“图像输入 - 预处理 - 昇腾推理 - 后处理”链路已经通了。跑一张640x640的图单次推理耗时不含后处理在Atlas 300V上大约是20到30毫秒也就是每秒30到50帧的吞吐能力这个性能跑实时视频流完全够用。5. 性能调优与多路视频流场景下的配置思路单张图片跑通只是“能用了”在真实项目中你一定是拿它跑视频流、跑摄像头、跑一批图片。在这个环节你要面对的核心问题不是模型推得够不够快而是“整条数据处理管道”能不能跟得上。5.1 从30ms到10msDVPP硬解码与AIPP预处理下放以24路1080p视频流实时分析为例如果每一帧都用CPU软解做JPEG解码或H.264解码CPU会先被拖垮推理卡反而闲着。Atlas 300V自带硬件解码能力这正好是它的看家本领。我的优化思路是把视频解码缩放格式转换全部下沉到DVPPCPU只负责读流和最后的业务逻辑。具体操作上我用到了昇腾的acllite样例库在昇腾社区代码仓里可以找到里面已经封装了DvppVideoDecoder等类可以方便地读取视频文件或RTSP流输出YUV格式的帧再通过VPCVideo Processing Codec模块做缩放输出模型需要的RGB或BGR数据。这个过程如果全部在Python层完成解码一帧1080p视频并缩放大约只需要2-4毫秒比CPU软解整整快了一个数量级。这里有一个大家容易忽略的点DVPP的缩放使用的是特定的宽高对齐规则一般要求宽度和高度都要对齐到16的整数倍否则会报错。如果模型输入是640x640满足对齐条件直接缩放没问题如果你的输入是640x384这种不规则的需要对缩放到16的倍数后做crop或者pad。5.2 多Stream并发比“加大batch”更实用的性能提升方式我在做性能压测的时候试过直接转一个batch4的OM模型来提高吞吐结果发现推理耗时虽然比batch1的4倍略低但没有想象中那么理想。原因在哪里因为batched推理在昇腾上并不是无脑加快AI Core的利用率只有在batch足够大时才能体现优势而batch4时数据从内存搬运到AI Core的时间可能成了新瓶颈。相比之下创建多个推理Stream每个Stream占用不同的计算资源能更灵活地分配负载。在我的场景中我用线程池开了4个线程每个线程都加载同一个OM模型模型在设备上只有一份ACL支持多线程安全调用并行执行推理。实测下来4个Stream并发跑batch1整体吞吐从30fps提升到80fps以上效果显著。具体实现思路如下import threading from concurrent.futures import ThreadPoolExecutor def infer_worker(image_list): # 每个worker独立调用同一个model实例 results [] for img in image_list: data preprocess(img) out model.infer(data) result postprocess(out) results.append(result) return results with ThreadPoolExecutor(max_workers4) as executor: futures [executor.submit(infer_worker, chunk) for chunk in chunks]需要注意使用多线程时必须确保ACL的上下文在线程内也被正确设置。我在最早版本代码里没有做这步结果偶发模型返回空输出。解决办法是在每个线程的函数开头加上# 线程内重新绑定上下文 context, ret acl.rt.create_context(0) acl.rt.set_context(context) # ... 推理 ... acl.rt.destroy_context(context)这样各线程的上下文互不干扰运行就稳定了。5.3 推理卡的内存占用观察与调优前面表格里我们看到Atlas 300V 24G的总内存是24576MB实际可用大概24GB。跑YOLOv5s batch1时模型本身占用约1.1GB再加上DVPP缓存、输入输出队列、Python运行时开销总共占用不到4GB。这意味着24G的内存容量下跑多个模型并行是绰绰有余的。但如果你跑的是YOLOv5x约7GB模型权重再叠加很大的batch就必须观察内存占用。用npu-smi info监控如果内存占用接近100%就可能出现模型加载失败或推理变慢的情况。这种情况下建议把不必要的DVPP缓存及时释放。还有一个很多人不知道的小技巧Atlas 300V的功耗和温度是可控的默认20度到80度之间都算正常。如果卡温持续高于85度大概率是服务器风道设计不合理此时性能会自动降频。不要只盯着算法优化散热问题也要留意。6. 部署过程中最常见的5个报错与排查思路这一节我整理了自己以及身边同行在实际部署中遇到的典型问题每一条都是真金白银踩出来的经验。如果你在部署过程中遇到类似报错可以直接对着排查。6.1 npu-smi能识别卡但ATC转换时报“soc_version is invalid”这个我前面提过一嘴对应报错为EI0001: Value of soc_version is invalid。根本原因是你不确定SoC版本。网上很多教程默认是Ascend310但Atlas 300V是新款它对应的是310P3不是310。通过npu-smi info查看Chip字段如果显示的是310P3那就把--soc_version设成Ascend310P3。这里还有一个细节AI Core跑的是310P3但如果你购买的是Atlas 300V Pro型号芯片可能又是310P4务必以npu-smi输出的实际芯片为准。6.2 ATC转换时报“Unsupported op:Resize”这个报错一般出现在YOLOv7、YOLOv8或者较新版本YOLOv5导出的模型上ONNX里有Resize算子且mode是cubic双三次插值。ATC对cubic模式支持不友好。解决办法有两个方向导出ONNX时把torch.onnx.export的opset_version改为12旧版本opset里Resize的格式更简单。如果一定要保留高版本opset在ONNX模型里把Resize算子的coordinate_transformation_mode从asymmetric改成align_corners并确认mode改成linear。实测下来YOLOv5s的模型中Resize算子主要用于上采样层修改为linear模式不影响精度可以放心操作。6.3 推理结果全零或输出shape不对输出全零大概率是预处理和AIPP配置重复归一化导致的。比如你在Python代码里把图片除了255AIPP配置里又把var_reci_chn设成了0.0039那模型输入就变成0-1/255网络直接就废了。要么完全交由AIPP做归一化要么纯用Python做归一化不要两边都做。如果输出shape不对则可能是ATC转换时指定的--output_type不对。YOLOv5的ONNX输出本来就是FP32你指定成FP16后Python端需要做一次精度转换这一步很多人会忘记。我的做法是保持FP32输出省心。6.4 推理延迟偏高但npu-smi显示AI Core利用率不足50%遇到这个问题先检查数据管道是不是出现了瓶颈。常见原因是视频解码在CPU上做、图像resize用Python循环做推理卡大部分时间在等数据。解决方向是把解码和缩放下沉到DVPP或者至少用OpenCV的并行resize代替循环操作。另外检查一下每次推理调用之间的间隔是不是远大于推理本身耗时。如果是那就是你的业务逻辑里有阻塞调用比如打印日志、上报结果太慢这些和加速卡无关优化业务代码比调模型更有效。6.5 长时间运行后内存持续增长并最终导致卡死这个问题我在最开始用Python脚本连续跑12小时后碰到过。后来排查发现是两个原因叠加一是每次推理创建的临时Tensor没有释放Python的GC没有及时回收二是ACL显式分配的设备内存没有在每次推理后释放。解决办法# 在每个推理结束后尝试手动释放中间buffer # 或者直接复用同一块设备内存而不是反复malloc在我的代码里我预先分配好输入输出内存每次推理只做memcpy和execute不重新malloc这样内存就稳定住了。这条经验对于长期运行的线上服务尤其重要。7. 写在最后Atlas部署YOLO的几点个人体会折腾这一周下来我对Atlas 300V 24G这张卡的定位有了更清晰的认识。它确实是一块不折不扣的运算加速卡而且是一块特别适合视频推理场景的卡。说几点我个人的实践心得希望对后来者有用。首先不要用GPU的使用习惯去套昇腾。GPU部署YOLO的最大舒适区在于PyTorch生态无缝衔接而昇腾的生态虽然也在快速补齐但现阶段要求你必须花时间理解模型转换、AIPP配置、DVPP流水线这些概念。一旦跨过这个门槛你会发现它的推理性能和稳定性其实是非常能打的特别是在多路视频分析这种对整条管道延迟敏感的场景下DVPP接力AI Core的设计思路比“使劲堆GPU算力”要高效得多。其次版本匹配是生死线。驱动、固件、CANN三个版本必须严格匹配安装之前一定要先查兼容矩阵。我在这上面浪费的时间比实际部署还多这也是为什么我在前文花了那么大的篇幅去强调版本组合。建议直接保存一套自己验证通过的组合作为团队内部的标准镜像。最后如果你只是想快速验证一张Atlas 300V能不能跑YOLO完全不需要像我一样从代码层面死磕ACL接口。可以直接使用昇腾社区的ais_bench工具命令行就能完成模型推理python3 ais_bench.py --model yolov5s_bs1.om --input ./test.jpg --output ./result它会自己处理预处理、推理、输出保存的整套流程先跑通它再深入研究内部细节这条路径对新手来说会友好很多。我第一次跑通时心里想的是其实也没想象中那么难但少了前面那些坑真的很难体会到这句话的含金量。
返回列表