尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Atlas 300V 24G 跑通 YOLO:从驱动到推理部署的全流程实践

Atlas 300V 24G 跑通 YOLO:从驱动到推理部署的全流程实践 最近群里好几拨人都在问同一件事Atlas 300V 24G 这块卡到底能不能跑 YOLO它到底算不算运算加速卡怎么把常见的 YOLOv5/YOLOv8 模型弄上去跑起来我趁着项目间隙把手头的 Atlas 300V 24G 重新搭了一遍环境完整跑通了 YOLOv5 的检测流程也踩了不少坑。这篇东西不是照着官方文档念而是我从装驱动、转模型、写推理代码到调性能的真实操作记录。手里正好有这块卡、或者刚准备接触昇腾推理生态的兄弟可以照着走一遍能省不少折腾时间。先把最关键的问题放前面回答Atlas 300V 24G 是运算加速卡但它不是我们熟悉的通用 GPU而是一块面向 AI 推理场景的 NPU 加速卡。它专门针对深度学习推理做了大量优化跑 YOLO 这类目标检测模型非常对路。但要注意它和训练卡不是一回事如果你指望在上面跑 PyTorch 训练脚本那大概率会碰一鼻子灰。整个部署链路和 CUDA 生态差别挺大模型要转成 OM 离线格式推理接口要用 AscendCL 或者 MindSpore Lite输入预处理还得考虑 DVPP 的对齐规则。把这些门道摸清楚之后整个流程其实很顺性能也够用。1. Atlas 300V 24G 到底是一块什么性质的卡1.1 先正面回答它是运算加速卡吗是。Atlas 300V 24G 的核心是一颗昇腾 NPU这颗 NPU 里有专门的 AI Core 计算单元用来做矩阵运算、卷积、激活函数这类深度学习里面的高频操作。它还集成了较大容量的片上存储24G 指的是显存容量能放下不少大模型。从硬件定位上说它就是一块标准的 AI 推理加速卡不是 GPU更不能用来做图形渲染。你把它插在服务器上用它跑 YOLO 推理、跑 ResNet 分类、跑 OCR 检测这些都是完全没问题的。但这里有个容易误导人的地方。很多人一听加速卡下意识觉得它什么都能算其实推理卡和训练卡的分工差别很大。Atlas 300V 24G 的设计目标就是把训练好的模型高效跑起来它的算力配置、存储带宽、指令集都偏向推理场景。你想在上面做训练比如用 MindSpore 或者 PyTorch 跑反向传播虽然技术上不是完全不能尝试但生态支持和性能都远不如专门的训练卡或者 GPU 集群。所以我的建议是训练继续用你熟悉的 GPU 或者云上训练集群训练完把权重拿下来转到这块卡上做推理部署这才是它最舒服的姿势。1.2 推理卡和普通 GPU 的差别在哪我用一个比较通俗的比喻来解释GPU 像是一个什么菜都能做的全能厨房训练和推理都能干但人多的时候出餐效率不一定最优NPU 推理卡更像是一条为目标菜系专门优化的中央厨房流水线菜单固定流程固定单份出餐速度极快但你想临时换一套做法就比较麻烦。具体到使用体验上有几点是刚从 CUDA 生态切过来的人必须接受的模型格式不一样。GPU 上可以直接用 PyTorch 的 .pt 权重跑推理但 NPU 上通常要把模型转换成 CANN 的 OMOffline Model格式转换工具是 ATCAscend Tensor Compiler。推理接口不一样。GPU 上你可以用 TensorRT、ONNX Runtime、PyTorch 随意折腾NPU 上主流是 AscendCL简称 ACL或者 MindSpore Lite代码写法和 CUDA 风格完全不同。算子支持范围有限。不是所有 PyTorch 算子都能在 NPU 上原生执行模型转换时经常遇到某个算子不支持的报错需要改模型结构或者用 CPU 算子兜底。预处理有额外约束。NPU 的图像预处理单元 DVPP 对图片宽高有对齐要求不是随便什么尺寸都能直接扔进去。这些限制听起来麻烦但其实只要走通一次整个链路后面换模型、换项目都是在同一套模板上做微调不会比 CUDA 生态复杂太多。2. 动手前的软硬件准备2.1 硬件环境怎么确认先说硬件。我手头这台服务器是 x86 架构的插了一块 Atlas 300V 24G。拿到卡之后第一步不是急着装驱动而是先确认硬件有没有被系统识别。你可以在服务器上执行 lspci 命令看看有没有出现昇腾相关的设备记录。如果系统里什么提示都没有先检查卡有没有插紧、供电线有没有接好、服务器 BIOS 里有没有禁用 PCIe 设备这些低级问题反而最常见。确认硬件被识别之后就要装 NPU 的底层驱动也就是常说的 HDK 里的驱动包。装完之后可以用一个很关键的命令来验证npu-smi info这个命令类似 GPU 生态里的 nvidia-smi能看到卡的温度、功率、显存占用、当前算力利用率。如果命令正常输出一张表里面有 24G 的显存信息说明驱动已经生效硬件层面的准备工作就算完成了。我在第一次装的时候遇到过 npu-smi 命令找不到的情况多半是环境变量没配把 CANN 安装路径下的 bin 目录加进 PATH 就能解决。2.2 软件栈怎么选Atlas 300V 24G 的软件栈核心是 CANNCompute Architecture for Neural Networks。CANN 里包含了 ATC 转换工具、AscendCL 推理接口、DVPP 图像处理库、算子库等等相当于 CUDA cuDNN TensorRT 的整合体。CANN 版本和驱动版本是配套的装之前一定要先查清楚版本兼容矩阵我见过太多人因为驱动和 CANN 版本不匹配浪费一整天在环境变量和报错里打转。在推理框架层面目前主流有三种选择纯 AscendCL最底层灵活度最高适合需要精细控制推理流程的人。MindSpore Lite封装度更高自带一些模型转换和部署工具适合不想写太多底层代码的人。PyTorch 的 Ascend 适配层如果你非要直接用 PyTorch 的 API 跑推理可以走这条路但性能和稳定性未必比前两者好。我个人的习惯是直接用 AscendCL原因很简单出了问题你能准确地知道是哪一步报错对着错误码去排查效率最高。MindSpore Lite 虽然封装方便但一旦遇到不支持的算子排查起来反而更头疼。3. YOLO 模型转换从 PyTorch 权重到 OM 离线模型3.1 导出 ONNX 时容易踩的坑Atlas 300V 24G 不能直接加载 PyTorch 的 .pt 文件标准流程是先把 PyTorch 模型导出成 ONNX再用 ATC 工具把 ONNX 转换成 OM。第一步的 ONNX 导出看着简单实际上80%的坑都埋在这。我用 YOLOv5s 举例。导出的核心命令大概是这样的python export.py --weights yolov5s.pt --include onnx --img-size 640 640导出之后建议用 Netron 打开 ONNX 模型看一眼结构。YOLOv5 默认导出的模型输出层是三个不同尺度的特征图分别是 80x80、40x40、20x20每个尺度对应三个 anchor总共 8400 个候选框。如果你要在 ATC 转换后再做后处理建议在导出时把三个输出拼接成一个输出维度变成 (1, 8400, 85) 这样后处理写起来会省很多事。拼接操作可以放在导出脚本里也可以导出后再用 onnx_graphsurgeon 或者 onnx 库处理。导出时还要注意动态维度问题。如果你希望模型能接收不同尺寸的输入ONNX 导出时要把输入维度设为动态的比如 (1, 3, -1, -1)。但这里有个现实问题NPU 上动态 shape 的推理性能通常不如静态 shape。所以我的建议是先确认你的真实业务场景需要哪些输入尺寸如果只需要 640x640 一种那就直接导出静态模型后面 ATC 转换时全走静态配置性能和稳定性都最好。3.2 ATC 转换参数和 AIPP 配置拿到 ONNX 之后用 ATC 工具转 OM。下面这个命令是我在 Atlas 300V 24G 上验证过可用的atc --modelyolov5s.onnx \ --framework5 \ --outputyolov5s_bs1 \ --input_shapeimages:1,3,640,640 \ --soc_versionAscend310P3 \ --insert_op_confaipp.cfg命令里的参数逐个说清楚model输入的 ONNX 文件路径。framework5 表示 ONNX。ATC 对不同框架有不同的编号ONNX 是 5。output输出的 OM 文件名。input_shape指定输入张量的 shape这里固定为 1 张 640x640 的 RGB 图片。soc_version这是最容易搞错的地方。一定要先用 npu-smi info 或者官方工具确认你的芯片具体型号再填对应的 SoC 版本。Ascend310P3 是 Atlas 300V 系列常见的版本号但你的卡如果是不同批次这个值可能不一样填错会直接转换失败。insert_op_conf插入 AIPP 配置文件。AIPP 是昇腾推理里一个很关键的特性它能把图片缩放、格式转换、减均值、除以标准差这些预处理操作固化到模型里在 NPU 上完成从而减少 CPU 的负担。AIPP 配置文件我常用的是这样的aipp_op { aipp_mode: static input_format: RGB888_U8 src_image_size_h: 640 src_image_size_w: 640 csc_switch: true rbuv_swap_switch: true 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 }这段配置的作用是输入是 640x640 的 RGB 图片在 NPU 上完成格式转换并且把每个通道的像素值乘以 1/255等于把归一化也做掉了。也就是说你在 CPU 端只需要把图片缩放成 640x640 并转成 RGB其他操作都交给 NPU。这个设计能显著降低 CPU 的开销对视频流场景特别友好。注意AIPP 的 mode 有两种static 和 dynamic。static 模式在模型转换时就把预处理参数固化下来性能最好但输入尺寸必须固定dynamic 模式允许运行时不经过重新转换就修改部分参数灵活但是性能略低。不是特殊情况建议用 static。3.3 转换失败的几个常见原因ATC 转换失败的报错信息往往让人一头雾水但大部分问题可以归为三类。第一类是算子不支持。YOLO 模型本身结构不复杂但如果你用了比较新的激活函数、注意力机制或者自定义算子ONNX 图里就会出现 ATC 不认的节点。解决办法是改模型结构把不支持的算子替换成等价的基础算子比如用 Conv Bias 激活函数的组合来替代一些自定义模块。第二类是 shape 不匹配。ATC 会严格按照你指定的 input_shape 来推导整个图某个中间节点的维度对不上就报错。这时候建议把报错信息里提到的节点名记下来回 ONNX 图里看那个节点前后连接的是什么通常能找到是哪里多了一个不必要的 reshape 或者 transpose。第三类是 AIPP 配置和模型不匹配。比如模型输入本来就是归一化后的数据你又开了 AIPP 的归一化开关结果就是推理结果完全不对。这里千万注意模型转换后你需要用同一张测试图在 CPU/GPU 上跑一遍原始权重和 NPU 上的输出对比确认数值是否正确。我习惯同时输出前几个 top 类别的概率数值误差在千分之一以内才算正常。4. 写推理代码基于 AscendCL 的最小实现4.1 初始化与模型加载模型转换完成后接下来的重头戏是写推理代码。我用 Python 调用 AscendCL 接口来做示例因为 Python 生态写起来快容易调试实际生产环境想追求极限性能再移植成 C 也不难。先看一个最简的初始化、加载模型、推理的骨架import acl def init(): ret acl.init() assert ret 0, acl.init failed ret acl.rt.set_device(0) assert ret 0, set_device failed def load_model(model_path): model_id acl.mdl.load_from_file(model_path) return model_id这段代码做了两件事初始化 ACL 运行时并指定使用第 0 块设备然后加载 OM 模型文件拿到一个 model_id。在整个推理程序退出前你需要调用 acl.finalize()并且在模型加载后设置一个全局的上下文context否则后面很多接口会因为没有默认上下文而报错。context 的概念类似 CUDA 里的 stream 和 context你可以理解成一个独立的工作空间所有任务都要在这个工作空间里排队执行。4.2 输入输出 Buffer 的分配与管理这是 AscendCL 编程里最核心、也最容易出错的地方。模型加载后你需要从模型描述中获取输入输出的信息包括每个 tensor 的维度、数据类型、大小。然后用 acl.rt.malloc 在设备侧分配内存再准备一个 host 侧的地址用来做数据搬运。# 获取模型描述信息 model_desc acl.mdl.create_desc() 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 acl.rt.malloc(input_size, 2) output_ptr acl.rt.malloc(output_size, 2)这里的第二参数 2 表示内存对齐要求通常按框架要求填 2 即可。你要注意输入数据必须是你模型要求的格式形状、数据类型、通道顺序全部要匹配。如果模型输入是 NCHW 的 RGB 图片你给它的数据就得是经过转换的数组不能直接把 HWC 的数据丢进去。数据从 CPU 传到 NPU 用 acl.rt.memcpy执行推理用 acl.mdl.executeacl.rt.memcpy(input_ptr, input_size, input_data_ptr, input_size, 2) ret acl.mdl.execute(model_id, [input_ptr], [output_ptr])这段逻辑里我建议把输入输出的分配放在模型加载后一次性完成不要在每次推理请求里重复 malloc 和 free否则性能会非常难看。在视频流或者高并发场景下更合理的做法是准备一个内存池或者至少复用已分配的 buffer。4.3 图像预处理与 DVPP 对齐问题之前提到 AIPP 能在 NPU 上完成归一化和格式转换但有一个前提图片本身要先被缩放到模型输入尺寸比如 640x640。这个缩放操作如果放在 CPU 上用 OpenCV 做也能跑但在高帧率场景下 CPU 会变成瓶颈。昇腾提供了专门的硬件图像处理单元 DVPP把缩放、裁剪、颜色空间转换这些操作也搬到 NPU 上执行。DVPP 本身有很强的对齐约束这是大家最容易忽略的点。以 VPCVideo Pre-Processing为例输入图片的宽高、输出图片的宽高在内部都要对齐到特定的步长比如宽度按 16 对齐、高度按 2 对齐不同版本要求还不一样。代码里如果直接按 640x640 传某些场景会报错或者输出异常。稳妥的办法是看官方接口文档里对 宽、高、对齐系数 的定义在调用前把输入输出的 buffer 大小按对齐后的尺寸计算。我在第一次用 DVPP 的时候就是没注意对齐处理 1920x1080 的图一直出绿边排了半天队才发现是输出 buffer 没按对齐尺寸分配。如果你的业务对实时性要求不是极端严格我建议先用 OpenCV 做缩放走 AIPP 归一化这条路把整个流程跑通之后再根据性能需求决定要不要把缩放也迁到 DVPP。一步到位当然好但调试难度会高很多特别是 DVPP 的报错信息很多时候不是特别明确新手很容易卡住。4.4 YOLO 后处理在 CPU 上做解码和 NMSYOLO 模型输出的数据格式大致是 (1, 8400, 85)其中 85 表示 4 个坐标信息center_x, center_y, width, height、1 个目标置信度、80 个类别分数。执行完 acl.mdl.execute 后输出数据在 output_ptr 里你需要把它从设备侧拷贝回 CPU然后做后处理。后处理的流程是从输出结果里解析出每个候选框的坐标和置信度。过滤掉置信度低于阈值比如 0.25的框。对剩余的框按类别做非极大值抑制NMS去掉重叠度过高的框。把坐标从 640x640 的模型坐标系映射回原始图像坐标系。用 Python 的 numpy 实现一个简单的 NMS 不难网上代码一搜一大把。但要注意在视频流场景下这步操作非常消耗 CPU。8400 个候选框在每帧都做一次 numpy 运算CPU 占用率会明显上升。一个常用的优化思路是先用置信度过滤大幅减少框的数量再进入 NMS另一个思路是使用 torchvision 的 nms 函数如果环境里恰好有 PyTorch 的 CPU 版本它的 NMS 实现会比手写 numpy 高效不少。5. 性能调优与问题排查5.1 影响推理延迟的几个关键因素模型转对了代码能跑通这只是第一步。真正到了上线阶段你肯定会关心延迟和吞吐。我在实际调参中感觉影响最大的因素是下面这几个。第一是 batch size。Atlas 300V 24G 这种推理卡最适合批量推理。如果业务是离线处理图片或者视频抽帧建议一次喂多张图比如 batch4 或者 batch8能显著提高吞吐。如果业务是实时单路视频分析那 batch1 的延迟就更重要。ATC 转换时就要把 input_shape 里的 batch 定好后面改 batch 得重新转模型所以一开始就要想清楚场景。第二是数据搬运。输入图片从 CPU 拷贝到 NPU 的过程是有时间开销的。如果每张图都是先拷贝再推理、推理完再拷贝结果那搬运时间会占掉很大比例。优化的思路是异步推拉在做当前帧推理的同时用另一块缓存区准备下一帧的输入让搬运和计算尽量重叠。第三是静态 AIPP 带来的好处。之前说过把归一化和格式转换塞进模型能省掉 CPU 侧的重复劳动。这个在性能测试里的差异非常明显尤其是跑视频流的时候CPU 占用率能下降十几个百分点。第四是后处理。前面提到 YOLO 的 NMS 在 CPU 上跑CPU 负载一高整个系统的帧率就会被拖下来。如果帧率达不到要求可以考虑把后处理逻辑改成 C 实现或者用多线程把检测和后处理分布在不同的核上。5.2 常见报错速查我把自己遇到过的问题整理成一张表方便大家直接对照排查。现象可能原因解决思路npu-smi info 执行失败驱动安装不完整或环境变量未配置检查驱动 .run 包安装日志确认 /usr/local/Ascend 下的路径已加入 PATHATC 转换时 soc_version 不识别芯片型号和填写的参数不匹配用 npu-smi info 确认芯片型号查询当前 CANN 版本支持的 soc_version 列表ATC 报错提示算子不支持ONNX 图里包含未知算子用 onnxsim 简化模型或用等价基础算子替代自定义模块运行时 acl.mdlexecute 报错输入输出 buffer 大小与模型要求不一致打印模型 desc 中的 input_size/output_size和实际分配的内存尺寸核对推理结果偏差很大AIPP 归一化配置重复或顺序不对对照模型训练时的预处理流程确认是否已经执行过归一化AIPP 里不要再叠加一次DVPP 输出图像花屏或绿边宽高没有按对齐步长计算 buffer 大小查当前 CANN 版本的 DVPP 对齐要求重新计算输出 buffer 尺寸多路视频流同时跑CPU 占用率过高大量后处理和预处理都在 CPU 上把缩放迁到 DVPP后处理用 C 或并行优化5.3 一些可以抄的经验最后分享几个我个人觉得很有用的实操经验都是踩过坑才拿到的。一是版本记录非常重要。驱动版本、CANN 版本、甚至服务器的操作系统内核版本都会影响行为。我每次搭环境都会在项目目录里写一个 requirements.txt 加上一行注释记录环境信息几个月后回来维护的时候能省掉大量重新排查的时间。二是模型先用小图跑通再用大图。第一次跑通整个流程时先把输入尺寸改小比如 320x320这样转模型、推理、后处理都很快能让你验证流程逻辑是否正确。流程对了再切回真实尺寸性能问题和高分辨率问题分开排查定位会快很多。三是性能测试不要只看推理那一小段的时间。实际业务里解码、缩放、拷贝、推理、后处理每一步都可能成为瓶颈。我在做优化时习惯给每一段都打上时间戳先找最耗时的一段动手往往比盲目调参数有效得多。四是 Atlas 300V 24G 这块卡特别适合视频分析和多路并发推理场景。24G 的显存意味着你可以同时加载多个模型或者跑比较大的模型。比如一个场景里既要检测人员又要检测车辆完全可以把两个模型都加载进去按帧切换使用不用频繁卸载模型。这种部署方式在 GPU 上容易出现显存不足在 24G 容量下就从容很多。根据我这段时间的实际使用感受Atlas 300V 24G 是一块需要花点时间去熟悉脾气的卡。它不像 CUDA 生态那样文档齐全、社区庞大很多问题要靠自己试错和翻官方工具说明。但一旦你习惯了它的工作流它在推理场景下的稳定性和性能表现确实很能打。上面这套从驱动到模型的部署流程我已经在两个项目里复用过了一次是工业质检的缺陷检测一次是视频流的行人检测只要按照步骤走基本都能顺利落地。
返回列表