ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

昇腾Atlas 300V部署YOLO实战:从模型转换到推理优化

昇腾Atlas 300V部署YOLO实战:从模型转换到推理优化 去年快年底的时候一个朋友找我帮忙看一张卡。他手里拿着一张半高半长的PCIe加速卡问我这玩意儿是不是跟游戏显卡一样插上就能跑YOLO能不能直接当显卡用我一看板卡上印着“Atlas 300V 24G”我说这卡不是干那个的这是华为昇腾的AI推理加速卡专门给服务器做深度学习推理用的不是图形卡。其实最近问Atlas的人不少尤其是做边缘计算、智慧园区、工业质检、安防监控这块的。大家最关心的几个问题出奇一致Atlas 300V 24G是不是运算加速卡是而且是纯推理加速卡、怎么用它部署YOLO需要经过模型转换不是直接跑PyTorch、性能和成本到底划不划算。这篇文章我就围绕这三个问题展开把我自己实际部署YOLOv5/YOLOv8的经验、踩过的坑、调优的思路都写出来。如果你是做AI应用落地的工程师、算法工程师或者正在给项目选型推理硬件这篇文章应该能帮你省不少时间。我不会只贴命令我会把“为什么这么做”讲清楚这样你换一张卡、换个模型也能举一反三。1. 整体设计思路与硬件选型分析1.1 Atlas 300V 24G的准确定位它不是显卡是推理卡先说结论Atlas 300V 24G是一张AI推理加速卡基于昇腾310P芯片24GB显存实际上是LPDDR4X支持FP16和INT8推理。它和游戏显卡、专业图形卡最大的区别是它没有图形输出接口不能接显示器也不能做3D渲染它的全部设计目标就是“跑神经网络推理”。很多人一听到“加速卡”三个字第一反应是“那我训练模型是不是也能用”。严格来说Atlas 300V这块卡不适合做训练它没有训练所需的高精度算力和完整的训练栈支持。昇腾的训练卡是Atlas 800系列训练服务器或者Atlas 900集群300V 24G的定位非常清晰把已经训练好的模型如YOLOv5、YOLOv8、ResNet、BERT等高效地跑起来在尽可能低的功耗和成本下获得最高的推理吞吐量。我自己选这张卡的核心原因有三个。第一是功耗单卡典型功耗72W左右不需要外接供电部分服务器机型需要CPU供电线辅助但不需要8pin显卡供电对现有服务器改造压力小。第二是价格相比同样24GB显存的NVIDIA T4或L4Atlas 300V整卡成本要低不少对成本敏感的B端项目非常友好。第三是国产化需求很多政企项目、信创项目明确要求使用国产芯片Atlas系列是最成熟的选项之一。但还有一个关键点必须说清楚这张卡的生态和CUDA不一样不是“pip install torch”就能直接用的。你需要用昇腾的工具链将模型转换、编译成OM格式后才能运行。这个转换过程是很多人的第一道坎后面我会详细讲怎么趟过去。1.2 用Atlas部署YOLO的整体架构与流程部署YOLO到Atlas 300V上整体流程可以拆成三条线模型线、运行线、数据线。模型线的核心是“从训练框架到昇腾格式的转换”。PyTorch训练的.pt权重文件需要先导出为ONNX再通过昇腾的ATCAscend Tensor Compiler工具转换成OM模型。这个过程不是傻瓜式的输入尺寸、批量大小、算子的支持情况都会影响转换是否成功。YOLOv5和YOLOv8的Detect头里有不少动态shape和自定义算子转换时经常报错需要针对性地处理。运行线是推理运行时的环境。昇腾提供了CANN昇腾异构计算架构作为底层运行时再往上可以用MindSpore Lite、OpenCV等配合或者直接用ACLAscendCLAPI编写推理代码。对大多数场景来说直接用MindSpore Lite或者ACL的Python接口就够了不需要碰C当然C性能和灵活性更好。我建议初学者先走通Python接口再考虑C优化。数据线是容易被忽略的一环。Atlas 300V板载的DVPP模块负责图像缩放、格式转换、抠图等预处理但DVPP有对齐要求比如宽度16对齐YOLO的输入尺寸如果没对齐DVPP输出会黑边或裁切这会影响精度。很多人在模型转换时报错或者在推理结果中对不上坐标问题都出在数据流上。2. 环境搭建与核心步骤拆解2.1 驱动与CANN工具包安装脱离文档踩坑的记录不管做什么第一步都是装环境。Atlas 300V的软件栈分为三层驱动Driver、固件Firmware、CANN工具包。前两者是硬件能正常工作基础CANN是AI推理的计算框架。先说驱动。驱动和固件的安装包在昇腾社区可以下载对应你的操作系统版本。我用的环境是Ubuntu 20.04 x86_64部分客户是麒麟V10 ARM平台流程类似但包名不同。主要命令就两个./Ascend-hdk-310P-npu-driver_xxx_linux-aarch64.run --full ./Ascend-hdk-310P-npu-firmware_xxx_linux-aarch64.run --full注意这里有个最常见的坑很多服务器已经装了NVIDIA驱动如果系统里同时有NVIDIA GPU和Atlas卡两者的驱动不冲突因为走的PCIe设备不同。但如果你用npu-smi info查看Atlas状态时发现“NA”大概率是驱动没加载或固件版本不对。需要重启或者重新执行rmmod相关模块。驱动安装完用下面命令验证npu-smi info如果能看到板卡型号、显存、芯片温度、算力利用率这些信息说明驱动和固件没问题了。然后是CANN工具包。CANN的版本很多我强烈建议选一个“保守”的版本。不要追求最新版昇腾的软件迭代比较快新的版本对操作系统、Python版本、第三方库有更多要求老项目反而容易出问题。我用的是CANN 6.3.RC3支持Python 3.9经过实际验证稳定性很好。安装CANN时它会检查依赖比如gcc、g、cmake、python3-dev、pybind11等缺什么补什么即可。CANN的安装包是一个run文件执行./Ascend-cann-toolkit_6.3.RC3_linux-x86_64.run --install安装完成后需要source环境变量source /usr/local/Ascend/ascend-toolkit/set_env.sh这里有一个很多教程不会提但实际会遇到的问题CANN对Python的版本非常敏感。如果系统自带Python 3.8而CANN版本要求Python 3.9你需要先装好对应Python再装CANN。装了多个Python版本时一定要确保python命令指向正确的版本否则后续atc转换和推理都会报“找不到模块”的错误。2.2 模型获取与PyTorch环境下ONNX导出YOLO的模型可以从官方仓库获取。YOLOv5和YOLOv8分别来自ultralytics/yolov5和ultralytics/ultralytics这两个仓库。本地准备一个带PyTorch和CUDA的环境训练或导出模型用不需要Atlas参与执行导出# YOLOv5 导出ONNX python export.py --weights yolov5s.pt --include onnx --opset 11 # YOLOv8 导出ONNX yolo export modelyolov8s.pt formatonnx opset11opset版本我建议固定用11。在Atlas ATC转换时有部分算子对opset 12以上的支持不太友好稳定压倒一切。导出时还有一个细节动态batch在导出前要小心。如果ONNX里带了dynamic_axesATC转换时也要配置动态batch否则转换出来的OM只能以特定batch运行。大部分推理场景固定batch1足够了所以我建议导出ONNX时直接固定输入shape# YOLOv5 固定shape导出 python export.py --weights yolov5s.pt --include onnx --opset 11 --batch-size 1 --imgsz 640导出完成后用onnxsim做一次简化可以大幅度提高ATC转换成功率。YOLO的模型结构里有大量Concat、Resize、Split等算子ONNX图结构冗余度高simplifier会把常量折叠掉、去掉无用节点ATC转换更顺滑pip install onnxsim python -m onnxsim yolov5s.onnx yolov5s_sim.onnx就用简化后的ONNX文件去转OM很多莫名其妙的算子错误都能消掉。2.3 ATC模型转换一个完整的转换命令模板ATC转换是部署YOLO最核心的一步也是最容易卡壳的一步。我先给出我验证可用的模板再解释每个参数为什么这么设置。atc --modelyolov5s_sim.onnx \ --framework5 \ --outputyolov5s_bs1 \ --input_shapeimages:1,3,640,640 \ --soc_versionAscend310P3 \ --insert_op_confaipp_yolov5.cfg \ --output_typeFP32 \ --enable_small_channel1 \ --logerror逐行解释--framework55代表ONNX。如果你用MindSpore导出的模型这个值不同。ONNX是目前对ATC支持最友好的中间格式。--input_shape固定输入shape。YOLOv5的ONNX输入节点名默认是“images”YOLOv8默认也是“images”但如果你改过模型结构需要先用netron或者onnxruntime查看输入节点名。--soc_version芯片型号。Atlas 300V用的芯片是昇腾310P不同子型号对应不同值常见的是Ascend310P3和Ascend310P1用npu-smi info能查到具体信息。如果没对应上ATC会报“SOC版本不支持”的错误。--insert_op_confAIPP配置文件。这个文件用于图像预处理参数配置比如均值、方差、缩放比例、色域转换等非常关键后面单独讲。--output_typeFP32输出精度。有时不设置会默认输出FP16导致后处理反序列化时数据不对。--enable_small_channel开启小通道优化。对YOLO这种典型卷积网络能提升一点推理性能。--logerror日志级别。报错时改成--logdebug可以看到更多信息。一个需要特别注意的细节是输出节点。YOLOv5的ONNX默认有3个输出对应3个检测尺度的输出YOLOv8默认只有1个输出将所有尺度的输出concat在一起。如果你的模型有多个输出后面推理代码要逐个解析。我建议如果后端代码是自己写的用YOLOv8这种单输出的结构会更省事。2.4 AIPP配置文件决定预处理是否能对齐的关键AIPPAI Preprocessing是昇腾专门用来把图像预处理放到硬件里做的模块。它可以在送入模型前完成Resize、色域转换如BGR转RGB、减均值、除方差等操作。它最大的意义是省掉了CPU或DVPP的预处理时间推理链路更短吞吐更高。我常用的AIPP配置如下aipp_op { aipp_mode: static input_format: RGB888_U8 src_image_size_w: 640 src_image_size_h: 640 csc_switch: true rbuv_swap_switch: true csc_matrix_r2c: [256, 0, 359, 0] csc_matrix_g2c: [256, -88, -183, 0] csc_matrix_b2c: [256, 455, 0, 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 }这段配置的核心逻辑是模型输入是RGB三通道图像像素值被归一化到[0,1]即除以255。RBUV_SSWAP_SWITCH控制输入图像先经过BGR转RGB如果你的图像源是BGR格式比如OpenCV读出来的就是BGR需要置为true。var_reci是归一化系数的倒数。这里有个非常容易踩的坑如果你在训练时用了transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225])那么在AIPP里不能只配var_reci还需要把减均值的逻辑配进去即min_chn_0填负的均值并注意处理方式。YOLOv5和YOLOv8的官方仓库在推理时是不减均值的只除以255所以上面的配置是匹配的。如果你改了预处理策略AIPP配置必须跟着改否则精度会严重下降。另外AIPP里配了src_image_size_w/h后输入图像的尺寸必须是这个大小。如果你的图像源是1920x1080那么要么用DVPP先缩放要么用--input_shape把模型输入shape改成对齐后的尺寸要么让AIPP自己把输入图像缩放注意它只做等比例resize不做letterbox。YOLO训练时经常用到letterbox带灰边的等比缩放这部分逻辑AIPP做不了你需要在推理代码里先把图处理好或者接受不是等比缩放带来的精度损失。3. 推理代码实现与运行效果3.1 Python推理接口选择MindSpore Lite vs AscendCL昇腾推理的Python接口主要有两种MindSpore Lite的Python API和AscendCLACL的Python API。我两个都试过简单说下选型思路。MindSpore Lite API包装得更高级接口风格类似TensorFlow Lite的Python接口加载模型、推理、释放资源分层清晰适合快速验证。AscendCL更底层但接口也不复杂关键是它的文档最全遇到问题好排查。底层上MindSpore Lite最终也是调用ACL的所以性能差别不大。我推荐团队协作或相关项目用MindSpore Lite因为它内置了一些数据处理工具的接口能少写很多代码。个人开发者或追求极致性能调优的直接上ACL。不管选哪个都要记得在代码开头加载昇腾的环境变量import os os.environ[ASCEND_AICPU_PATH] /usr/local/Ascend/ascend-toolkit/latest os.environ[LD_LIBRARY_PATH] /usr/local/Ascend/ascend-toolkit/latest/lib64: os.environ.get(LD_LIBRARY_PATH, )这个不写经常会出现“找不到libascendcl.so”之类的错误。3.2 以AscendCL为例的推理代码框架下面这段代码是我在项目中实际用过的去掉了业务逻辑保留关键框架。import numpy as np import cv2 from constants import IMG_SIZE, CLASS_NAMES, CONF_THRESH, NMS_THRESH import acl import torch # 仅用于后处理的NMS也可以用NumPy实现 # 初始化 ret acl.init() ret acl.rt.set_device(0) context, ret acl.rt.create_context(0) # 加载模型 model_path ./yolov8s_bs1.om model_id, ret acl.mdl.load_from_file(model_path) desc acl.mdl.create_desc() ret acl.mdl.get_desc(desc, model_id) # 获取输入输出尺寸 input_size acl.mdl.get_num_inputs(desc) output_size acl.mdl.get_num_outputs(desc) input_dims [acl.mdl.get_input_dims(desc, i) for i in range(input_size)] output_dims [acl.mdl.get_output_dims(desc, i) for i in range(output_size)] # 准备输入输出内存 input_data np.zeros((1, 3, IMG_SIZE, IMG_SIZE), dtypenp.float32) output_data np.zeros((1, 84, 8400), dtypenp.float32) # 以coco 80类为例 # 图像预处理 def preprocess(image): # 原图等比例缩放加letterbox h, w image.shape[:2] r min(IMG_SIZE / h, IMG_SIZE / w) new_h, new_w int(h * r), int(w * r) resized cv2.resize(image, (new_w, new_h)) canvas np.full((IMG_SIZE, IMG_SIZE, 3), 114, dtypenp.uint8) x_off (IMG_SIZE - new_w) // 2 y_off (IMG_SIZE - new_h) // 2 canvas[y_off:y_offnew_h, x_off:x_offnew_w] resized # BGR转RGB归一化转CHW tensor cv2.cvtColor(canvas, cv2.COLOR_BGR2RGB).astype(np.float32) / 255.0 tensor np.transpose(tensor, (2, 0, 1))[None, ...] return tensor, x_off, y_off, r # 推理 def infer(tensor): input_buffer acl.util.numpy_to_ptr(tensor) output_buffer acl.util.numpy_to_ptr(output_data) ret acl.mdl.execute(model_id, [input_buffer], [output_buffer]) return output_data.copy() # 后处理以YOLOv8为例解码为xyxy格式 def postprocess(pred, x_off, y_off, r): # pred shape: [1, 84, 8400] - [8400, 84] pred pred[0].T boxes pred[:, :4] scores pred[:, 4:] class_ids np.argmax(scores, axis1) confs np.max(scores, axis1) mask confs CONF_THRESH boxes, class_ids, confs boxes[mask], class_ids[mask], confs[mask] # 把坐标从模型输入空间还原回原图空间 boxes[:, [0, 2]] (boxes[:, [0, 2]] - x_off) / r boxes[:, [1, 3]] (boxes[:, [1, 3]] - y_off) / r # NMSOpencv或自定义 ... return final_boxes关于输出的尺寸8480个类别加4个坐标维度YOLOv8的x,y,w,h或x1,y1,x2,y2具体看版本乘以特征点数量8400三个尺度的特征图拼起来。如果你用YOLOv5输出是[1, 3, 84, 8400]还多了一个维度对应不同的anchor解析逻辑更复杂。3.3 实测性能单路视频实时性与多路并发能力我在实际项目中用Atlas 300V 24G跑YOLOv8s输入640x640FP16推理测试结果如下配置单帧耗时ms备注YOLOv8s 640x640 FP16约11~14ms单路超过60FPSYOLOv5s 640x640 FP16约7~9ms单路超过100FPSYOLOv8s 640x640 INT8量化后约5~7ms精度通常下降1%~3%8路1080P视频流每路YOLOv8s总计可跑满约70~90FPS用多线程模型多实例这个性能在24GB显存、72W功耗的推理卡里算很不错了尤其适合“一台服务器同时处理十几路视频流做结构化分析”这种场景。如果对速度要求极高可以考虑把模型输入降到416x416时间能再压掉40%左右当然小目标检出率会受影响。有一说一Atlas的推理性能跟NVIDIA同档位卡相比有一定差距但在国产化要求、功耗限制、成本敏感这三座大山面前这个性能表现已经相当能打了。实际项目中很多客户核心诉求就是“在指定预算内把模型跑起来”而不是“一定比NVIDIA快”。4. 部署过程中高频问题与避坑记录4.1 ATC转换报错算子不支持或图优化失败这是新手遇到最多的一类问题。常见报错有E19999: The node [Resize] is not supportedONNX中的Resize算子在ATC支持的版本里没有对应实现。解决方法是升级CANN版本或者修改ONNX图结构把动态Resize替换成静态shape的Resize或者换opset版本重新导出。E10001: No OpType found in ops info说明算子未注册一般是ONNX版本太新比如opset 17 当前CANN算子库太旧。我建议opset 11或12配合旧一点但稳定的CANN版本能避开大多数算子兼容问题。有一个绕不开的经验遇到算子不支持的报错先查算子本身有没有问题再查CANN版本是否太旧/太新不要一上来就想改模型结构。YOLO这种广泛部署的模型昇腾官方其实是有很多已验证案例的很多坑网上都能搜到搜不到的基本就是版本组合太激进。4.2 推理结果精度下降或完全检测不到目标这个问题的排查顺序非常重要我经历过太多项目在这里走弯路所以列个清单AIPP配置和训练时预处理不一致这是最容易被忽略的。YOLO训练时一般不做减均值只做/255归一化但很多人看网上教程抄了ImageNet的mean/std配置结果模型直接就废了。输入图片尺寸是否被resize成640x640但没有等比缩放。如果直接把1920x1080压成640x640目标比例早就变形了检测精度会崩。推理输出解析是否正确。YOLOv5的输出是xywh格式YOLOv8是xyxy格式且默认带DFL解码如果你用YOLOv5的解析逻辑去解YOLOv8坐标完全是乱的。模型量化后精度下降太多。如果INT8量化校准集代表性不够比如只用了几十张图量化误差会很大。建议校准集至少用500~1000张覆盖目标场景的图片。4.3 DVPP对齐限制导致图片出现黑边或裁切DVPP在做图像缩放时对宽高有对齐要求通常是16对齐有的版本是32对齐。如果你直接把图片resize成640x640给DVPP如果原始尺寸不是16的倍数输出图像边上可能出现脏数据或黑边。解决办法有两种一是绕过DVPP用OpenCV的CPU reszie二是用DVPP时把原始图像先pad到对齐尺寸再做缩放。第一种简单但多占CPU第二种高效但代码复杂一点。我实际项目中CPU资源充裕直接用OpenCV处理省心很多。4.4 多路并发推理时的显存和线程问题Atlas 300V的24GB显存听起来很大但如果你用多个Python进程分别加载同一个OM模型显存占用会成倍增加。我的建议是在一个进程里创建多个推理线程共享同一个模型上下文。昇腾的acl.mdl.execute本身是线程安全的多线程并发执行推理不会冲突这样可以最大程度复用模型加载占用的显存。真实场景里我一般开4~6个推理线程每个线程处理2~4路视频流显存占用基本稳定在8~12GB之间留下充足余量给后续的业务处理。4.5 常见问题速查表现象可能原因解决方案npu-smi info显示NA驱动未加载或固件版本错误重新安装驱动固件并重启检查PCIe设备识别状态ATC转换时报E19999算子不支持ONNX版本/opset过新CANN版本过旧降低opset到11使用稳定版CANN必要时修改ONNX结构加载OM模型报内存不足未设置AscendCL环境或模型输入shape过大检查环境变量减小输入shape或换成batch1模型推理结果全是背景框AIPP预处理与训练不一致核对均值方差、BGR/RGB、缩放方式多线程推理偶发卡死线程间共享了同一个输出Buffer每个线程分配独立输入输出Buffer单卡吞吐上不去未开enable_small_channel或未使用多线程开启AI CPU优化开关用多线程并发推理5. 部署之外的进阶优化方向5.1 模型量化INT8推理到底值不值得做Atlas 300V对INT8的支持是原生且高效的理论上INT8吞吐量是FP16的两到三倍。但INT8量化后的精度损失因模型、数据集、量化方案而异。我的实践结论是YOLOv5s做INT8量化在标准COCO验证集上mAP下降一般不超过2%在工业质检这种类别少、背景相对单一的场景下降更少。YOLOv8做INT8量化会复杂一些因为模型结构里的某些算子对量化敏感比如SiLU激活函数容易掉精度。如果项目对精度要求不是“99.9%不能错”我建议直接上INT8因为推理速度提升实在太明显了。量化工具用昇腾的AMCTAscend Model Compression Toolkit流程是准备校准集几百张真实场景图→ 调用量化接口 → 得到量化模型 → ATC重新转换。校准集一定要贴合线上真实数据分布否则量化后精度会崩。5.2 让Atlas同时处理多路视频流的架构方法多路视频流处理时算法代码反而不是瓶颈真正的瓶颈在于解码和循环处理。我用的是FFmpeg Python线程的经典组合架构可复用通用解码线程用FFmpeg从RTSP流解码出1080P图像帧放入队列。推理线程池从队列取帧执行预处理如果要跑AIPP就在DVPP里做否则用OpenCV、推理、后处理。结果汇聚模块将检测结果合成为结构化JSON写入消息队列或数据库。这个架构下8路1080P视频流跑YOLOv8sCPU占用率大概70%卡上算力利用率稳定在80%以上整体延迟约80ms。注意如果你的视频流是H.265编码要确认FFmpeg编译了H.265解码库否则解码会非常吃力。我的个人体会是Atlas 300V 24G不是那种插上就能用的卡它需要你花一两天去熟悉工具链。但摸清楚它的脾气之后它确实是一张稳定、便宜、功耗低、适合量产部署的AI推理卡。现在昇腾生态也在快速补齐MindX SDK、ModelZoo的预置模型越来越多后面做项目和方案选型时可以多给它一个机会。如果这篇文章让你成功跑通了YOLO在Atlas上的部署记住我最后这个建议转换模型永远用简化后的ONNX调精度永远先查预处理。稳了这两点你的Atlas之路会顺很多。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表