☰
Atlas 300V 24G推理卡部署YOLOv5实战:从环境配置到性能调优
2026/9/25 6:52:23 网站建设 项目流程

最近好多人在问 Atlas 300V 24G 是不是一张“运算加速卡”,还有人问我能不能拿它来训练 YOLO。这个问题的答案其实就一句话:它是推理加速卡,不是训练卡,但搞定 YOLO 目标检测的线上部署,它确实是一把好手。我去年在 Atlas 300V 24G 上完整部署过 YOLOv5s,从驱动安装到模型转换再到推理优化,整个链路走下来踩了不少坑,这篇文章就把这段经历完整写出来,既是回答那个高频问题,也顺带整理一份在昇腾推理卡上跑 YOLO 的可参考作业。

1. Atlas 300V 24G 到底是一张什么卡

先把这个最基础的问题掰开说清楚。很多刚接触昇腾生态的同学,看到 24G 显存,第一反应是“这卡显存比不少训练卡还大,是不是能直接拿来做训练”。实际上,Atlas 300V 24G 的定位非常明确,它面向的是 AI 推理场景,主要解决的是模型训练完成后,在业务侧如何又快又省地把推理跑起来的问题。

1.1 24G 显存很大,但为什么训练任务建议别指望它

训练和推理对硬件的要求其实不太一样。训练需要高精度计算,梯度反传、参数更新这些环节对精度损失非常敏感,所以训练卡一般会做强 FP16、BF16 甚至 FP32 的算力,同时还要有比较大的带宽来支撑频繁的权重更新。推理则不同,模型结构固定,参数已经训练好了,核心只要把前向计算跑得快、跑得稳,很多时候用 FP16 甚至 INT8 精度就够了。

Atlas 300V 24G 的强项恰恰就在这里。它支持 FP16、INT8 这样的低精度推理,单卡算力主要用于前向运算,拿来跑 YOLO 这种目标检测模型,性能释放非常可观。但你要是想用它做完整的模型训练,就会遇到几个现实问题:一是它对高精度训练场景的算力支持不像训练卡那样充裕;二是昇腾的 CANN 框架虽然支持训练,但生态上训练需求一般都会上专门的训练卡或者训练集群,拿推理卡硬顶训练任务,性价比不高,也容易踩各种软件兼容性的坑。

所以我对那个热搜问题的回答是:你要问它是不是运算加速卡,是,但它加速的是推理运算,不是训练运算。这个定位区别搞清楚了,后面选型、做方案就不会跑偏。

1.2 Atlas 300V 24G 适合用在哪类场景

我自己用下来的感觉是,Atlas 300V 24G 最舒服的场景就是视频流目标检测和图像批量分析。24G 显存意味着它能同时装下较大的模型,也能通过多 batch 或多路并发的方式跑更多路视频流,这对智慧园区、工业质检、安防监控这类需要同时对多路视频做实时分析的项目来说,非常合适。

另外它对 YOLO 系列的支持比较成熟。YOLOv5、YOLOv8 这类模型先导出成 ONNX,再通过 ATC 工具转成昇腾的 OM 格式,就能正常跑起来。官方文档里也提供了不少参考样例,尤其适合那种“我手里有一批 Jetson 或者其他 GPU 设备上的 YOLO 项目,想迁移到昇腾平台”的存量项目。成本上,单张 Atlas 300V 24G 比同显存大小的训练卡便宜不少,用来做推理侧部署,单位成本能打下来一截。

2. 部署前环境准备:驱动、固件与 CANN 安装

说句大实话,昇腾这套环境在安装阶段劝退过不少人。它和 GPU 平台不太一样,不是简单装个驱动就能识别设备,总共分好几层:底层有 NPU 驱动和固件,上层有 CANN 工具包,再往上才是你写的推理代码。任何一层版本没对齐,后边跑起来都是各种莫名其妙的报错。

2.1 硬件识别与驱动固件安装

拿到 Atlas 300V 24G 之后,第一步是确认系统有没有识别到这张卡。在 x86 服务器上插好卡后,可以用 lspci 检查:

lspci | grep -i ascend

如果能看到类似Processing accelerators: Huawei Technologies Co., Ltd. ...的输出,说明硬件层面已经识别到了。接着查看系统架构,注意昇腾的驱动包分 x86_64 和 aarch64 两种,别下错:

uname -m

然后去昇腾官网下载对应架构的 HDK(包含驱动和固件),安装顺序上是先装固件再装驱动,或者直接按照官方安装脚本走一遍。我之前踩过的坑是跳过固件直接装驱动,结果 npu-smi info 能显示卡,但一调用推理接口就报设备初始化失败。后来重新走完固件安装流程,问题就消失了。安装完成后,验证一下 NPU 状态:

