ARTICLE DETAIL

资讯详情

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

Atlas 300V 24G运行YOLO全攻略:从推理卡选型到模型部署实践

Atlas 300V 24G运行YOLO全攻略:从推理卡选型到模型部署实践 前几天一个同事突然发我一条消息Atlas 300V 24G 到底是不是运算加速卡他说他想把手头一套 YOLO 检测服务从 GPU 服务器迁到这台卡上网上搜了半天越看越糊涂。这个问题我确实被问过不止一次。Atlas 这个词在国内 AI 推理圈里代表了昇腾系列的一套硬件和工具链而 Atlas 300V 24G 正是其中非常典型的一块推理卡。它确实是运算加速卡但它的“运算”和普通 GPU 的“运算”并不是一回事。这篇文章我就从这块卡的定位说起完整讲清楚为什么有人能拿它在 Atlas 上顺利部署 YOLO以及整个过程中你会遇到的坑和可行的操作步骤。如果你正准备做推理硬件选型或者手上正好有一张 Atlas 300V 想跑 YOLOv5/YOLOv8这篇文章应该能让你少走很多弯路。1. Atlas 300V 24G 到底是一块什么卡1.1 它是运算加速卡但不是你想的那种运算加速卡Atlas 300V 24G 是华为昇腾系列里的一块 PCIe 接口的 AI 推理加速卡核心芯片是昇腾 310P。它做的是神经网络推理加速也就是你训练好一个模型之后把模型部署上去做前向计算。它不是一个通用 GPU你没法像 NVIDIA 显卡那样装个 CUDA 直接跑 PyTorch 训练脚本也不能拿来做图形渲染。你要把它理解成一个“专用计算单元”专门为 CNN、Transformer 这类深度学习模型的推理计算做了优化。这块卡名字里的 24G 是它板载的 LPDDR4X 内存也常被叫“显存”但准确说它和显卡的 GDDR 显存不是一回事。它更重要的一组参数是算力Atlas 300V 24G 板卡的 INT8 算力标称在 140 TOPS 左右FP16 算力大约 70 TFLOPS 上下功耗却只有 70 多瓦。这个能效比对很多场景来说非常关键。作为对比一块常见的中端 GPU 推理卡如果能跑到 60-70 TFLOPS FP16功耗通常已经在 200 瓦以上了。这就是为什么很多做边缘计算、视频结构化、安防监控的团队会考虑昇腾的卡。那它是运算加速卡吗是但严格说它是 AI 推理加速卡。它不擅长训练也不擅长跑通用计算它在“把训练好的模型高效地跑起来做推理”这件事上才是强项。理解到这一层你后面选型才不会犯方向性错误。1.2 24G 内存到底能装下多大模型很多人在选卡时只看内存觉得 24G 很大什么模型都能装。这个直觉对了一半。24G 内存确实能装下绝大多数常见目标检测模型YOLOv5s、YOLOv5m、YOLOv8s、YOLOv8m甚至更大的模型都没问题。你算一下就明白了以 YOLOv5s 为例FP16 精度输入 640×640模型权重加上中间激活值单路推理占用通常在 1GB 以内。即便开 batch8也就 4-6GB 的样子完全没压力。但 24G 不是给你随便浪费的。Atlas 300V 的 24G 内存在做高并发推理时非常有用尤其是在多路视频流场景。设想一个边缘盒子要跑 16 路 1080p 摄像头每路都需要 YOLO 做目标检测如果模型单实例占 1GB16 个实例就是 16GB24G 正好卡在线上。所以这块卡定位很明确要么跑大模型单路高精度要么跑小模型多路并发。这两种模式的内存管理策略是完全不同的后面我会展开讲。还要提醒一点Atlas 300V 和 Atlas 300V Pro 是不同型号SoC 版本可能不一样对应到模型转换时的--soc_version参数也不同。买卡之前先确认清楚具体型号最好用npu-smi info直接查不要只凭内存大小猜型号。2. 部署 YOLO 前必须先想清楚的三件事2.1 为什么不能像 GPU 那样直接跑如果你用惯了 NVIDIA 那套生态会觉得部署 YOLO 就是装个 PyTorch 然后加载权重文件跑 forward。但在昇腾上这条路走不通原因在于昇腾没有 CUDA。atlas 部署 yolo 的标准流程是先用 PyTorch 训练好模型或拿到权重导出成 ONNX 格式再通过 ATCAscend Tensor Compiler工具转换成昇腾的离线模型格式 .om最后用昇腾的推理接口去加载这个 .om 文件执行推理。为什么非要转一道因为昇腾的 NPU 不像 GPU 那样逐条执行 CUDA kernel它更倾向于把整个网络做图编译。ATC 在做转换时会把 ONNX 的算子映射到昇腾硬件支持的算子上然后做算子融合、内存复用、图优化。比如卷积后面跟 BatchNorm 和激活函数在 GPU 上可能需要分成多个 kernel 执行在昇腾上可以融合成一个算子减少数据搬运。这种静态图的优化方式是昇腾高能效的关键。这个流程听起来不复杂但实际做的时候有不少细节。最常见的坑就是模型在 GPU 上精度正常导成 ONNX 再转 om 后精度漂移了或者干脆某个算子不支持导致转换失败。所以“先转小模型验证链路”这个步骤一定不能跳过。2.2 你需要的核心组件驱动、固件、CANN要把 Atlas 300V 用起来你需要三样东西驱动、固件和 CANN 工具包。驱动负责让操作系统识别 NPU 设备固件负责 NPU 芯片的底层运行CANN 是昇腾的计算架构类似 CUDA 工具包。三者版本必须匹配否则会出现设备挂不上或者推理报错的情况。CANN 里面有几个东西你写代码时会接触到AscendCLACL底层 C/C 推理接口也提供 Python ACL 接口。ATC模型转换工具把 ONNX/Caffe/TensorFlow 模型转成 .om。AOE算子调优工具可以针对你的模型做算子级性能优化。AMCT模型量化工具做 INT8 量化校准用。对于部署 YOLO 来说最核心的是前两个。你不需要把 CANN 的所有模块都研究一遍但至少要知道每个工具是干什么的这样出了问题才知道去哪个日志里找线索。2.3 模型转换和后处理方案设计YOLO 这类检测模型一般包含两部分主干网络部分backbone neck head和 NMS 后处理部分。在昇腾上NMS 有两种做法。第一种是让模型只输出预测框和得分NMS 在 CPU 上用代码实现这是最稳妥最灵活的方式也是社区里最常见的做法。第二种是想办法把 NMS 也放进模型图里比如通过 ATC 的自定义算子接口插入 NMS这样做完推理直接出结果省掉大量 CPU 后处理时间但实现复杂度高很多调试难度也大。我的建议是第一次部署老老实实选第一种先跑通再说。NMS 放在 CPU 上做如果输入分辨率控制得当、单帧目标数不超过几百个CPU 后处理也就几毫秒完全不是瓶颈。等性能优化阶段再考虑要不要把 NMS 下沉到 NPU。3. 实操从 ONNX 到 .om 再到推理全套流程记录3.1 环境准备驱动、CANN 安装与验证在开始转换模型之前先把你手上的 Atlas 300V 插到服务器上装好驱动。安装完之后用npu-smi info验证设备状态。正常能看到板块信息、温度和内存使用情况类似下面这样npu-smi info然后安装 CANN 工具包。以 CANN 7.0 版本为例安装脚本通常是./Ascend-cann-toolkit_7.0.0_linux-x86_64.run --install安装完记得 source 一下环境变量文件否则命令找不到source /usr/local/Ascend/ascend-toolkit/set_env.sh验证 ATC 是否可用atc --version这一步没问题说明环境基本就绪。3.2 导出 ONNX以 YOLOv5 为例如果你用的是 YOLOv5 官方仓库导出 ONNX 其实很方便。在仓库根目录执行python export.py --weights yolov5s.pt --include onnx --opset 11 --img 640 --batch 1几个值得注意的点--opset 11是昇腾支持得比较好的 ONNX 算子集版本不建议盲目用 opset 17 之类更高的版本部分算子转换会失败。--batch 1导出的是静态 batch 模型。如果你后面要动态 batch需要加--dynamic参数但动态输入在昇腾上的支持和优化程度不如静态输入建议先用固定的 batch1 跑通。导出的 ONNX 文件可以用onnx-simplifier简化一下去掉一些冗余的算子比如python -m onnxsim yolov5s.onnx yolov5s_sim.onnx。但要注意简化后一定要重新验证精度有些简化器会改变模型行为。导出完成后用netron打开看一眼图结构确认输入节点名、输出节点名。YOLOv5 导出的 ONNX 输入节点名通常是images输出节点名是output0或类似的名字。这个信息后面 ATC 转换时要用务必记下来。3.3 ATC 转换核心参数的详细对照得到 ONNX 之后执行 ATC 转换。下面这条命令是我实际用过的完整版本atc --modelyolov5s_sim.onnx \ --framework5 \ --outputyolov5s_bs1 \ --soc_versionAscend310P3 \ --input_shapeimages:1,3,640,640 \ --insert_op_confaipp.cfg \ --output_typeFP16 \ --loginfo每个参数是什么意思我列一个表参数作用备注--model输入的 ONNX 文件路径--framework源框架类型5 表示 ONNXCaffe 是 0TensorFlow 是 1有种说法是 5 对应 ONNX--output输出 .om 文件路径前缀注意不要写 .om 后缀工具会自动加--soc_version芯片型号规格常见是 Ascend310P3具体以你卡为准--input_shape指定输入维度名字必须和 ONNX 图中输入节点名一致--insert_op_conf插入预处理算子配置主要用来做图像缩放、归一化等--output_type指定网络输出数据类型可减少输出拷贝开销--log日志级别转换报错时调成 debug 可以查看更多信息这里重点说明--soc_version。你可以先用下面命令查设备支持的 SoC 版本或者直接根据卡型号推断。Atlas 300V 常见是 Ascend310P3如果你不确定在跑 ATC 之前先在环境里执行npu-smi info然后看芯片型号那一行再对应到Ascend310P1、Ascend310P3等。--input_shape里的images:1,3,640,640含义是 batch13 通道高 640宽 640。这里有一个非常容易踩的坑YOLOv5 在 PyTorch 里输入的 tensor 顺序是 NCHW即 batch、channel、height、width所以写1,3,640,640是对的。如果你模型训练时用的是 NHWC那这里就得对应调整不然后面推理结果全乱。--insert_op_conf对应的 AIPP 配置文件作用是把图像的预处理操作下沉到 NPU 上。比如输入图像需要 resize 到 640x640需要做归一化都可以在 aipp.cfg 里配置。我的配置如下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: false mean_chn_0: 0 mean_chn_1: 0 mean_chn_2: 0 min_chn_0: 0.003921568627 min_chn_1: 0.003921568627 min_chn_2: 0.003921568627 }这段配置的意思是输入图像是 RGB 格式每通道除以 255 做归一化均值为 0。如果你的 YOLO 模型在训练时用了 ImageNet 均值方差比如 mean[0.485, 0.456, 0.406]std[0.229, 0.224, 0.225]那就得把 mean_chn_x 和 min_chn_x 改掉。换算方法是min_chn 1 / (255 * std)mean_chn对应减去的均值。很多人在这一步偷懒导致精度不对这里要格外上心。转换成功后会生成一个yolov5s_bs1.om文件。这时候可以再用一个工具检查一下 om 文件信息omg --info --modelyolov5s_bs1.om不过这个工具不是每个版本都有没有就用别的方式验证比如直接用下面的推理代码试跑。3.4 推理代码基于 Python ACL 的最小实现有了 .om 文件接下来就是写推理代码。昇腾的 Python ACLpyACL接口可以直接加载 .om 做推理。下面是最简版本import numpy as np import acl # 初始化 acl.init() acl.rt.set_device(0) context, ret acl.rt.create_context(0) # 加载模型 model_id, ret acl.mdl.load_from_file(yolov5s_bs1.om) # 获取输入输出尺寸 input_size acl.mdl.get_input_size_by_index(model_id, 0) output_size acl.mdl.get_output_size_by_index(model_id, 0) # 分配输入输出内存 input_ptr acl.rt.malloc(input_size, 2) output_ptr acl.rt.malloc(output_size, 2) # 构造输入数据假设 image 是预处理后的 640x640x3 数据 input_data image.astype(np.float16).flatten() acl.rt.memcpy(input_ptr, input_size, input_data.ctypes.data, input_size, 3) # 推理 acl.mdl.execute(model_id, [input_ptr], [output_ptr]) # 取出输出 output_data np.zeros(output_size, dtypenp.uint8) acl.rt.memcpy(output_data.ctypes.data, output_size, output_ptr, output_size, 2) output np.frombuffer(output_data, dtypenp.float16).reshape((1, 25200, 85)) # 清理资源 acl.rt.free(input_ptr) acl.rt.free(output_ptr) acl.mdl.unload(model_id) acl.rt.destroy_context(context) acl.rt.reset_device(0) acl.finalize()这段代码只是把推理主流程串起来了其中有几个点必须注意。第一acl.mdl.execute是同步接口会阻塞到推理完成。如果要做多路并发需要使用acl.mdl.execute_async配合 stream或者起多个线程每个线程创建独立 context各自加载一份模型实例。昇腾的卡和 GPU 不太一样它不是靠单卡大显存吃下所有并发而是每个线程/进程持有自己的模型实例和内存空间所以多路视频流最稳妥的方式是多进程。每个进程绑定一张卡的某个设备互不干扰调起来也容易排查问题。第二YOLOv5 的原始输出形状是[1, 25200, 85]其中 25200 3 个检测头 ×80×80 40×40 20×20个 anchor85 4 个框坐标cx, cy, w, h 1 个置信度 80 个类别分数。如果你的 YOLOv5 版本检测框的坐标是 xywh 格式后处理解码时要手动转成 xyxy。第三输出数据类型。默认情况下ATC 转换后的输出可能是 FP32 或 FP16。我这里设置--output_typeFP16所以代码里读出来是 float16。如果你没设置可能读出来是 float32这个取决于你转换时的参数务必保持一致否则你取出来的数值全是乱的。看一下完整的后处理核心逻辑def postprocess(output, conf_threshold0.25, iou_threshold0.45): output output[0] # [25200, 85] boxes output[:, :4] scores output[:, 4:5] * output[:, 5:] # 类别的最终置信度 class_ids scores.argmax(axis1) confs scores.max(axis1) mask confs conf_threshold boxes boxes[mask] class_ids class_ids[mask] confs confs[mask] # xywh - xyxy boxes[:, 0] boxes[:, 0] - boxes[:, 2] / 2 boxes[:, 1] boxes[:, 1] - boxes[:, 3] / 2 boxes[:, 2] boxes[:, 0] boxes[:, 2] boxes[:, 3] boxes[:, 1] boxes[:, 3] # NMS可以用 PyTorch 自带的 torchvision.ops.nms或者自己实现 keep torchvision.ops.nms(torch.from_numpy(boxes), torch.from_numpy(confs), iou_threshold) return boxes[keep.numpy()], confs[keep.numpy()], class_ids[keep.numpy()]预处理部分我通常不建议用 AIPP 做完整的 letterbox因为 letterbox 涉及比例计算和填充在 AIPP 里配起来比较绕。更稳的做法是在 CPU 上用 OpenCV 把图像 resize 到 640x640注意保持长宽比填充灰色然后把 HWC 转成 CHW再转成 FP16 喂给模型。这样出问题的概率小也便于排查。3.5 实际性能表现参考我自己实测下来Atlas 300V 24G 跑 YOLOv5s输入 640x640FP16 精度batch1 单线程大约能跑到 300-500 FPS 的量级如果做 INT8 量化并开多线程能到 700-1000 FPS。这个数据只代表我手上这套环境和软件版本不同 CANN 版本、不同驱动固件组合、不同输入分辨率结果可能差不少。但有一点是确定的Atlas 300V 在跑 YOLO 系列这种中小型检测模型时能效比确实非常能打。如果你看到某个模型在转换后推理速度明显低于预期先不要怀疑硬件优先查这几个地方模型是否做了算子融合优化、输入是否过大、是否有不必要的 H2D/D2H 拷贝、CPU 后处理是否成了瓶颈。4. 常见问题与排查技巧实录4.1 模型转换时报算子不支持这是 atlas 部署 yolo 遇到频率最高的问题。YOLOv8 用的是 C2f 模块里面有大量的 split、concat 操作YOLOv5 的 focus 模块里也涉及 slice 操作这些在 ONNX 导出后很可能会映射成一些昇腾暂时不支持的算子。解决办法有几个方向升级 CANN 到新版本新版本通常会补齐更多算子支持。换导出的 opset 版本比如从 11 改成 13 再试一次。调整模型结构把不支持的算子替换成等价结构比如 focus 模块可以重写成普通卷积加 slice。在 ATC 转换时加--enable_small_channel之类的高级参数但是这种参数需要具体问题具体分析不建议乱加。最直接的办法还是看日志。ATC 转换失败时会在日志里明确写出是哪个算子不支持你根据算子名去查昇腾的算子支持列表基本能找到对应的替代方案。4.2 推理时报内存分配失败排除掉真的内存不够的情况后大概率是因为显存碎片化或者上一个模型没有释放干净。昇腾的显存管理比较特殊如果在同一个进程里反复加载/卸载模型中间产生的碎片会堆积最后导致大块内存分配失败。最简单粗暴的解决方法是把模型加载和卸载放到子进程里做父进程负责调度避免碎片累积。另一个方法是加大ACL_MEMORY_MALLOC_NORMAL_ONLY相关配置但这不是长久之计。如果跑的是多路视频流每路一个进程每个进程又加载一份模型那 24G 内存很快会被占满。这时候需要评估模型实例的数量和 batch 大小。比如 24G 内存、每个模型实例占 1.5GB最多也就 16 个实例再多就会 OOM。合理做法是让多个线程共享同一个模型实例但输入输出 buffer 用不同的地址这样内存占用不会随并发数线性增长。4.3 推理精度明显不对遇到这个问题先从头排查你的图像预处理而不是怀疑模型转换。主要检查三处输入数据的 shape 是不是 NCHW很多从 GPU 转过来的人习惯 NHWC直接导致结果全乱。BGR 和 RGB 的通道顺序YOLOv5 在 OpenCV 里默认读进来是 BGR而模型训练时如果用 PIL 转的就是 RGB。必须在喂给模型前保持一致。归一化方式AIPP 里配置的 mean 和 std 是否和训练时保持一致是否除了 255是否忘了减均值。这一步错了模型输出的置信度会异常地高或异常地低。如果预处理没问题再检查后处理。YOLOv5 的一处经典坑是输出的 85 个维度中第 0-3 个是坐标第 4 个是 objectness置信度第 5-84 是 80 个类别分。最终置信度是 objectness 乘以类别分最大值。如果你直接拿类别分最大值当置信度检测结果也会看起来很奇怪。4.4 多路视频流性能上不去很多人把视频流并发的性能问题归咎于 NPU算力不够其实很多时候是线程模型设计有问题。在昇腾上做多路推理正确做法是进程内创建多个线程每个线程绑定不同的设备上下文使用acl.mdl.execute_async异步提交任务再用 event 或 callback 同步结果。线程数并不是越多越好通常和 NPU 的 AI Core 数量有关调到 2-4 路之后收益就会明显减小。另一个隐藏性能瓶颈是数据拷贝。图像从内存拷到 NPU推理完再拷回来这个 H2D/D2H 的开销在一些小模型上能占到总耗时的一半以上。优化思路有两个一是用 CANN 提供的内存池接口重复利用同一块显存二是使用 AIPP 做预处理省掉 CPU 上的颜色空间转换和缩放操作让数据在 NPU 内部完成。实测下来后者对性能提升非常明显。常见问题典型表现推荐解决方式算子不支持转换失败日志提示某算子升 CANN、换 opset、替换模型结构内存分配失败推理时报 OOM 或 malloc 失败优化模型实例数、检查碎片、子进程管理精度异常检测框错乱或置信度全为 0/1检查 NCHW、RGB/BGR、归一化参数吞吐低多路并发后 FPS 上不去异步接口 多线程 AIPP 下沉预处理单算子性能差某个算子耗时异常高用 AOE 算子调优工具--job_type15. 关于性能优化我再多啰嗦两句模型能跑通只是第一步真正要把 Atlas 300V 的性能吃透有几个优化手段需要掌握。第一个是 AOE 工具。ATC 转换时加上--aoeTrue或者单独执行 AOE 调优工具会针对你的模型做算子级自动调优选择最优的 tiling 方案。这个优化在第一次转换时可能要多花几分钟时间但对最终的推理性能有实实在在的提升。我见过同一个模型开了 AOE 和没开端到端延迟差了 20%。第二个是熟悉昇腾的融合规则。比如 Conv BN ReLU 这种组合在 ATC 转换时通常会自动融合但有些自定义激活函数或者特殊结构的模型融合可能失败。你可以通过--logdebug查看转换日志确认哪些算子被融合了、哪些没有。如果发现大量算子没融合可以考虑改模型结构尽量用小算子组合代替复杂自定义层这会给编译器更多优化空间。第三个是注意 CPU 后处理的时间。YOLO 的 NMS 和 decode 如果在 CPU 上实现单帧耗时一般可以控制在 2-5ms这不会成为瓶颈。但如果你的输入分辨率很高比如 1280x1280或者单帧目标数特别多后处理就可能到 10ms 以上。这时就要考虑把后处理放到模型图里或者在多核 CPU 上并行处理多帧的 NMS。最后分享一个小技巧昇腾的日志和 GPU 的日志风格差异很大很多在 NVIDIA 上调参的经验在昇腾上不适用。遇到问题先去看/root/ascend/log下的日志文件根据日志级别的 ERROR 日志去反查。只要能定位到具体算子和具体报错码大部分问题都能搜到答案。不要一上来就重装驱动大多数部署问题都是模型结构、参数配置和版本匹配问题不是硬件问题。Atlas 300V 这块卡我自己实际用了大半年从最初的一头雾水到后来能稳定跑多个 YOLO 模型中间确实踩了不少坑。但这套工具链一旦跑通你会觉得它其实很稳定、很可靠适合长期放在机房里面做生产推理。希望这篇文章能帮你把 atlas 部署 yolo 这条路线走顺。
返回列表