ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Atlas 300V 24G部署YOLO全流程实战:从环境搭建到性能调优

Atlas 300V 24G部署YOLO全流程实战:从环境搭建到性能调优 说实话这两个问题几乎是同一个问题Atlas 300V 24G 就是一张用来做 AI 推理的运算加速卡而它最典型的落地场景之一就是把 YOLO 这类目标检测模型真正推到生产环境里跑起来。我手上这块卡用了大半年从驱动安装、CANN 环境配置到模型转换、推理调优踩过的坑多得能写一本小册子。今天不聊那些花里胡哨的官方介绍就从一个实际跑过 YOLO 的人的角度把“atlas 部署 yolo”这条链路从头到尾拆开讲清楚顺便回答那些你搜了三天都找不到准确答案的细节问题。1. 先把卡认清楚Atlas 300V 24G 到底能干吗1.1 一张运算加速卡的自白很多人第一次拿到 Atlas 300V 24G会下意识地把它当成“显卡”。严格来讲它不是显卡它没有视频输出接口不能接显示器它的定位是 AI 推理专用加速卡。你可以把它理解成一条专门给图像、视频、自然语言处理任务准备的“高速流水线”CPU 负责调度和业务逻辑这张卡负责把模型推理这种高密度矩阵运算一口气啃下来。从硬件架构来说Atlas 300V 24G 基于昇腾 310P 芯片核心是达芬奇架构里的 AI Core专门优化矩阵乘法和卷积运算。这类芯片在推理任务上非常高效因为推理不像训练那样需要反向传播只要把前向计算做到极致快就行。24G 显存用的是 LPDDR4X带宽做到 204GB/s 这个量级标称 INT8 算力在 260 TOPS 附近。只看纸面数据它已经站到了单卡推理的第一梯队。它的形态是半高半长单槽 PCIe 卡最大功耗 75W 左右插到普通服务器里非常省事不需要额外的 8pin 供电线。我在测试机上插一张日常推理时整卡功耗基本在 45W 上下浮动这个功耗控制对机房里长期跑 7x24 小时服务的场景特别友好。1.2 24G 大显存对 YOLO 部署意味着什么YOLO 这种目标检测模型绝大多数版本吃显存并不算夸张YOLOv5s 用 FP16 精度跑 640 分辨率单张图推理时模型本身占用的显存可能只有几百 MB。那 24G 大显存是不是浪费不是。显存大最直接的好处是能开大 Batch、跑更多并发。你在一张 24G 的卡上可以把一个 Batch 直接开到 32 甚至更大一次喂进去 32 张 640x640 的图做推理把卡上的算力全部打满。做视频分析的时候一个模型同时分析 16 路乃至 24 路视频流除了显存够不够还要看解码能力跟不跟得上。8G 显存如果跑 24 路 1080P 视频的 YOLOv5l 模型显存早就爆了但在 24G 卡上不用纠结这些。如果你做的是工业质检、医学影像这种高分辨率输入场景大显存更是刚需。原图 2048x2048 甚至 4096x4096直接整图输入8G 卡连 Batch 1 都可能内存不足24G 卡还能一次放多张图进去。这也是我当初选 24G 版本而不是 8G 版本的核心原因你可以不用但不能没有。1.3 和 NVIDIA T4 对比凭什么选它聊到推理卡很多人第一反应是 NVIDIA T4。确实T4 是上一代推理首选我在早期项目里也用过。但把 Atlas 300V 24G 摆在一起有几个点值得对比一下。从推理算力看Atlas 300V 的标称 INT8 算力明显高于 T4 那个量级在 YOLOv5s 这类中小模型上单帧延迟能压到几毫秒级别。显存上 24G 对 16G多出来的 8G 在视频分析和批量推理上有实打实的优势。功耗两卡几乎持平75W 对 70W不用改服务器供电方案。最容易让人纠结的是软件生态。NVIDIA 有 CUDA、TensorRT社区资料多上手快。Atlas 这边的软件栈是 CANN起步比 CUDA 晚但这些年迭代速度很快官方文档越来越全PyTorch 模型转 ONNX 再转 OM 的工具链也越来越稳。如果你愿意花一两天熟悉 CANN 的开发范式后面做推理优化其实非常顺手。选哪张卡取决于你的约束条件。如果项目明确要求成本控制和推理吞吐Atlas 300V 24G 是不错的选择如果你手里的模型包含大量自定义算子、依赖 TensorRT 插件短期迁移成本会高一些。我个人的建议是先拿一张测卡跑通 YOLO感受一下 CANN 的开发和调试节奏再决定是否整体切过来。2. 部署 YOLO 之前的硬环境准备2.1 开箱上机安装与驱动验证环境搭建这一步最容易被新手忽略但它决定了后面所有工作能不能顺利跑起来。Atlas 300V 24G 是标准 PCIe 卡插进服务器的 16x 插槽接线、开机然后装驱动。驱动安装好以后第一件事就是在终端输入npu-smi info这个命令等价于 NVIDIA 那边的nvidia-smi能看到卡的实时状态。正常输出会显示卡的名称、健康状态、功耗、显存占用、温度和当前算力利用率。如果这一步报错或者找不到设备多半是驱动没装好或者卡的固件和驱动版本不匹配去昇腾社区把配套的驱动和固件包重新刷一遍。这里有个容易踩的坑Atlas 300V 有多个硬件版本固件和驱动必须和硬件版本严格对应。我之前有一台服务器升级内核后驱动重新编译完还是识别不到卡最后是重刷了固件才解决。建议开机验证时直接把npu-smi info的输出保存一份后面排查性能问题还要拿它做基准。2.2 安装 CANN 工具链驱动搞定之后下一步是安装 CANN。CANN 是昇腾 AI 处理器的软件栈等价于 CUDA 对整个 GPU 体系的角色。模型转换工具 ATC、推理接口 ACL、算子库、运行时全部在 CANN 里。CANN 的安装包可以在昇腾社区下载有几种形态完整版、开发版、推理版。如果是纯做部署推理安装推理版就够了体积更小装起来也快。安装时重点记一下安装路径我这边通常装在/usr/local/Ascend/ascend-toolkit装完以后执行source /usr/local/Ascend/ascend-toolkit/set_env.sh把环境变量导进当前 shell。环境变量里最关键的是把 ATC 工具的 bin 目录和 ACL 的 so 库路径加进 PATH 和 LD_LIBRARY_PATH。每次新开终端都要先 source 一次嫌麻烦可以直接写进用户的.bashrc。验证 CANN 装没装好可以用atc --version能正常打印版本号就说明 ATC 工具可用。CANN 版本更新很快不同的版本对 ONNX operset 的支持不一样强烈建议不要用太老的版本至少选一年内发布的新版本算子兼容性会好很多。2.3 用 Docker 封装一套干净的开发环境如果你和我一样需要在多台机器之间切换用 Docker 封装开发环境是最省心的方式。昇腾官方维护了一批带 CANN 的容器镜像拉到本地直接跑不需要每台机器都重新装一遍 CANN。启动容器时要把 NPU 设备挂载进去。常规 docker run 参数要加上docker run -it --rm \ --device/dev/davinci0 \ --device/dev/davinci_manager \ --device/dev/hisi_hdc \ -v /usr/local/Ascend/driver:/usr/local/Ascend/driver \ -v /usr/local/dcmi:/usr/local/dcmi \ -v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi \ ascendhub.huawei.com/public/ascend-alpine:latest在容器里跑npu-smi info能正常显示卡信息就说明设备挂载成功。这样做的好处很明显即使不小心把系统搞坏了删掉容器重新起一个几分钟就能恢复环境。不过我一般不直接在容器里做模型转换ATC 这类工具放在宿主机装一份容器里只装运行 ACL 推理需要的库分工更清晰排查问题时也不用怀疑是容器隔离层带来的性能损耗。3. YOLO 模型迁移从 ONNX 到 OM 的完整链路3.1 导出 ONNX 前的模型改造Atlas 上最终运行的模型格式是 OM它由 ONNX 转换而来。所以第一步是把训练好的 PyTorch YOLO 模型导出成 ONNX。以 YOLOv5 为例官方仓库自带导出脚本python export.py --weights yolov5s.pt --include onnx --opset 11 --batch-size 8这里的--opset建议控制在 11 到 13 之间。我试过用 opset 16 甚至 17 导出ATC 转换时报了一堆算子不支持的错改成 opset 11 就顺了。导出时还要注意一个点YOLOv5 原模型的检测头包含了 Decode 逻辑也就是把 feature map 解算成最终的框坐标和类别概率。这部分在 ONNX 里会引入大量额外算子增加转换难度而且推理时放在模型里跑也会多耗时间。常见的做法是导出时只保留三个尺度的原始输出也就是 shape 为 [bs, 255, 80, 80]、[bs, 255, 40, 40]、[bs, 255, 20, 20] 的 feature map后处理放到代码里自己写。这样模型更干净ATC 转换的成功率高很多。如果你用的是 YOLOv8导出 ONNX 的逻辑类似但输出节点的组织方式不同转换参数可能要微调。我习惯先导出 ONNX再用 Netron 看一眼模型结构确认输入输出节点的名字和 shape再去写 ATC 命令。3.2 ATC 转换命令与作用详解模型转换是整条链路里门槛最高的一步ATC 命令的参数很多但核心就那几个。拿我之前转 YOLOv5s 的命令举例source /usr/local/Ascend/ascend-toolkit/set_env.sh atc --modelyolov5s.onnx \ --framework5 \ --outputyolov5s_bs8 \ --soc_versionAscend310P3 \ --input_shapeimages:8,3,640,640 \ --input_formatNCHW \ --insert_op_confaipp_yolov5.cfg \ --output_typeFP32 \ --loginfo逐项拆开说--framework5表示输入模型是 ONNX这个 5 是固定值不要改。--soc_versionAscend310P3指定目标芯片版本。这个参数最容易填错不同型号的 Atlas 300V 对应不同的 soc version有的对应 Ascend310P1有的对应 Ascend310P3。填错了虽然能转但生成的 OM 在卡上很可能加载失败。具体填什么以官方产品文档或npu-smi info显示的型号为准。--input_shape要和导出 ONNX 时的输入名一致。YOLOv5 的输入名默认是images。Batch 数可以改这是调优的重要入口。--insert_op_conf指定 AIPP 配置文件它做图像预处理的下沉具体在下面一节细讲。--loginfo比较实用。转换失败时info 级别日志能看到具体是哪个算子出了问题默认的 error 级别信息太少。转换成功的标志是输出一个.om文件和一行类似AIToolKit update aicore result的日志。如果日志里出现E19999这种错误先别慌大部分是算子兼容性问题升级 CANN 版本或者降低 ONNX opset 能解决大部分。3.3 AIPP 配置精度崩了八成是它的锅AIPP 全称 AI Preprocessing就是让 Atlas 卡在模型推理之前自动帮你在硬件层面完成一部分图像预处理。这样 CPU 端就不用在推理前做归一化、颜色空间转换这些操作了。但 AIPP 配置错了模型精度会突然崩盘而它又是最容易出错的地方。我踩过最典型的一个坑YOLOv5 训练时的预处理是把像素值除以 255归一化到 [0,1] 区间但我 AIPP 里忘了配这个归一化操作结果推理出来的框全是乱的。一个能用的 AIPP 配置模板是这样aipp_op { aipp_mode: static input_format: RGB888_U8 csc_switch: false rbuv_swap_switch: false src_image_size_h: 640 src_image_size_w: 640 mean: 0.0 0.0 0.0 var_reci: 0.003921569 0.003921569 0.003921569 }逐项解释input_format是喂给模型的输入图像格式。我的 Host 代码里已经做好了 BGR 转 RGB 和通道顺序调整所以这里填 RGB888_U8。如果你直接把 OpenCV 读出来的 BGR 数据丢进去通道会反检测结果基本全错。mean是每个通道的均值。YOLOv5 官方训练时没减均值所以填 0。var_reci是方差的倒数。像素值从 0~255 映射到 0~1需要乘以 1/255也就是 0.003921569。这个数要精确差一点都不行。在这个配置下以前在 CPU 上做的img / 255.0就可以完全去掉输入数据直接给原始 uint8 图像就行算力卡自动完成归一化少了 CPU 到 NPU 的数据搬运整体延迟能低不少。4. 在 Atlas 上跑 YOLOACL 推理代码实操4.1 ACL Python 接口初始化和模型加载模型转好以后真正跑推理用的是 ACL。CANN 提供 C 和 Python 两套接口我这边为了迭代快先用 Python 验证逻辑性能要求高的 C 接口再单独写。ACL 的 Python 接口上手非常简单核心流程分四步初始化、加载模型、准备输入输出内存、执行推理。一个最基本的示例骨架如下import acl import numpy as np # 1. 初始化设备 acl.init() acl.rt.set_device(0) context, ret acl.rt.create_context(0) # 2. 加载 OM 模型 model_path byolov5s_bs8.om model_id, ret acl.mdl.load_from_file(model_path) # 3. 获取模型输入输出信息 model_desc acl.mdl.create_desc() acl.mdl.get_desc(model_desc, model_id) ret, input_dims acl.mdl.get_input_dims(model_desc, 0) ret, output_dims acl.mdl.get_output_dims(model_desc, 0) # 4. 申请 device 内存 input_size 8 * 3 * 640 * 640 * 4 # bs8, RGB, 640x640, FP32 input_ptr, ret acl.rt.malloc(input_size, 2) output_ptr, ret acl.rt.malloc(input_size, 2) # 5. 拷贝输入数据host - device acl.rt.memcpy(input_ptr, input_size, host_input_data.ctypes.data, input_size, acl.rt.MEMCPY_HOST_TO_DEVICE) # 6. 同步推理 ret acl.mdl.execute(model_id, [input_ptr], [output_ptr]) # 7. 取回输出 output_data np.zeros(input_size, dtypenp.float32) acl.rt.memcpy(output_data.ctypes.data, input_size, output_ptr, input_size, acl.rt.MEMCPY_DEVICE_TO_HOST)有几个细节必须提醒一是acl.mdl.get_input_dims的返回值在不同 CANN 版本里可能有差异有的版本返回(ret, dims)有的直接返回 dims。写代码之前先简单打印一下返回值结构。二是申请内存时的大小单位是字节我的示例里默认模型输入是 FP32所以每个像素占 4 字节。如果模型是 FP16要改成 2 字节。算错的话内存不够推理结果就是乱的。三是acl.mdl.execute同步版本会阻塞到推理完成简单但不高效。实际项目里想打满卡后面要用execute_async配合 stream。4.2 图像预处理能用 DVPP 就别用 OpenCV在 Atlas 上做视频分析图像预处理如果还走 OpenCVCPU 使用率会高得离谱。我之前做 1080P 视频流分析还没开始推理CPU 的解码和 resize 就已经占到 70% 以上整个系统卡得不行。后来换成了 DVPPCPU 直接降到 15% 以下。DVPP 是 Atla 硬件自带的视频预处理单元里面有专门负责 JPEG 解码的模块还有统一做缩放、裁剪、颜色空间转换的模块。JPEG 解码和 640x640 缩放这种高频操作全部交给硬件去并行处理CPU 就彻底解放出来了。用 DVPP 的流程是先把 JPEG 数据送进解码模块得到 YUV420SP 格式的图像然后交给缩放模块resize 到模型需要的 640x640再做颜色空间转换输出 RGB888。这两步都可以在 Device 端完成输出结果直接作为 ACL 推理的输入省掉了重复的 host-device 拷贝。这里有个大坑DVPP 对图像宽高有对齐要求一般是 16 像素对齐。如果你输入一张 1920x1080 的图直接 resize 到 640x640 没问题但如果模型输入本身是 640x960 这种不能被 16 整除的尺寸DVPP 出来的图底部或者右侧会被补齐多余像素导致推理结果偏移。解决办法是在 AIPP 里配置裁剪或者把输入尺寸统一调整到 16 的倍数。4.3 推理输出与帧率统计推理只是个开始YOLO 的输出还需要解析才能变成检测框。如果模型导出时保留了三个尺度的 feature map后处理要遍历三层分别计算 anchor 对应的坐标、置信度和类别概率再经过 NMS 合并结果。这部分计算量不小我一般用 NumPy 向量化操作虽然还是比不上 C但在 Python 原型里够用。写完解析函数之后要做的一件重要事情是统计真实帧率和延迟。CANN 自带的时间接口精度够用也可以用 Python 的time.perf_counter()打点统计。我的测试方式是连续跑 1000 张真实场景图取平均延迟。单纯看单帧推理延迟会骗自己因为整条链路里还要算上解码、预处理、后处理的时间。要评估真实吞吐就记从输入图像到输出检测框的完整耗时。这样统计出来的 FPS 才是你上线以后真正能达到的水平。5. 性能调优的三个关键动作5.1 BatchSize 调整从 3ms 到 1.8ms模型在 24G 显存上跑如果一次只推理一张图浪费太明显。推理卡和游戏显卡不一样它追求的是高吞吐。把 BatchSize 调大让卡一次处理更多张图单帧平均延迟会明显下降。我在测试机上用 YOLOv5s、640 输入、FP16 精度做过一组实测数据大致是这样的配置单帧平均延迟综合吞吐BS1约 3.2 ms约 300 FPSBS8约 1.9 ms约 420 FPSBS16约 1.7 ms约 470 FPS这组数据在我自己的服务器上跑出来的不同固件和 CANN 版本会有差异但趋势是一致的BS8 到 BS16 之间已经是边际收益递减区。用 BS8 的情况下24G 显存还有大量余量此时瓶颈往往不在算力而在 PCIe 传输和预处理速度。所以 BatchSize 不是越大越好要结合你的数据到达速度和硬件平台整体考虑。模板里的做法是把多张图动态拼成一个 Batch 再推理。视频流并发场景里只要凑够 8 帧就触发一次推理既能保证吞吐又不会让单帧延迟高到离谱。5.2 Stream 并发让多路视频真正流畅同步推理是一锤子买卖拷贝数据、算完、拷结果再处理下一帧整个流程线性执行卡的利用率其实很低。想要把卡的算力彻底榨干要用 stream 并发。stream 可以理解成卡上的一条执行队列。一个 stream 里的操作按顺序执行但多个 stream 之间可以并行。我之前做 16 路视频流分析时开了 4 个 stream每个 stream 处理 4 路视频整体吞吐比单 stream 翻了将近一倍。代码层面的改动不大把同步执行换成异步# 创建 stream stream, ret acl.rt.create_stream() # 设置当前 stream acl.rt.set_current_stream(stream) # 异步推理 ret acl.mdl.execute_async(model_id, [input_ptr], [output_ptr]) # 等待 stream 执行完成 acl.rt.synchronize_stream(stream)异步模式下多路视频的预处理、推理、后处理可以形成流水线一张卡同时干解码、推理、拷贝几件事每一块硬件都在忙碌。碰到多卡环境还能把 stream 分配到不同卡上进一步扩展。5.3 INT8 量化把 INT8 算力用满Atlas 300V 24G 的 INT8 算力远高于 FP16但默认情况下模型跑的是 FP16INT8 算力完全是闲置的。想让推理吞吐再上一个台阶量化是最直接的路径。CANN 自带的模型压缩工具叫 AMCT支持后训练量化也叫 PTQ。不需要重新训练准备几十到一百张有代表性的校准图片让工具统计出合适的量化范围就能把 FP16 模型转成 INT8。命令大致长这样amct_onnx quantize \ --modelyolov5s.onnx \ --input_shapeimages:1,3,640,640 \ --data_dir./calibration_data \ --num_quant_iter100 \ --save_path./outputs量化完成后会生成量化后的 ONNX再走一遍 ATC 转成 OM。我实测 YOLOv5s 在 INT8 量化后mAP 掉点能控制在 1% 以内但推理速度几乎翻倍。一开始我以为量化会伤筋动骨结果在检测任务上比我想象中稳得多。不过量化这事不能闭眼用。模型对数值变化敏感度不同有的模型掉点严重如果验证集上掉点超过 3%就老老实实退回 FP16。另外校准图片一定要覆盖真实场景的分布例如拿白天图像做校准到了夜间场景精度可能掉得厉害。6. 常见问题与排查技巧实录6.1 模型转换失败的典型报错ATC 转换报错是最容易劝退新手的环节。我把这段时间遇到的典型问题整理成了一个速查表报错或现象根本原因解决思路E19999: Inner Error, Op:ResizeONNX 里某个算子当前 CANN 版本不支持降低 opset或用 ONNX Simplifier 简化模型ATC 转换成功后加载 OM 失败soc_version 填错用npu-smi info确认芯片型号后改参数转换时间极长且日志卡死输入 shape 有动态维度用固定 shape 导出 ONNX或设置动态 shape 范围转换后模型输出 NaNAIPP 里的归一化参数不对检查 var_reci 和 mean 是否匹配训练时的参数遇到 E19999 这类错误第一步不要反复改一个参数乱试。把--loginfo的日志打开搜日志里提到的第一个不支持算子名字基本就能定位问题。很多时候用python -m onnxsim model.onnx model_sim.onnx把冗余节点清掉问题就消失了。6.2 精度莫名下降的排查路径模型转换后精度掉了先别怀疑卡坏了按这个顺序排查第一看 AIPP 参数。这是最高频的坑mean、var_reci、通道顺序任何一个不对检测结果都会崩。把 AIPP 关掉在 Host 侧自己做好归一化再喂给模型如果精度恢复正常那问题就锁死在 AIPP 配置上。第二看输入尺寸。ONNX 导出时设置的输入尺寸必须和 AIPP 里src_image_size_h/w一致也要和推理代码实际喂进去的数据尺寸一致。三处只要有一处不一致模型拿到的就是未经对齐的图像后处理自然也错位。第三看后处理。YOLO 的不同版本后处理逻辑差异很大用 YOLOv5 的解析代码去解 YOLOv8 的输出精度肯定不对。确认模型的三个输出 feature map 尺寸和你的后处理代码里预设的 stride 是否匹配。6.3 显存占用和算力利用率异常很多人反馈显存占用上不去或者说算力利用率只有百分之二三十。这其实是推理场景里很正常的现象不全怪卡。显存上不去通常是因为数据投喂得太慢卡一直在空等。先看一眼 CPU 端的预处理是不是成了瓶颈把 DVPP 用起来把图片 resize 从 CPU 挪到硬件上。另外就是前面反复说的开 BatchSize 和多 stream只有卡上同时有大量计算任务在排队显存和算力才会被真正占满。还有一种情况是模型本身太小比如 YOLOv5n 这种轻量模型单帧计算量就很小算力再怎么压也跑不满。这种时候与其纠结利用率不如多开几路视频流把整卡的吞吐顶上去。我跑 YOLOv5n 的时候16 路视频并发卡上算力利用率能到 70% 以上吞吐比单路时高出十几倍。最后再分享一个我踩了好几次才记住的经验拿到 Atlas 300V 24G 之后不要一上来就上量化、多卡并发这些高级操作先把 BS1 的整条链路跑通确认 AIPP、模型转换、代码逻辑都没问题再逐步加 Batch、上 stream、上量化。每加一层优化就重新测一次精度和吞吐这样出了问题你永远知道是哪一步引入的。这个习惯比任何调优技巧都值钱。
返回列表