npu-smi info

正常情况下能看到卡的温度、功耗、显存占用,还能看到芯片型号,比如 Ascend 310P3 这类字样。这一步的信息很重要,因为后面模型转换时需要根据这个芯片型号指定 soc_version,这和 CUDA 里根据显卡算力设置 -arch 是一个道理。如果你的 npu-smi 看不到信息,先检查驱动和固件版本是否匹配,再看内核模块有没有加载,尽量不要在设备状态没确认的情况下继续往下走。

2.2 CANN Toolkit 安装与环境变量配置

CANN 是昇腾的异构计算架构,类比一下就是 CUDA 工具包。部署 YOLO 用到的主要是 ATC 模型转换工具和 ACL(AscendCL)推理库。

安装 CANN Toolkit 时建议直接装最新稳定版,因为版本越老,算子支持越少,模型转换报“不支持算子”的概率越高。安装包是自解压格式,下载后赋执行权限运行即可:

chmod +x Ascend-cann-toolkit_8.0.rc1_linux-x86_64.run ./Ascend-cann-toolkit_8.0.rc1_linux-x86_64.run --install

装完以后,需要把环境变量加到 shell 配置里:

source /usr/local/Ascend/ascend-toolkit/set_env.sh

建议把这句话写进 /etc/profile 或者 ~/.bashrc,避免每次开终端都要手动 source。有一个很容易被忽略的小点:CANN 的环境变量里包含了对 Python 路径的设置,如果你的机器上有多个 Python 环境,比如系统 Python 和 conda 虚拟环境,最好在 source 之后确认当前 which python 指向的那个路径里能正常 import acl,否则后边跑推理代码时会遇到模块找不到的情况。

2.3 用 npu-smi 确认卡状态

环境装好后,我习惯把系统重启一次,然后再跑一遍 npu-smi info。这一步主要是确认驱动和固件在开机自检阶段就被正常加载。重启之后如果 npu-smi info 正常显示,再看几个关键指标:

  • 芯片温度是否在合理范围
  • 24G 显存是否有异常占用
  • 当前功率是否处于正常待机状态

注意这里有个小问题,新卡插上后显存占用可能不是 0,因为系统本身会预留一部分。看到几百兆占用是正常的,但如果一开机就用掉好几 G,那就要查一下是不是有残留进程占着设备。

3. 把 YOLO 模型改造成昇腾能吃的格式:ONNX 转 OM

昇腾 NPU 不像 GPU 那样直接加载 PyTorch 权重跑推理,它需要一种专门的离线模型格式,叫 OM。简单理解,OM 就是把模型结构、算子、权重和底层调度信息全部打包成一个文件,推理时 NPU 直接按这个文件执行,省掉了运行时再做算子调度和内存规划的开销。这个转换过程在昇腾里叫 ATC 模型转换,是整条部署链路的核心环节。

3.1 先导出 ONNX:YOLOv5 的导出细节

我这次用的是 YOLOv5s,先从 PyTorch 权重导出 ONNX。YOLOv5 官方仓库里自带导出脚本:

python export.py --weights yolov5s.pt --include onnx --opset 11 --batch-size 1

有几个参数要特别留意。opset 版本建议用 11 左右,太低有些算子导不出来,太高昇腾的 ATC 工具可能还没完全适配。batch-size 这里我一开始设成 1,先把链路跑通,后边做性能优化时再改成 4 或者 8 转一个多 batch 的模型。还有一个隐藏问题,YOLOv5 的检测头里有比较多的小算子,导出的 ONNX 图会比较大,算子数量多,转换时耗时会长一些,这都正常。

导出完成后,可以用 Netron 打开 ONNX 文件看一遍模型结构,重点确认输入节点的名字和形状。我见过不少同学把输入节点名记错,导致 ATC 转换时指定 --input_shape 对不上,报错让人摸不着头脑。YOLOv5 导出的输入节点名一般是 images,形状是 [1, 3, 640, 640]。

3.2 ATC 转换命令与参数选择

ATC 工具在 CANN 安装目录下,路径类似:

/usr/local/Ascend/ascend-toolkit/latest/bin/atc

针对 YOLOv5s,我的转换命令是这样:

atc --model=yolov5s.onnx \ --framework=5 \ --output=yolov5s_ascend \ --input_format=NCHW \ --input_shape="images:1,3,640,640" \ --soc_version=Ascend310P3 \ --insert_op_conf=aipp.cfg \ --output_type=FP32 \ --log=info

