一个半月前,我接了个活儿:把一个跑在GPU上的YOLOv5s检测服务,迁移到一张Atlas 300V 24G推理卡上。当时团队里第一反应是——“atlas不是地图数据库吗?”第二反应是——“300V 24G这玩意到底算不算运算加速卡?”老实说,我拿到卡之前也只在文档里见过它,真正上手之后才发现,这卡和常见的GPU推理卡在使用逻辑上差得不是一星半点。
这篇就把我用Atlas 300V部署YOLO的完整过程、关键参数和踩过的坑写出来,给想用昇腾推理卡跑目标检测、又没什么经验的人一个参考。内容覆盖选型对比、环境搭建、模型转换、ACL推理、性能调优这几个环节,顺便把“它到底是不是运算加速卡”这个基础问题也一次说清楚。
1. Atlas 300V 24G:先把它“是不是运算加速卡”这件事说清楚
1.1 板卡形态:四颗昇腾310P组成一块卡
Atlas 300V 24G这个型号,官方叫法也常写作Atlas 300V Pro 24GB。它本质上是一张标准的PCIe半高半长推理卡,插在服务器上就能用,不需要专用机箱,散热也就是被动散热加服务器风道。很多人第一次看到npu-smi info输出时会愣住,因为一张卡显示出来不是1个设备,而是4个NPU设备——没错,这块板卡上有4颗昇腾310P AI处理器芯片,每颗芯片独立工作。
昇腾310P用的是华为达芬奇架构,这个架构和GPU的CUDA核心设计思路很不一样。它里面分了几种计算单元:AI Core负责矩阵和向量运算,另外还有专门做标量运算、数据搬运的单元。对于YOLO这种卷积占比极高的网络来说,矩阵算力基本决定了推理速度。
这颗芯片定位就是边缘推理和智能视频分析场景,低功耗、高能效,不追求单芯片极限算力。板上4颗310P凑在一起,整卡显存加起来才到24GB。这里要强调一下,24GB是4颗芯片各6GB的显存加起来的总和,不是24GB统一显存池。
1.2 推理定位:能跑什么、不能跑什么
回到热词里那个问题:“Atlas 300V 24G是运算加速卡吗?”答案是:是运算加速卡,但要分清是哪种运算加速。
它和NVIDIA的A100、4090这类训练卡/通用GPU完全是两个物种。Atlas 300V是纯粹的AI推理加速卡,设计目标是高吞吐跑已经训练好的模型,比如YOLO检测、ResNet分类、OCR识别、语音识别这类任务。官方标称的算力是INT8约140 TOPS这个级别,FP16也有几十TFLOPS,纸面数据非常漂亮。
但它不能当图形卡用,没有显示输出;也不是用来训练模型的,虽然理论上能跑训练,实际没人这么干,软件栈和显存带宽都不适合。它是一款专用推理加速设备,类似Google的TPU、寒武纪的MLU,而不是通用GPU。
1.3 24GB显存的架构账:不是统一显存池
刚才说了,24GB是4颗芯片各自6GB相加。这对部署策略有直接影响:如果你要跑一个很大的模型,比如YOLOv8x或者更大,单颗芯片6GB显存放不下,那就要么做模型切片,要么把不同推理请求分发到不同芯片上,而不是指望一个进程直接用满24GB。
一开始我不太习惯这个逻辑,后来想想也合理。推理场景追求的是“整体吞吐”,4颗芯片相当于4个独立的推理引擎,前端用负载均衡把请求撒出去,反而比单卡单模型更容易打满吞吐。
实际用的时候,npu-smi info里能看到4个device,编号通常是0到3,每个device有自己的显存占用、温度、算力利用率。写推理程序时,要么指定device id,要么用多进程每个进程绑一个device。
2. 拿Atlas跑YOLO的选型理由:功耗、成本和生态的三笔账
2.1 功耗账:75W板卡和200W GPU的差距
部署现场的机房条件决定了你不能光看算力。一个客户现场可能机柜供电有限、散热条件一般,插一张350W的GPU和插一张最大功耗75W左右的Atlas 300V,差别非常大。
我在实测时观察过,跑满4颗芯片做YOLOv5s INT8推理,板卡整体功耗也就60多瓦,满载不超过75W。而一张T4是70W,一张A10是150W,RTX 4090直接奔着450W去了。也就是说,同样跑一个视频分析业务,用Atlas 300V的话,一个机箱里能塞进更多卡,供电压力小很多,散热要求也低。
2.2 成本与部署形态
国产化替代需求是目前很多项目选昇腾的核心原因,这里不展开政策层面的东西,单说硬件本身的性价比。
Atlas 300V Pro 24G这种卡的市场定位和T4差不多,但INT8推理吞吐在同价位段上并不吃亏,甚至在部分模型上能打出更高的帧率。对于目标检测这种INT8友好型任务,昇腾的达芬奇架构在INT8上的吞吐表现是相当不错的。
部署形态也灵活。它是标准PCIe卡,x86服务器能插,鲲鹏/昇腾服务器也能插,不需要专用供电线,插上就能识别。我们当时用的是一台双路x86服务器,插了两张Atlas 300V,一共8个推理NPU设备,跑8路视频流做实时YOLO检测,CPU占用还很低。
2.3 也别回避软件生态差距
说句公道话,昇腾的软件生态和CUDA比,确实有差距,但不是不能用,而是用起来“思维方式不一样”。
CUDA生态是“什么都有,你随便挑”,昇腾生态是“官方给了一条主路,你沿着走就顺,想走野路子就会难受”。主路就是:模型先转成ONNX,再用ATC工具转成.om格式,最后用官方推理引擎或AscendCL接口调用。只要老老实实按这条链路走,官方文档和案例基本能覆盖90%的问题。一旦想搞些骚操作,比如自定义算子、复杂动态shape、训练推理混合调度,那就要做好熬几个通宵的准备。
这个生态现状决定了选型时的一个判断标准:如果你的业务是成熟的CNN推理,比如YOLO检测、分类、分割,Atlas完全够用;如果你要跑大模型、做训练、频繁改模型结构,那还是老老实实用GPU。
3. 从PyTorch权重到NPU可执行:模型转换与部署完整链路
3.1 底软准备:驱动、固件、CANN,版本对不上就全白搭
昇腾的软件栈有三层:驱动(Driver)、固件(Firmware)和CANN(Compute Architecture for Neural Networks)。驱动和固件管设备,CANN是上层的计算库和工具链,类似CUDA Toolkit。
安装顺序不能乱:先装驱动,再烧固件,最后装CANN。装完用npu-smi info检查能不能看到4个NPU设备。如果这一步就报错,后面全都不用谈。
我这边的环境是Ubuntu 20.04 x86_64,CANN用的7.0.RC1那版,配套的驱动和固件版本号在昇腾社区的“版本配套表”里能查到。切记不要自己随便混搭版本。比如CANN 7.0和老的驱动5.1搭配,npu-smi info经常显示正常,但ATC转换或者推理时会报一些莫名其妙的错误,比如E19999内部错误或者aclrtMalloc failed,排查一整天最后发现就是版本不匹配。
安装完成后记得source一下CANN的环境变量:
source /usr/local/Ascend/ascend-toolkit/set_env.sh如果你用root安装,默认装在/usr/local/Ascend/ascend-toolkit下。建议把source写进~/.bashrc,不然每次开终端都要手敲。
3.2 导出ONNX:把网络的“形状”固定下来
昇腾跑的是.om模型,.om从哪儿来?从ONNX来。我们用YOLOv5做例子,首先要把PyTorch权重导出为ONNX。
YOLOv5官方仓库自带导出脚本:
python export.py --weights yolov5s.pt --include onnx --opset 11这里最关键的参数是--opset。昇腾ATC对ONNX算子支持是按opset版本走的,老版本比如opset 11支持最稳,新版本虽然也在跟进,但偶尔会碰到某个算子不支持的问题。我建议先按opset 11导出,如果转换报错再考虑升级opset。
导出时还有一个必须注意的地方:输入shape要固定。YOLOv5导出时默认是动态shape,--dynamic默认是False,但有些代码仓库或者自己改过的模型会带上动态维度。ATC转换动态shape虽然也支持,但配置麻烦、性能差,不如直接固定:
model.model[-1].export = True # 将Detect模块的export设为True,导出时用固定shape导出之后用onnx.checker或者直接onnxruntime跑一遍,确认ONNX本身没问题,再往ATC走。这一步别看简单,很多人忽略,导致后面出问题分不清是导出问题还是转换问题。
3.3 ATC转换:一条命令背后的参数逻辑
拿到ONNX之后,用ATC工具转成.om。昇腾的ATC全称是Ascend Tensor Compiler,它负责把ONNX/Caffe/TensorFlow模型转换成昇腾芯片能高效执行的离线模型。
我用的核心命令如下:
atc --model=yolov5s.onnx \ --framework=5 \ --output=yolov5s_bs1 \ --soc_version=Ascend310P3 \ --input_shape="images:1,3,640,640" \ --input_format=NCHW \ --log=info逐项解释一下:
--framework=5:5代表ONNX,这个数字是固定的,别改。--soc_version=Ascend310P3:这个要特别小心,必须和板卡芯片型号对应。Atlas 300V Pro 24G用的是昇腾310P3芯片,所以写Ascend310P3。如果你拿不准,可以看npu-smi info里每个NPU设备的芯片型号,或者查官方文档里的“处理器型号-Atlas系列推理卡”对照表。--input_shape:要和你导出的ONNX输入节点名、shape完全一致。YOLOv5的输入节点名默认是images,shape是1,3,640,640。如果你训练时用的是其他输入尺寸,这里也要对应改。Input节点名可以用onnxruntime或者netron查看。--input_format=NCHW:ONNX导出的默认是NCHW,一般不用改。
转换成功后会生成yolov5s_bs1.om文件。如果报算子不支持,多半是opset版本问题,回去重新导出;如果报shape不对,就检查--input_shape和ONNX的输入是否一致。
3.4 推理调用:msame验证和ACL接口落地
模型转好之后,先用官方工具msame快速验证一下能不能跑通。
msame --model yolov5s_bs1.om \ --input "images.bin" \ --output ./outputmsame会返回单次推理耗时和输出文件路径。输出文件是二进制raw格式,需要用Python自己解析。这一步主要用来确认模型转换没问题,以及看个大概性能,真正落地还是要用AscendCL(ACL)接口写推理服务。
ACL是昇腾的C语言API,也有Python封装。整体使用流程比CUDA简单很多:
acl.init()初始化acl.rt.set_device(0)绑定NPU设备acl.mdl.load_from_file()加载.om模型acl.mdl.create_desc()创建模型描述信息,拿到输入输出buffer大小acl.rt.malloc()分配设备内存acl.mdl.execute()执行推理- 把输出拷贝回主机内存,做后处理
实际项目中,我建议把推理部分做一个简单的C++服务,或者用Python的pyACL配合队列做并发。Python调ACL的动态库开销很小,性能瓶颈主要在模型执行本身,所以Python做推理服务完全可行,开发效率高很多。
我自己是把YOLO的后处理——解码、NMS、画框——写成了纯Python模块,推理部分用pyACL,整个服务跑在Flask上,压测下来单路视频流完全没压力。
3.5 NMS放哪跑:几个可行方案的取舍
这是昇腾部署YOLO最容易踩坑的地方。GPU上跑YOLO,NMS一般就放在TensorRT或者torchvision里顺手做了,但ONNX导出的YOLO模型,输出是解码前的原始预测——通常是1, 25200, 85这种形状,包含每个anchor的坐标、置信度和类别概率。ATC转换不会帮你做NMS,.om模型的输出依然是这些原始预测值。
所以NMS必须自己在CPU上实现,或者用昇腾提供的算子拼。我的建议是:
优先做CPU NMS。YOLOv5s的候选框也就25200个,单帧NMS用向量化NumPy或者普通Python循环也就几毫秒,相比NPU推理时间,完全可接受。直接上一个轻量级NMS实现,比如把所有框置信度排序,按顺序抑制IoU大于阈值的框,80个类别处理完整个流程还能稳定在2-3ms以内。
如果你实在想在NPU上做NMS,昇腾CANN里有一个NMS算子可以尝试,但要自己写算子融合图,调试成本高。还有一个思路是使用MindX SDK,它自带后处理插件,不过配置起来也不省心。
4. 实测数据和调优记录:什么样的吞吐值得开心
4.1 基线实测:先把每个环节的耗时拆开
我拿YOLOv5s、输入640x640、转成INT8的.om模型做了一轮基线测试。先说明,测试环境是双路x86服务器,CANN 7.0.RC1,单设备推理。
单张图片单次推理(NPU执行时间)大约是2.5-4毫秒,换算成单设备FPS大概250-400之间。注意这里说的是单颗310P芯片,整卡4颗芯片如果同时跑,理论吞吐能到1000+FPS,当然这是纯模型推理时间,不含前后处理。
这个成绩什么概念呢?T4上用TensorRT跑YOLOv5s FP16,单卡大概300-500 FPS,算下来Atlas 300V的INT8性能并不差,考虑到功耗差距,性价比是真的能打。
但基线数据只是参考,实际业务不可能只算模型推理。整个链路是:
摄像头拉流 -> 解码 -> 缩放Resize -> 归一化 -> NPU推理 -> 后处理NMS -> 画框推流我测试时发现,图像预处理反而成了瓶颈。用Python PIL或OpenCV做resize和归一化,单帧要花5-8ms,比NPU推理还慢。这是非常典型的现象。
4.2 调优三板斧:批量、AIPP、算力拆分
第一板斧:加大batch size。
.om模型转出来是固定batch的。如果你转的是--input_shape="images:1,3,640,640",那一次只能推理一张。但如果场景允许,比如做离线批量检测,可以转成batch 4或batch 8,然后在代码里攒够一个batch再送进去推理。
我实测batch 4比batch 1的时延只增加了不到50%,但吞吐翻了接近3倍。对于视频分析业务,攒batch会牺牲一点点时延,但对吞吐型场景非常值得。
第二板斧:用AIPP把预处理下沉到NPU。
AIPP是昇腾的图像预处理模块,可以在ATC转换时配置,让NPU直接处理原始图像数据,包括缩放、色域转换、归一化等,省掉CPU上的OpenCV预处理。
配置一个aipp.cfg,里面可以指定:
aipp_op { aipp_mode: static input_format: RAW_RGB888_U8 src_image_size_w: 640 src_image_size_h: 640 csc_switch: true rbuv_swap_switch: true # BGR转RGB 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 }配置好之后,ATC转换时加上:
--insert_op_conf=aipp.cfg这样输入给NPU的就直接是原始图片数据(比如JPEG解码后的RGB buffer),归一化在NPU内部完成。注意,如果用了AIPP,你写推理程序时的输入数据格式就变了,要按AIPP配置喂数据。
第三板斧:把4颗芯片分开调度。
整卡4颗芯片,如果只用一个device,其他三个就闲着。我们的做法是起4个推理进程,每个进程绑定一个device id,前端用一个队列做分发。这样整卡吞吐才是完整的。
注意进程绑设备不是自己写个循环就行,AscendCL在进程初始化时通过set_device绑定,绑了之后这个进程的所有acl.mdl调用都会落到指定设备上。一个进程内部不要跨device,否则内存拷贝开销巨大。
4.3 调优后的结果与判断
做了上面三板斧之后,实际效果:
- 单设备YOLOv5s INT8,batch 4,纯推理约8-10ms/4帧,平均单帧2-2.5ms
- 加了AIPP之后,CPU预处理时间从每帧5-8ms降到几乎为零(仅剩JPEG解码)
- 整卡4设备并发,跑4路1080p视频流做实时检测,端到端每路稳定在25FPS以上,CPU占用不到30%
这个结果已经足够满足大多数安防、交通、工业质检场景的实时性要求。
5. 部署中不得不提的几个坑:每个都让项目晚一周
5.1 版本匹配与驱动固件烧写
昇腾的设备管理比较严格。新卡第一次上机,如果没烧固件,npu-smi info可能会显示“Device unhealthy”或者干脆看不到卡。
需要先装驱动,然后执行固件升级:
/usr/local/Ascend/driver/tools/upgrade-tool --device_index -1 --component firmware --upgrade --file xx.fw这个操作要小心,烧固件过程千万不要掉电,否则板卡可能变砖。我一开始以为装完驱动就万事大吉,结果固件没烧,设备一直报E40012,折腾了一天才发现是固件版本太老。
5.2 预处理不一致:GPU上好好的,NPU上检测全糊
这是最隐蔽的坑。GPU上跑PyTorch时,数据预处理是letterbox + RGB + /255。但ONNX导出的模型里并没有包含这些预处理逻辑,如果你在NPU侧喂进去的是没有归一化的原始数据,模型输出就会完全乱掉,检测框置信度全部逼近0,或者画出一些莫名其妙的框。
我建议的做法是:在喂给NPU之前,严格按照训练时的预处理流程处理数据,并且先用单张图片验证。官方也提供了一种方式,就是用ATC的--insert_op_conf配AIPP来做预处理,但AIPP的src_image_size_w/h和crop参数如果和训练设置不一致,效果也会出错。所以每次改模型都要先跑一张验证图,确认输出的检测结果和GPU上有可比性,再做批量测试。
5.3 动态shape的限制与新版本
昇腾的.om模型虽然支持动态shape,但限制很多。比如动态维度只能用于batch,H和W的动态范围需要配置dynamic_dims,而且会被限制在特定档位。
大部分YOLO部署建议固定输入尺寸。如果你的业务需要多分辨率输入,最稳妥的办法是转换多个不同分辨率的.om模型,运行时根据输入情况选择对应模型。Atlas 300V Pro板载内存24G,多放几个模型文件完全没问题。
另外建议用较新的CANN版本(比如6.3以上),动态shape的支持会好很多,ATC转换时报错的概率也低一些。
5.4 npu-smi查不到设备或报错
设备不识别是最常见的第一道坎,通常由以下几个原因导致:
- 卡没插好或者供电不足,重新插拔,确认PCIe链路正常
- 驱动没装对,用
npu-smi info前先确认lsmod | grep drv_pcie之类的驱动模块是否加载 - 与GPU卡混插在某些服务器上存在PCIe资源冲突,需要调整BIOS里的PCIe拆分模式(如Above 4G Decoding开启)
- 服务器内核版本过新或过老,昇腾驱动对内核版本敏感,卸载重装前先确认内核版本在官方支持列表内
如果npu-smi info能出来但显示HwChip错误,多半是固件版本问题,按前面的步骤重新烧固件。
最后再分享一个我自己用着的经验:昇腾的运行时日志默认在/var/log/ascend下面,部署遇到问题别瞎猜,先去翻日志。比如模型加载失败,plog里会明确告诉你哪个算子不支持、哪块显存分配失败。排查效率能提高一大半。另外新卡上手,先不要按着GPU的思维去调优,把转换链路跑通、把预处理对齐、把后处理接上,整个流程是通的,再一步步看性能瓶颈在哪。我这次从拿到卡到全流程跑通,用了大概三天,之后基本就顺了,现在这套服务已经稳定跑了一段时间,没出过幺蛾子。