首页 / 资讯中心 / 文章详情

Atlas 300V 24G部署YOLOv5s实战:从环境搭建到推理优化全记录

Atlas 300V 24G部署YOLOv5s实战:从环境搭建到推理优化全记录 ★ FEATURED ARTICLE
去年底接了一个产线上的缺陷检测项目老机台本来跑的是传统视觉算法客户要求换成深度学习的检测模型专门盯产品表面的划痕和脏污。我们在选型阶段纠结过一阵最后定了 Atlas 300V 24G 这张卡在上面部署 YOLOv5s。整条链路从硬件认知、环境搭建、模型转换到推理调优我算是完整踩了一遍。这篇文章就记录一下当时的实际操作路径如果你也准备在 Atlas 系列上跑 YOLO不管最后选的是 300V 还是别的型号这里的思路和排错方法大概率能帮你省下不少时间。先说一个很多人拿到卡之后都会问的高频问题Atlas 300V 24G 是运算加速卡吗答案是肯定的它是一张标准的人工智能计算加速卡插在服务器的 PCIe 插槽里工作不负责显示输出也不是用来跑游戏的。它和普通显卡长得有点像但定位完全不同核心是华为的昇腾 NPU 芯片专门做深度学习推理和训练加速。我这次用它在 YOLOv5s 模型上做推理部署整个过程走下来对这套软硬件栈有了比较完整的认识下面按实际推进的顺序展开。1. Atlas 300V 24G 到底是什么卡为什么选它做目标检测1.1 先回答那个高频问题它确实是运算加速卡很多人第一次看到“300V 24G”这个名称第一反应是“这会不会是一张高端显卡”。这类命名确实容易让人往图形显卡方向想但 Atlas 300V 24G 的本质是 AI 推理/训练加速卡属于华为昇腾 Atlas 系列产品线。它的工作方式是插在服务器主板的 PCIe x16 插槽中主机通过驱动识别到 NPU 设备然后由 CANN 软件栈把深度学习任务调度到 NPU 上执行。关键点在于这张卡不做图形渲染不接显示器它的计算单元是昇腾特有的达芬奇架构由 AI Core、Vector 单元、Cube 矩阵计算单元等组成。AI Core 里的 Cube 单元擅长矩阵乘加运算而这恰恰是卷积神经网络最核心的计算类型所以跑 YOLO、ResNet 这类模型时效率很高。我记得测试时看npu-smi info设备信息会显示昇腾芯片型号和显存大小24G 就是板载显存容量。对做 AI 部署的人来说24G 显存是一个很友好的指标意味着可以加载较大 batch 的输入或者跑一些参数量比较大的模型比如 YOLOv8 的大版本、轻量化的分割模型甚至小规模的 fine-tune 任务都不会立刻撞上显存瓶颈。1.2 24GB 显存对 YOLO 部署的实际意义做目标检测部署时显存大小直接决定了你能同时处理多少路输入。YOLOv5s 的权重文件只有二十多 MB单张 640x640 的三通道图像输入也只有 1.2MB 左右单从模型和单帧数据来看2G 显存都够用。但工程落地不是单帧处理往往是多路视频流、多个并发任务同时跑还要考虑缓存、中间特征图、后处理临时内存等开销。我这次项目的实际场景是 8 路工业相机定时抓拍每帧 1200 万像素需要先缩放再推理。如果不做分批次处理单张图直出显存占用非常低但 NPU 利用率也上不去。后来把 batch 调到 8一次推理塞进 8 张图整个卡的吞吐量明显提升显存占用也只有几个 GB。24G 显存给了你充足的操作空间不需要特别精细地抠缓存开发效率能高不少。还要说一句Atlas 300V 24G 并不只是为 YOLO 这种小模型准备的。我同事拿它跑过亿参数级别的视觉模型依然能装下。如果你的业务接下来要从检测往分割、OCR、工业大模型方向扩展这张卡可以少折腾一次硬件选型。1.3 和普通 GPU 相比部署 YOLO 的体验差异用过 NVIDIA GPU 的人上手昇腾的第一感觉多半是“软件栈不习惯”。GPU 那边基本是 CUDA cuDNN TensorRT 一条龙网上教程多、资料全。昇腾这边对应的是 CANN AscendCL ATC思路是类似的但命令和配置方式完全不同需要一点适应时间。从硬件角度看NPU 的架构更偏向确定性执行AI Core 对固定 shape、固定 batch 的模型效率很高一旦模型输入尺寸频繁变化或者图结构里有大量动态分支性能就会打折。所以用 Atlas 卡跑 YOLO我强烈建议把输入分辨率固定下来训练和部署用同一个尺寸整个链路会顺畅很多。另外Atlas 卡对 INT8 量化支持很成熟YOLO 这种检测模型量化后精度损失通常可以控制在 1~2 个百分点以内速度提升却很明显。这些特点在 GPU 上反而不一定这么突出。2. 部署前必须理清的软硬件栈和基础环境2.1 理清 CANN、AscendCL、OM 模型之间的关系开始装环境之前先花五分钟把软件栈关系搞清楚后面排错会轻松很多。CANN 是昇腾的软件计算平台你可以把它类比成 NVIDIA 的 CUDA它是驱动之上、应用之下的一整套库和工具链。AscendCL 是 CANN 提供的编程接口对应 CUDA Runtime API应用层通过它来管理设备、申请内存、加载模型、执行推理。OM 是昇腾的离线模型格式。你训练出来的 PyTorch 模型先导出成 ONNX再通过 ATC 工具转换成 OM推理时直接加载 OM 文件。为什么不能直接喂 ONNX因为 ONNX 是通用描述格式昇腾的 NPU 需要把计算图映射到自己的算子实现上ATC 就是做这个映射和编译工作的。转换后的 OM 会针对特定芯片型号、特定输入 shape 做算子融合和内存优化执行效率比在线解释执行高得多。这个关系理解了你再看部署流程就非常清晰训练 - 导出 ONNX - ATC 转 OM - AscendCL 加载执行 - 输出解析。后面所有步骤都是围绕这条链路展开的。2.2 安装驱动、固件和 CANN Toolkit 的完整流程环境安装是 Atlas 部署的第一个大坑很多问题都出在这一步。先说驱动和固件这两者是分开的都需要安装。驱动负责操作系统识别 NPU 设备固件负责设备底层的运行。最常用的检查命令是npu-smi info类似 NVIDIA 的nvidia-smi能看到芯片型号、驱动版本、显存占用和算力状态。安装流程大致如下先准备一个装有标准 x86 架构操作系统的服务器Ubuntu 18.04 或 20.04 都行内核版本最好对照官方支持列表。然后从昇腾社区下载对应版本的驱动和固件包解压后以 root 权限执行安装脚本依次装固件、装驱动、重启。重启之后跑npu-smi info如果能看到设备列表说明底层没问题。接着装 CANN Toolkit安装包同样是解压后执行脚本默认安装在/usr/local/Ascend目录下。装完以后需要配置环境变量我每次都会在~/.bashrc里追加一行source /usr/local/Ascend/ascend-toolkit/set_env.sh这行命令会把 ATC 工具、AscendCL 的 Python 依赖和运行时库全部加到当前 shell 的环境里。很多人报错说找不到atc命令八成就是没 source 这个脚本。安装时还有个小细节CANN 在设备侧会创建名为HwHiAiUser的用户和用户组如果你用普通用户跑推理需要把自己加进这个组否则访问 NPU 设备时会报权限错误sudo usermod -a -G HwHiAiUser $USER加完记得退出登录重新进一次组权限才会生效。2.3 最小验证用样例确保环境真的可用环境装完不能直接开跑先用一个最小样例验证。CANN Toolkit 自带了 samples里面通常会有一个简单的 resnet50 推理示例。走一遍样例的编译和运行能跑通就说明驱动、固件、Toolkit、权限、环境变量全部没问题之后再碰自己的模型时问题就不会出在环境层。如果嫌样例麻烦也可以先跑atc --version和 Python 里验证 ACL 能否初始化import acl ret acl.init() print(ret)返回 0 说明 ACL 初始化成功。这一步花不了五分钟但能过滤掉后面至少一半的环境报错。我这个项目里第一次就是直接拿自己的 YOLO 模型去转换结果报了一堆看不懂的错后来才发现是 Toolkit 环境变量没配置完整白白浪费了半个下午。3. YOLO 模型从 ONNX 到 OM 的转换实战3.1 训练侧和导出 ONNX 时就要注意的细节很多人在模型转换阶段卡住根本原因不是 ATC 命令写错而是训练侧导出的 ONNX 就不满足昇腾部署的需求。昇腾 NPU 对动态 shape 的支持比较有限动态输入在转换时会生成大量分支逻辑导致算子融合率下降推理速度变慢甚至直接报“ contains dynamic shape”之类的错误。我建议在导出 ONNX 时就固定输入尺寸或者至少固定部分维度。以 YOLOv5s 为例训练时的输入一般是 640x640导出时用以下方式固定 batch 为 1import torch model torch.load(yolov5s.pt, map_locationcpu)[model].float() model.eval() dummy_input torch.randn(1, 3, 640, 640) torch.onnx.export( model, dummy_input, yolov5s.onnx, opset_version11, input_names[images], output_names[output0, output1, output2], dynamic_axesNone, )这里dynamic_axesNone是关键ONNX 图里所有维度都被固化ATC 转换时就不需要做动态 shape 推导生成的 OM 效率更高。如果你的业务确实需要不同 batch比较推荐的做法是分别导出bs1和bs8两个模型文件按需加载。实际部署时二选一别想着一个模型吃遍所有场景。另外导出前记得把模型里的前处理信息弄清楚。YOLOv5 训练时通常会把图像 resize 到 640x640像素值除以 255 归一化到 0~1 范围。这些操作要么留在 PyTorch 模型内部要么在 AIPP 配置里做但两边必须一致否则推理结果会非常离谱。我在后面 AIPP 章节会详细讲这个坑。3.2 ATC 转换命令、参数和 AIPP 配置ONNX 准备好之后用 ATC 工具转换成 OM。ATC 全称是 Ascend Tensor Compiler命令行的核心参数就那么几个但每个参数填错都会带来完全不同的报错。下面是我这次用的转换命令可以作为一个基础模板atc --modelyolov5s.onnx \ --framework5 \ --outputyolov5s_bs1 \ --soc_versionAscend310P3 \ --input_shapeimages:1,3,640,640 \ --insert_op_confaipp.cfg \ --output_typeFP32解释一下关键参数--framework5表示输入是 ONNX 模型如果输入的是 TensorFlow 的 pb 文件这个值要改--soc_version指定目标芯片型号这是最容易报错的地方一定不要照抄网上的值要用npu-smi info查自己的芯片信息再对照 CANN 官方文档确认对应的 soc_version--input_shape告诉 ATC 输入张量的维度--insert_op_conf是 AIPP 配置文件--output_type控制输出数据类型一般保持 FP32。AIPP 是昇腾的图像预处理模块可以在模型推理前把 resize、颜色空间转换、归一化等操作合入模型图中很大程度减少 Host 侧的数据处理开销。对于 YOLOv5s我用过一个比较简单的静态 AIPP 配置aipp_op { aipp_mode: static input_format: RGB888_U8 src_image_size_w: 640 src_image_size_h: 640 mean: 0 0 0 min_chn_0: 0.003921568627451 min_chn_1: 0.003921568627451 min_chn_2: 0.003921568627451 }这份配置做的事情非常明确输入格式是三通道 RGB 的 uint8 图像不需要再次缩放每个通道直接乘以 1/255正好对齐 YOLOv5 训练时的归一化逻辑。这里要注意PyTorch 训练时用的是 RGB 通道顺序而 OpenCV 读图默认是 BGR需要在喂给推理接口之前先转换通道顺序或者用 AIPP 里的 RGB 到 RGB 配置自行处理。注意mean和min_chn不是随便填的。AIPP 的计算逻辑是pixel (pixel - mean) * min_chn所以当你要实现“除以 255”这个操作时mean 填 0scale 填 1/255。如果你训练时用的是 ImageNet 的 mean/std 归一化就得把对应的三个值填进去不能照抄这份配置。3.3 用 msame 先验证 OM 模型能不能推理模型转换成功后先不要急着写推理代码用昇腾官方的msame工具验证一下 OM 模型是否能正常跑通。msame 是一个命令行推理工具用法类似 TensorRT 的 trtexec输入预处理好的二进制数据输出推理结果可以检查模型输出维度是否合理。我一般这么操作先用 Python 预处理一张测试图存成二进制文件然后运行msame --modelyolov5s_bs1.om \ --inputpreprocessed_data.bin \ --outputout/如果运行成功out目录下会出现一个二进制输出文件可以用 NumPy 读取检查维度。对 YOLOv5s 来说输出应该是 3 个 tensor对应三个尺度的预测结果。看到输出维度正确就说明 OM 模型本身没问题接下来写推理代码时就可以放心排自己代码的错。这一步看起来多余实际上能帮你做一次非常关键的问题隔离。很多新手一遇到推理结果不对就怀疑代码有问题其实是模型转换时 AIPP 配置错了反过来如果一上来就写完整推理代码出问题时根本搞不清是转换的问题还是代码的问题。先用 msame 把模型验证干净后面排查范围缩小一大半。4. 编写推理代码从加载模型到输出目标框4.1 AscendCL 推理主流程msame 验证通过后开始写正式的推理代码。这里用到的是 AscendCL 的 Python 接口整体流程可以概括为初始化设备 - 加载 OM 模型 - 准备输入输出内存 - 执行推理 - 取回输出 - 释放资源。我用一个简化版本演示主流程具体函数签名以你安装的 CANN 版本和官方 sample 为准import acl import numpy as np # 1. 初始化 acl.init() device_id 0 acl.rt.set_device(device_id) context, ret acl.rt.create_context(device_id) # 2. 加载模型 model_path byolov5s_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) # 3. 获取输入输出尺寸 input_size acl.mdl.get_input_size_by_index(desc, 0) output_size0 acl.mdl.get_output_size_by_index(desc, 0) output_size1 acl.mdl.get_output_size_by_index(desc, 1) output_size2 acl.mdl.get_output_size_by_index(desc, 2) # 4. 申请 device 侧内存 input_ptr, ret acl.rt.malloc(input_size, acl.const.ACL_MEM_MALLOC_HUGE_FIRST) output_ptr0, ret acl.rt.malloc(output_size0, acl.const.ACL_MEM_MALLOC_HUGE_FIRST) output_ptr1, ret acl.rt.malloc(output_size1, acl.const.ACL_MEM_MALLOC_HUGE_FIRST) output_ptr2, ret acl.rt.malloc(output_size2, acl.const.ACL_MEM_MALLOC_HUGE_FIRST) # 5. 把预处理好的图像拷到 device 侧 input_data preprocess(image) # 形状为 (1, 3, 640, 640) 的 float32 numpy 数组 input_data np.ascontiguousarray(input_data) input_data_ptr acl.util.numpy_to_ptr(input_data) ret acl.rt.memcpy(input_ptr, input_size, input_data_ptr, input_size, acl.const.ACL_MEMCPY_HOST_TO_DEVICE) # 6. 执行推理 ret acl.mdl.execute(model_id, input_ptr, input_size, [output_ptr0, output_ptr1, output_ptr2], [output_size0, output_size1, output_size2]) # 7. 取回结果 def copy_back(ptr, size): output_np np.zeros(size, dtypenp.uint8) out_ptr acl.util.numpy_to_ptr(output_np) acl.rt.memcpy(out_ptr, size, ptr, size, acl.const.ACL_MEMCPY_DEVICE_TO_HOST) return output_np out0 copy_back(output_ptr0, output_size0) out1 copy_back(output_ptr1, output_size1) out2 copy_back(output_ptr2, output_size2)这段代码里的核心逻辑就是五个字拷贝、执行、拷回。CANN 的环境内存管理比 CUDA 更直接模型输入输出都需要显式申请 device 侧内存执行完成后必须手动释放。如果是长期运行的服务建议在初始化阶段把所有内存一次性申请好避免每次推理都做内存分配那会显著拖慢性能。4.2 YOLO 输出的解码与后处理AscendCL 拿到的是三个原始输出 tensor它们不是最终的检测框。YOLOv5 的三个输出分别对应 80x80、40x40、20x20 三个尺度的特征图每个特征图上每个格子有 3 个 anchor每个预测值是5 类别数对 COCO 80 类来说就是 85 维。需要在 CPU 侧做 decode、置信度过滤和 NMS才能得到最终的矩形框。decode 的逻辑和训练代码是一致的把预测的 x、y 加上网格偏移再乘上对应 stride把预测的 w、h 通过 exp 还原成实际宽高类别分数用 sigmoid 转成概率。NMS 可以直接用cv2.dnn.NMSBoxes这个函数底层实现经过了充分优化对 YOLO 输出的小目标候选框处理速度很快。这里有一个重要的工程取舍decode 和 NMS 放在 NPU 上做还是 CPU 上做。我的经验是放在 CPU 上做因为 YOLOv5s 在 640 输入下总共只有 25200 个候选框decode 加 NMS 在 CPU 上跑一次大概几毫秒远远小于单次推理的耗时完全没有必要增加模型的复杂度。如果你用的是大模型或者超高分辨率输入再考虑把这些步骤搬到 NPU 上实现。4.3 让推理跑得更稳的几点工程建议推理代码能跑通只是第一步真正要把它变成一个稳定的服务还有几个细节值得注意。第一输入数据的 dtype 和 layout 必须严格对齐训练时的配置。YOLOv5 的 PyTorch 模型推理时通常用 NCHW 的 float32 张量但很多人在 Host 侧用 OpenCV 处理后忘
阅读完成 · 觉得有帮助?
咨询建站