Atlas 300V 24G部署YOLOv5:从环境搭建到推理调优全指南
发布时间:2026/9/25 6:54:32来源:尧图网络
最近项目里拿到一块 Atlas 300V 24G身边不少同事第一反应是“这卡到底是不是运算加速卡能不能直接拿来跑 YOLO”说实话我第一次接触昇腾推理卡时也是一头雾水性能和显存看起来都不错但部署流程跟 GPU 那套完全不是一回事。这篇文章就基于我这次从零开始把 YOLOv5 搬到 Atlas 300V 24G 上跑目标检测的经历把硬件定位、环境搭建、模型转换、推理脚本、调优方法一次讲清楚。无论你是刚接触昇腾的小白还是准备把手头 YOLO 服务迁移到国产卡上的老手都可以直接照着操作。1. 拆开Atlas 300V 24G先搞清楚它是什么加速卡1.1 硬件规格与产品定位很多人一看到“Atlas”第一反应是数据库 Atlas但这里说的是昇腾平台的 AI 推理加速卡。Atlas 300V 24G 是一张面向数据中心的推理卡采用昇腾 310 系列的 AI 处理器板载 24GB 显存专门用来做深度学习模型推理而不是模型训练。它的整体形态是半高半长单槽体积比常见的全尺寸显卡小很多适合放进 2U 或 4U 服务器里做高密度推理节点。先看一张规格速览表我在实测中核实过的主要参数如下项目参数说明芯片昇腾310系列主打高能效推理显存24GB适合做视觉大模型、多batch推理算力类型INT8/FP16 推理不是用于训练的独立算力典型功耗几十瓦级别低于同显存GPU部署密度高接口形态PCIe标准服务器PCIe插槽即可使用这张卡的 24GB 显存是个很关键的卖点。很多旧款推理卡是 8GB 或 16GB遇到批量推理或者模型输入分辨率较大时会明显吃紧。24GB 意味着你可以在不削减输入尺寸的情况下跑到 batch 8 甚至更大的 batch这对 YOLO 这类视觉模型的吞吐提升非常直接。不过要注意它并非 CUDA 生态下的 GPU程序不能直接用 CUDA 去调用它必须通过昇腾自带的 CANN 工具链和 ACLAscend Computing Language接口来驱动。1.2 为什么用它跑YOLO而不是RTX显卡因为长期做视觉服务的部署我手边也有不少 NVIDIA 的卡。坦白讲从生态成熟度上来讲 CUDA 确实更省心但 Atlas 300V 24G 在几个场景下很有优势第一是能效比。单卡功耗低意味着一个机箱里可以插很多张卡单位机架空间内能堆出更高的视频流路数。对于摄像头数量多、每路只需要跑 YOLO 小模型的场景用 Atlas 这种推理卡比用大功率 GPU 划算很多。第二是显存容量带来的灵活性。24GB 显存放在推理卡里已经属于“大杯”跑 YOLOv5s 或 YOLOv8s 这种模型时甚至可以同时加载多个模型实例做多模型混布。比如同一张卡里跑一个行人检测模型和一个车牌识别模型节省服务器数量。第三是国产化需求。许多政企项目明确要求使用国产芯片方案Atlas 系列是当前最主流的选择之一。如果你所在团队正在做国产化迁移提前把 YOLO 部署链路跑通后面项目落地会顺利很多。当然如果你是做训练、做算法迭代不建议用 300V 24G它的定位是推理加速卡不是训练加速卡。训练请用昇腾的 Atlas 训练卡或者继续用 GPU。2. 拿到卡之后的环境搭建这一步决定后面少踩一半的坑2.1 装好驱动固件用npu-smi确认设备状态环境搭建是整个 Atlas 部署流程里最容易让人心态崩溃的部分因为版本匹配关系非常严格。我建议按“驱动 - 固件 - CANN Toolkit - Python 依赖”这个顺序装不要跳步骤。先把卡插进服务器 PCIe 插槽开机后系统里默认是看不到卡的必须装驱动。驱动安装包可以在昇腾社区下载注意区分操作系统架构x86_64 和 aarch64 的包不能混用。我这次用的机器是 x86_64 Ubuntu 20.04所以选择对应版本。安装命令一般是chmod x Ascend-hdk-*.run ./Ascend-hdk-*.run --install驱动和固件安装完成后需要重启或重新加载驱动模块然后执行npu-smi info如果能看到类似下面的输出说明卡已经被系统识别---------------------------------------------------------------------------- | npu-smi 22.0.0 Version: 22.0.0 | -------------------------------------------------------------------------- | NPU Name | Health | Power | Hugepages-Usage | |-------------------------------------------------------------------------- | 0 Atlas 300V ... | OK | 36W | 0/0 | --------------------------------------------------------------------------这里要重点看“Health”是不是 OK以及驱动版本和固件版本是否匹配。我遇到过一次驱动版本比固件版本新很多的情况npu-smi 能显示卡但加载模型时反复报设备错误最后把固件升级到配套版本才解决。建议安装时先记录好驱动版本再去昇腾社区查询对应的固件和 CANN 版本配套表。2.2 CANN工具链与Python推理环境安装驱动和固件只是让系统“认识”这块卡真正要跑模型还需要安装 CANNAscend Computing Architecture Neural Network工具链。CANN 相当于昇腾的 CUDA cuDNN它提供算子库、模型转换工具 ATC、运行时 ACL 等核心组件。安装 CANN Toolkit 时要注意如果你用 PyTorch还需要安装配套的 torch_npu。我的个人经验是优先使用昇腾官方提供的 Ascend PyTorch 镜像或安装脚本版本搭配最简单。如果手动安装流程如下。先安装 CANN Toolkitchmod x Ascend-cann-toolkit_*.run ./Ascend-cann-toolkit_*.run --install安装完成后环境变量脚本在/usr/local/Ascend/ascend-toolkit/set_env.sh每次打开新终端都要 source 一下source /usr/local/Ascend/ascend-toolkit/set_env.sh然后安装 Python 侧的依赖pip install torch torchvision torch_npu安装完后可以快速验证 PyTorch 是否能调用 NPUimport torch import torch_npu print(torch.npu.is_available()) print(torch.npu.device_count())如果第一行输出 True说明 NPU 后端已经接入 PyTorch。接下来就可以考虑把 YOLO 模型加载进来。我用 PyTorch 直连 NPU 的方式测试了 YOLOv5 的 forward但实际生产部署更推荐走 ONNX - OM 离线推理链路因为性能更稳定也更容易控制 batch 和显存。下面小节展开讲这条链路。3. 把YOLO模型搬到Atlas上的完整链路3.1 从YOLOv5权重导出ONNX模型Atlas 的离线推理不直接读 PyTorch 的 .pt 文件而是需要把模型转换为 ONNX再通过 ATC 工具编译成昇腾专用的 .om 格式。这样做的好处是编译期就能固定算子、融合图结构推理时节省算子调度开销。我用的是 YOLOv5 官方仓库整个过程非常顺。下载好 yolov5s.pt 后执行导出命令python export.py --weights yolov5s.pt --include onnx --opset 11 --batch-size 1 --img 640 640这里有两个参数很关键。第一个是--opset 11不要盲目用高版本 opset。Onnx 算子版本太高ATC 编译时反而可能遇到不支持的算子。第二个是--batch-size 1先固定 batch 为 1 跑通流程之后要提吞吐再导出一个 batch4 或 batch8 的版本。导出完成后可以用 Netron 打开 yolov5s.onnx 看一眼输出节点。YOLOv5 的 Detect 头会输出三个尺度的特征分别是 80x80、40x40、20x20每个尺度对应 255 个通道85 类 x 3 anchor。Atlas 侧的 ATC 工具能处理多输出模型所以不需要额外改模型。如果后期你需要做端到端的 nms 集成可以再研究昇腾的自定义算子但对大多数场景来说让后处理留在 Python 端更灵活。3.2 用ATC把ONNX编译成om离线模型ATC 是 CANN 里最重要的模型转换工具。编译时指定输入模型、输入形状、芯片型号就可以生成 .om 文件。我这里给出一个可以直接套用的命令source /usr/local/Ascend/ascend-toolkit/set_env.sh atc --modelyolov5s.onnx \ --framework5 \ --outputyolov5s_bs1 \ --soc_versionAscend310P3 \ --input_shapeimages:1,3,640,640 \ --insert_op_confaipp_yolov5.cfg \ --output_typeFP32 \ --loginfo参数含义解释一下--framework5表示输入模型是 ONNX。--soc_versionAscend310P3是指定芯片型号。这里你需要用npu-smi info查询实际芯片型号不同版本可能叫 Ascend310P1、Ascend310P3 等填错会编译失败。--input_shape必须和导出 ONNX 时的输入尺寸一致。YOLOv5 导出后的输入名是imagesshape 是1,3,640,640。--insert_op_conf是插入 AIPP 预处理配置。这一步可以把图像缩放、归一化全部下沉到硬件预处理单元不仅省 CPU还能降低 Host 与 Device 之间的拷贝开销。--output_typeFP32是因为 YOLO 的后处理一般有阈值比较和 NMS用 FP32 输出可以避免精度损失。AIPP 配置我建议直接写成这样aipp_op { aipp_mode: static input_format: RGB888_U8 src_image_size_w: 640 src_image_size_h: 640 csc_switch: false rbuv_swap_switch: false crop: false 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 }这里的0.003921569就是 1/255也就是把 RGB 从 0~255 归一化到 0~1。如果你在写推理代码时不想再做归一化这个配置就能帮你省掉。注意放入模型之前图像必须已经被缩放填充成 640x640AIPP 在硬件上只负责通道处理和归一化不会自动帮你做 letterbox 缩放。编译结束后目录下会生成yolov5s_bs1.om。如果 ATC 报算子不支持请优先检查 CANN 版本和 ONNX opset 版本如果报 soc 不支持就重新确认芯片型号。3.3 pyACL推理脚本图像预处理、推理、后处理有了 .om 模型接下来写推理脚本。最底层的方式是直接用 ACL API它类似 CUDA Runtime API。我先给一个精简版的示例框架。import acl import numpy as np import cv2 # 初始化 ACL ret acl.init() ret acl.rt.set_device(0) context, ret acl.rt.create_context(0) # 加载模型 model_path byolov5s_bs1.om model_id, ret acl.mdl.load_from_file(model_path) 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_num acl.mdl.get_num_outputs(model_desc) output_sizes [] for i in range(output_num): output_sizes.append(acl.mdl.get_output_size_by_index(model_desc, i))这段代码的核心是先把模型加载到设备上然后获取输入输出的内存尺寸。接下来要分配 Host 侧和 Device 侧的内存把预处理好的图像数据拷贝到 Device再调用acl.mdl.execute_async或者acl.mdl.execute执行推理。很多教程会用acl.rt.memcpy做 H2D 拷贝这里不展开全部代码重点说几个容易踩坑的环节。第一是 letterbox 预处理。YOLOv5 训练时用矩形缩放加灰色填充推理时也要保持一致。很多新手直接cv2.resize成 640x640导致目标拉伸变形最终检测框偏移严重。正确做法是计算缩放比例保持宽高比把不足的部分用 114 灰度值填充。def letterbox(img, new_shape(640, 640), color(114, 114, 114)): shape img.shape[:2] r min(new_shape[0] / shape[0], new_shape[1] / shape[1]) new_unpad (int(round(shape[1] * r)), int(round(shape[0] * r))) dw (new_shape[1] - new_unpad[0]) / 2 dh (new_shape[0] - new_unpad[1]) / 2 img cv2.resize(img, new_unpad, interpolationcv2.INTER_LINEAR) top, bottom int(round(dh - 0.1)), int(round(dh 0.1)) left, right int(round(dw - 0.1)), int(round(dw 0.1)) img cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, valuecolor) return img之后把 BGR 转 RGB、HWC 转 CHW、再转成 float32 并除以 255 即可。如果你在 AIPP 里已经做了归一化这一步只需要做 BGR 转 RGB 和 HWC 转 CHW。要注意 AIPP 默认输入格式通常是 RGB888_U8所以转成 RGB 后不要顺手归一化否则等于重复归一化模型输出会异常。第二是输出解析。YOLOv5 的 ONNX 输出通常是一个包含三个 tensor 的 tuple每个 tensor 的 shape 是[1, 255, 特征图尺寸, 特征图尺寸]。你需要把它们转成[num_anchors, 85]后再做阈值过滤和 NMS。第三是后处理的 NMS。可以用 PyTorch 的torchvision.ops.nms也可以用 OpenCV 的cv2.dnn.NMSBoxes。如果推理结果要走高性能服务推荐把 NMS 写成 C 算子或者用矢量化的 numpy 实现Python 循环太慢。ACL 执行部分相对简单我提供一个基础调用示例# 假设 input_data 是已经完成 letterbox 和 HWC-CHW 的 numpy 数组 input_data np.ascontiguousarray(input_data, dtypenp.uint8) # 申请 device 内存并拷贝 dev_ptr, ret acl.rt.malloc(input_size, 2) ret acl.rt.memcpy(dev_ptr, input_size, input_data.tobytes(), input_size, 1) # 1 表示 H2D # 执行推理 out_ptr_list [] for size in output_sizes: ptr, ret acl.rt.malloc(size, 2) out_ptr_list.append(ptr) ret acl.mdl.execute(model_id, dev_ptr, input_size, out_ptr_list, output_sizes) # 将 Device 内存拷回 Host outputs [] for i, ptr in enumerate(out_ptr_list): buf bytes(output_sizes[i]) ret acl.rt.memcpy(buf, output_sizes[i], ptr, output_sizes[i], 2) # 2 表示 D2H outputs.append(np.frombuffer(buf, dtypenp.float32).copy())使用完以后记得释放内存和 contextfor ptr in out_ptr_list: acl.rt.free(ptr) acl.rt.free(dev_ptr) acl.mdl.unload(model_id) acl.rt.destroy_context(context) acl.rt.reset_device(0) acl.finalize()这串代码看似简单但内存生命周期相当关键。如果多个请求共用 context不能每次都创建新 context如果单线程循环推理必须保证上一轮输出已经拷回 Host 并且释放 Device 内存否则显存会慢慢涨满。4. 实测中遇到的那些坑以及性能调优的几个方向4.1 常见报错与排查清单我这次部署踩了不少坑整理成表格方便大家快速定位现象可能原因解决思路npu-smi info找不到卡驱动未安装或未加载检查驱动版本重新跑安装脚本加载 .om 时提示 soc mismatchATC 指定芯片型号与卡实际型号不一致用npu-smi info查询真实型号重新转换ATC 编译 ONNX 报算子不支持ONNX opset 版本太高或 CANN 版本过旧降低 opset升级 CANN检查是否有自定义算子推理输出全是 0AIPP 归一化重复或输入图像尺寸与input_shape不一致检查预处理链路确认只用一次归一化Python import acl 失败没有 source set_env.sh或者 python 版本不匹配确认执行环境变量并检查 python 是否 3.7/3.8/3.9Device 内存持续增长推理循环没有释放上一次的 device ptr用acl.rt.free释放每次分配的内存推理结果框偏移明显letterbox 参数和训练时不统一严格使用训练时的填充灰度值和缩放方式还有一个特别容易忽略的问题ACL 初始化线程模型。如果你的推理服务用多线程并发尽量避免每个线程都调用acl.init()。通常在进程启动时做一次全局初始化然后各个线程只创建 context 或 stream。多次初始化轻则警告重则导致设备句柄冲突。4.2 把吞吐压上去的优化手段跑通单张图片之后下一步就是性能优化。我验证了几个比较有效的手段。第一是静态 batch。Atlas 推理卡对静态 shape 的优化很激进。把模型导出时 batch 设为 4 或 8然后用 ATC 编译成固定的images:8,3,640,640推理吞吐能比动态 batch 高不少。具体 batch 多大要看实际视频路数和显存余量。24GB 显存对 YOLOv5s 来说非常宽裕batch 8 通常没压力但如果模型较大需要结合npu-smi info的显存占用情况调整。第二是合理使用 AIPP。前面提到 AIPP 可以做归一化和色序转换这能把很多 CPU 工作卸载到硬件。实际测试中使用 AIPP 后单帧端到端延迟大概能降低 2~3 毫秒对于追求实时性的项目来说值得做。第三是多 stream 并发。ACL 支持多个推理 stream你可以把视频流分成多组每组用一个 stream异步提交推理。配合acl.mdl.execute_async和事件同步可以明显提升多路视频并发时的帧率。我这里建议不要直接无脑开 16 个 stream先压测找到最优并发度一般 4~8 个 stream 能达到比较好的平衡。第四是尽量使用acl.mdl.execute_async而不是同步执行。同步执行会阻塞当前线程等待推断完异步执行可以继续做下一帧预处理形成流水线。一个简单模型把预处理和推理放到两个线程并用队列连接吞吐提升肉眼可见。第五是不要频繁加载和卸载模型。如果一个进程内要长期服务多路视频模型只加载一次推理时反复用同一个model_id即可。不要在每帧请求里动态加载 .om那会带来毫秒甚至几十毫秒的额外开销。4.3 官方Samples路线快速验证如果你不想从零写推理代码也可以走官方 Samples 路线。昇腾社区提供了大量现成的 YOLO 模型和推理示例覆盖 YOLOv3、YOLOv5、YOLOv7 等常见版本。使用方式一般是下载对应仓代码按照 readme 配置模型路径和输入图片路径然后直接跑脚本。我建议先把官方 Sample 跑通一次哪怕只是看着它输出检测结果图。这样做有两个好处一是能确认你的 CANN 和驱动环境完全正常二是能看到官方代码里对 AIPP、模型输出解析、后处理的标准写法后续改自己的模型时可以少走弯路。之后再把官方代码里的模型替换成自己的业务模型改输入输出预处理即可。在跑官方 Sample 时同样要注意版本匹配。有些 Sample 提交时间较早用的是旧版 CANN API在你当前环境下可能会有微小差异。遇到报错先看 readme 的版本说明不要直接硬改代码。最后再做几句项目落地层面的提醒从拿到 Atlas 300V 24G 到把 YOLOv5 完整跑通我差不多花了一个星期其中大半天耗在环境匹配上真正写模型转换和推理脚本其实很快。个人最深的体会是昇腾的部署链路确实比 CUDA 生态要“重”但只要理解了PyTorch模型 - ONNX - OM - ACL推理这条主线后面换模型、换输入分辨率都是顺水推舟的事。另外生产环境里一定要提前设计好模型版本管理。.om 文件不像 .pt 那样可以直接改参数它是在固定 shape、固定 AIPP 配置下编译出来的二进制。每次改输入尺寸或预处理方式都需要重新 ATC 编译所以建议把 ATC 命令和 AIPP 配置写进构建脚本形成自动化流程。如果你也是第一次拿这块卡跑 YOLO建议先不要追求极致性能先跑通一张图、再做多线程并发最后再考虑显存压缩和算子融合。24GB 显存给了很大的宽容度把标准链路理顺之后这个卡其实相当能打。希望这篇文章能帮你少踩一些我踩过的坑。
网站建设高端定制企业官网