逐个参数说下含义。--framework=5 表示输入模型是 ONNX,这个数字是固定约定。--soc_version 就是前面提到的重要参数,它要和你 npu-smi info 里看到的芯片型号对应起来,比如昇腾 310P 系列对应 Ascend310P3,具体值以官方兼容列表为准,填错了转换大概率失败或者推理行为异常。--insert_op_conf 是用来插入 AIPP 预处理配置的,这个非常重要,我单独拿出来讲。

转换过程会在控制台打印一大段日志,看到ATC run success就说明成功了,这时候目录下会出现一个 yolov5s_ascend.om 文件。如果过程中报算子不支持的错误,先不要急着换模型,把 --log 调到 debug,看具体是哪个算子卡住,再决定是升级 CANN 版本还是修改模型结构。

3.3 AIPP 配置:把图像预处理下沉到硬件

AIPP 是昇腾推理卡的一个特色功能,它允许你把图像缩放、通道转换、归一化这些预处理操作从 CPU/GPU 搬到 NPU 上执行。在 GPU 平台上,图像预处理一般是在 PyTorch 里用 torchvision 做,或者用 OpenCV 自己写;在昇腾上,通过配置 AIPP,推理接口一调用,硬件自动完成从原始图像到模型输入的转换,少了一层 Host 和 Device 之间的数据来回拷贝,实测能省下不少耗时。

我的 aipp.cfg 配置如下:

aipp_op { aipp_mode: static input_format: RGB888_U8 src_image_size_w: 640 src_image_size_h: 640 csc_switch: true rbuv_swap_switch: false min_chn_0: 0 min_chn_1: 0 min_chn_2: 0 var_reci_chn_0: 0.003921568627451 var_reci_chn_1: 0.003921568627451 var_reci_chn_2: 0.003921568627451 }

解释一下几个关键项。input_format 表示输入图像是 RGB888 格式,也就是常见的 8 位三通道彩色图。csc_switch 控制是否做颜色空间转换,YOLOv5 训练时一般用 RGB 顺序,但如果你的图像读取流程是 OpenCV 的 BGR,就需要在主机端先把通道顺序调整对,或者通过 rbuv_swap_switch 让硬件做 R/B 通道交换。min_chn_x 和 var_reci_chn_x 就是归一化的减均值、乘方差的硬件化实现,0.003921568627451 这个数就是 1/255,对应把 0-255 的像素值缩放到 0-1 之间。

这里有一个很容易踩的坑:如果模型转换时用了 AIPP,推理时的输入就不再是模型定义的 [1, 3, 640, 640] 浮点张量,而是一张原始图像数据。输入数据的通道排列、尺寸都要按 AIPP 配置来,否则推理结果完全不对。我第一次跑通时在这里耗了不少时间,最后发现是图像尺寸没做 letterbox,直接 resize 导致目标变形,检测精度崩了。

4. 推理代码实现:ACL Python 接口实战

模型转换成功只是第一步,接下来要写代码调用 OM 模型执行推理。昇腾提供了 ACL(AscendCL)编程接口,支持 C++ 和 Python。Python 接口对快速验证非常友好,性能上虽然比 C++ 有一点开销,但做业务系统已经够用了。

4.1 ACL 初始化和模型加载

ACL 的 Python API 使用前要做一系列初始化,流程和写 CUDA 代码有点像。核心步骤如下:

import acl ret = acl.init() assert ret == 0, "ACL init failed" ret = acl.rt.set_device(0) assert ret == 0, "set device failed" # 加载 OM 模型 model_path = b"yolov5s_ascend.om" model_id, ret = acl.mdl.load_from_file(model_path) assert ret == 0, "load model failed" # 创建模型描述对象,用于获取输入输出信息 model_desc = acl.mdl.create_desc() ret = acl.mdl.get_desc(model_desc, model_id) assert ret == 0, "get model desc failed" # 获取模型输入输出的大小 input_size = acl.mdl.get_input_size_by_index(model_desc, 0) output_size = acl.mdl.get_output_size_by_index(model_desc, 0)

这里有个小提示,acl.init 只需要调用一次,但整个进程里如果有多个地方都要做推理,最好把初始化逻辑放在入口处统一处理,不要每个线程各自 init,容易引发资源冲突。set_device 的编号要和实际插卡位置对应,设备编号从 0 开始,如果机器上有多张卡,可以按需求选择。

4.2 执行推理的输入输出处理

加载完模型,接下来就是准备输入数据并执行推理。因为前面用了 AIPP 做预处理,这里直接喂原始图像字节流即可,不用再手动做归一化。但要注意,喂给模型的“原始图像”必须经过 letterbox 处理成 640x640,并且保持 RGB 三通道排列。

ACL 的执行流程需要把数据放到 device 内存上,然后通过 data buffer 传给执行接口:

import numpy as np from ctypes import addressof, c_int # 假设 img 是已经 letterbox 并转成 RGB 的 640x640x3 图像数据 img = img.astype(np.uint8) input_data = img.tobytes() # 申请 device 内存并拷贝输入数据 input_ptr = acl.rt.malloc(input_size, 2) # 第二个参数是内存对齐要求,一般传 2 ret = acl.rt.memcpy(input_ptr, input_size, addressof(c_int.from_buffer_copy(input_data)), input_size, 1) # 创建输入输出 dataset input_dataset = acl.mdl.create_dataset() input_data_buffer = acl.create_data_buffer(input_ptr, input_size) acl.mdl.add_dataset_buffer(input_dataset, input_data_buffer) output_ptr = acl.rt.malloc(output_size, 2) output_dataset = acl.mdl.create_dataset() output_data_buffer = acl.create_data_buffer(output_ptr, output_size) acl.mdl.add_dataset_buffer(output_dataset, output_data_buffer) # 同步执行推理 ret = acl.mdl.execute(model_id, input_dataset, output_dataset) assert ret == 0, "model execute failed"

这里要注意 acl.rt.memcpy 的最后一个参数是复制类型,1 表示 H2D(主机到设备),2 表示 D2H。推理执行完,输出数据还在 device 内存上,需要拷回主机端才能解析:

output_data = output_ptr output_np = np.zeros(output_size, dtype=np.uint8) acl.rt.memcpy(output_np.__array_interface__['data'][0], output_size, output_ptr, output_size, 2)

对这个 YOLOv5s 模型,输出张量形状是 [1, 25200, 85],25200 是 640x640 输入下三个尺度特征图的总预测框数,85 是 4 个坐标值 + 1 个置信度 + 80 个类别数。把字节流转成 float32 numpy 数组后,按这个形状 reshape,后处理就能按熟悉的方式做了。

4.3 后处理:从输出张量到检测框

后处理逻辑和 GPU 上跑 YOLO 几乎一样,只是数据来源换成了 NPU 的输出。先把 25200 个候选框按置信度阈值过滤掉大部分低质量框,再做 NMS 去除重叠框,最后把归一化的坐标映射回原图尺寸。

有个环节要特别提醒:因为预处理时做了 letterbox,原图的尺寸和 640x640 之间有一个缩放比例和 padding 偏移。后处理映射坐标时,必须先把模型输出的坐标减去 padding 偏移量,再除以缩放比例,才能得到原图上的正确坐标。我见过不少同学直接在原图上画框结果框的位置整体偏移,问题就出在这里。

NMS 可以直接用 numpy 手写一个,也可以用 PyTorch 的 torchvision.ops.nms 做,但要注意如果后处理想在 CPU 上跑,数据流转会有开销。如果对性能要求高,可以考虑把后处理也搬到昇腾上用算子实现,或者用 MindX SDK 自带的模型后处理插件,能省不少工作量。我前期调试时图省事就用 numpy 版 NMS,线上系统再针对性地优化。

5. 性能调优与常见问题排查

模型能跑出正确结果之后,接下来就是压性能、抠细节。我整理了几个实际部署中容易踩的坑和调优方向,这些都是官方文档里写得不细、但实战里非常关键的内容。

5.1 静态 Batch 和多 Stream 并发

第一个调优点是把模型转成静态多 batch。前面转换时我指定的是--input_shape="images:1,3,640,640",这是单 batch 模型,推理时一次只处理一张图。如果业务场景里有大量图片要检测,建议转成 4 或 8 batch 的模型,一次推理同时处理多张图,NPU 利用率会有明显提升。

转多 batch 模型很简单,转换命令里改一下 input_shape:

atc --model=yolov5s.onnx \ --framework=5 \ --output=yolov5s_bs4 \ --input_format=NCHW \ --input_shape="images:4,3,640,640" \ --soc_version=Ascend310P3 \ --insert_op_conf=aipp.cfg \ --output_type=FP32

推理时,把 4 张图的预处理结果拼成一个 [4, 3, 640, 640] 的输入数据,模型输出就是 [4, 25200, 85],后处理时逐张解析即可。多 batch 的代价是最小 batch 限制,如果凑不够 4 张图,就得做 padding 或者等待,所以实际使用时要根据业务流量合理选择 batch 大小。

第二个调优点是多 Stream 并发。ACL 里 stream 的概念和 CUDA stream 非常像,不同 stream 上的推理任务可以并发执行。如果你的服务是多线程模型,每个线程可以创建独立的 stream,然后把推理请求分发到不同 stream 上,这样能利用 NPU 的并行调度能力,提高整体吞吐。单 stream 下的大 batch 和 多 stream 下的小 batch 各有优势,我建议先跑一遍基准测试,再结合业务调。

5.2 部署中遇到的几个典型坑

我一共整理出 5 个最容易出现的问题,基本都是我从踩坑现场捞出来的经验。

第一个问题是模型转换时报算子不支持。这个最多见。解决办法是首先看日志里具体哪个算子不识别,然后查官方算子清单,如果确认当前 CANN 版本不支持,要么升级 CANN,要么修改模型结构替换掉对应算子。我之前遇到过一个动态 shape 相关的算子,改成静态 shape 后顺利通过。

第二个问题是推理结果全为 0 或者完全不对。八成是 AIPP 配置问题。先检查图像通道顺序和 AIPP 的 input_format 是否一致,再看是否需要做颜色空间转换。如果用了 AIPP,主机端不要再做归一化,否则等于做了两遍预处理,模型输入全乱了。

第三个问题是时延不符合预期。如果单帧时延比预期高很多,先确认模型是不是不小心转成了动态 shape,再看输入数据是否频繁在内存和显存之间拷贝。尽量一次推理塞满 batch,减少 execute 调用次数。还有注意热启动和冷启动的差别,第一次推理时模型初始化和内存分配会带来额外开销,压测时一定要先跑几轮预热再统计时延。

第四个问题是显存不够用。Atlas 300V 虽然有 24G 显存,但如果同时加载多个模型,或者开了太多路并发,还是会出现显存告警。用 npu-smi info 监控显存占用,及时释放不再使用的模型和中间 buffer。ACL 提供了模型句柄销毁接口,用完了就释放,不要挂在全局变量里不管。

第五个问题是多路视频流场景下 CPU 占用飙高。很多人以为推理都放 NPU 了 CPU 就没活了,实际上图像解码、缩放、letterbox 这些预处理如果全在 CPU 上做,照样能吃掉好几个核。这种场景建议走昇腾的 DVPP 硬件解码和图像处理接口,把解码和缩放也下沉到硬件。如果用的 MindX SDK,可以直接用它的视频解码插件,比自己用 OpenCV 硬顶强很多。

5.3 实测性能参考与部署建议

最后分享一组我在项目里的实测数据,给大家一个参考。CPU 是 Intel Xeon 银牌系列,Atlas 300V 24G,输入 640x640,YOLOv5s 模型,单 batch 推理的端到端耗时大概在 10 毫秒级别,连续推理时 NPU 利用率能稳定在较高水位。在这个吞吐下,单卡同时跑 4 路 1080P 视频流做实时检测,每路保持 25FPS 没有任何压力。

如果业务要求更高并发,我的建议是优先用多 batch 加多 stream 的组合,先把 NPU 利用率拉满。24G 显存留给模型的占用其实不大,YOLOv5s 的 OM 模型可能才几百兆,剩余显存都够跑多路视频了。

6. 和 GPU 平台相比,昇腾部署有哪些体验差异

文章最后再聊点实际体验层面的差异,毕竟很多人是从 CUDA 平台切过来的。在 GPU 上跑 YOLO,流程一般是 PyTorch 训练完然后直接用 torch.hub 或者 Triton 部署,工具链比较统一。昇腾这边则需要额外多走一步 ONNX 转 OM,并且要理解 AIPP、soc_version 这些专属概念,上手成本确实高一些。

但用了一段时间之后,我觉得这套设计也有它的逻辑。OM 模型把算子调度、内存复用这些事在转换阶段就固定下来了,推理时少了很多运行时开销,对生产环境来说反而更稳。而 AIPP 把预处理下沉到硬件,对视频流这种高吞吐场景帮助非常大。一旦把这条链路走通,后续不管是换模型还是换设备,复用性都很高。

最后再分享一个小技巧,如果在部署调试中遇到很奇怪的报错,先别急着改代码,优先把 CANN 版本、驱动版本、固件版本三者拉齐。昇腾这套东西对版本一致性要求很高,我遇到过很多次问题都是因为某个组件版本不匹配导致的,升级对齐之后就自然消失了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询