ARTICLE DETAIL

资讯详情

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

Atlas 300V 24G推理卡部署YOLOv5全攻略:从硬件解析到OM转换与性能调优

Atlas 300V 24G推理卡部署YOLOv5全攻略:从硬件解析到OM转换与性能调优 前阵子手头正好有一颗Atlas 300V 24G推理卡配合YOLOv5做目标检测服务前后折腾了将近一周才把整体性能压到理想状态。先直接回答那个被问了很多次的问题Atlas 300V 24G确实是运算加速卡但它的“加速”范围是AI推理不是通用计算。这篇文章我就把这颗卡从开箱到部署YOLO的完整链路、关键参数、踩坑记录全部摊开讲给准备上车的朋友省点时间。1. Atlas 300V 24G硬件定位是运算加速卡但不是你想象的那种1.1 一张名字被误导的卡通常用来干什么Atlas 300V最常出现的场景是视频分析、智慧园区、工业质检这些需要“大路数并发推理”的业务。和训练卡不同它的设计目标不是把模型loss降下去而是把训练好的模型以最低延迟、最高吞吐跑起来。你拿到的300V 24G插在服务器上系统里能看到一张PCIe设备但用nvidia-smi这类工具是看不到的因为昇腾这边有自己的管理工具叫npu-smi。第一次用的人很容易在这卡壳插上卡、装完驱动完全不知道设备是否正常。这时候先跑npu-smi info看卡状态出现昇腾芯片信息就说明物理链路没问题。24G这个数字也需要说清楚。300V 24G整卡其实是两颗昇腾310P芯片封装在同一块PCB上单颗芯片12G显存两颗合计24G但这24G不是统一的存储空间而是两块物理独立显存。写程序时通过指定device_id来区分device 0和device 1各占12G。很多人以为24G可以完整跑一个超大模型实际做不到需要模型并行或拆成两个实例分别部署。1.2 硬件规格与参数解读从规格表看Atlas 300V 24G的关键参数可以这样理解参数项数值说明芯片型号昇腾310P双芯片推理场景专用AI芯片显存单芯片12G整卡24G芯片间显存不统一寻址内存带宽单芯片约51.2GB/s与GPU相比偏小对访存密集型算子不友好INT8算力单芯片约140TOPS推理卡主打量化后的INT8性能FP16算力单芯片约70TFLOPS支持FP16但FP32性能较弱PCIe接口PCIe 4.0 x16数据搬运瓶颈特别注意对外接口无显示输出纯计算卡不是显卡这颗卡的FP32算力其实很一般如果你在PyTorch里用float32直接推理性能会很难看。正确用法是转成FP16或者INT8的om模型后再跑。很多初次接触的人抱怨“300V跑YOLO怎么这么慢”十有八九是模型精度没转对。1.3 “运算加速卡”的正确理解AI推理专用而非GPGPU运算加速卡这个词比较宽泛。NVIDIA的A100叫通用计算GPUCUDA生态啥都能算而Atlas 300V是AI推理加速卡它的硬件架构针对卷积、矩阵乘这类算子做了深度优化但你不能指望它去跑大规模科学计算或者图形渲染。这带来一个最直接的思维模式变化在英伟达平台上你习惯了PyTorch、TensorRT、CUDA这套技术栈切换到昇腾核心技术栈变成了CANNCompute Architecture for Neural Networks、ATC模型转换工具、AscendCL推理接口。PyTorch模型不能直接在这里跑必须先转换为om格式。另外卡上的两颗芯片虽然是独立计算单元但CANN提供了一些跨芯片协同的能力比如通过集合通信做多卡推理不过对大多数业务来说双卡各跑各的再用负载均衡分发请求性价比最高。2. 在Atlas上部署YOLO的整体架构与方案选型2.1 部署一条链路上你会遇到的四个件昇腾推理部署不是装个驱动就能直接跑的它像一条流水线每个环节都有对应组件先把这个地图认清楚驱动与固件最底层让操作系统识别硬件。一般叫Ascend HDKHardware Development Kit。CANN Toolkit昇腾的计算软件栈相当于CUDA Toolkit的地位。里面有算子库、图编译引擎、运行时等。ATC工具CANN里的模型转换器。作用是把TensorFlow、PyTorch、ONNX等格式的模型转换成昇腾用的om格式。AscendCL统一推理编程接口。你写推理代码时调用的是这层API类似CUDA Runtime API。这一条链路的关系可以理解为应用调用AscendCL接口AscendCL由CANN驱动底层芯片计算ATC则负责把外部训练框架的模型“翻译”成昇腾芯片能听得懂的语言。这四个件缺一不可。2.2 为什么是“转OM”而不是直接跑PyTorch很多人在最开始会问既然CANN支持PyTorch为什么不能直接加载.pt文件推理这里要分清两个层面。CANN确实提供了PyTorch适配框架torch_npu但它的主要用途是让训练和在线推理能在昇腾上跑起来底层是需要Aten适配层把算子映射到NPU上的。而yolov5s.pt在CPU上加载后你执行model(img)走的还是PyTorch自己的算子执行逻辑不会自动变成NPU上的算子。转换成om后ATC会做这几件事将网络的结构、权重、算子都编译成芯片直接执行的指令对算子进行融合优化比如把ConvBNReLU合并成一个算子根据你指定的输入尺寸、batch大小生成最合理的执行方案。换句话说om是“为Atlas芯片量身定制的可执行文件”PyTorch模型是“通用的描述文件”。要想发挥芯片性能转换这步省不掉。2.3 两种部署姿势纯AscendCL手写推理 vs MindX SDK拿到om模型后怎么把推理程序跑起来通常有两种路线对比项纯AscendCLMindX SDK上手难度较高需手写预处理、推理、后处理较低配置pipeline即可灵活性高每个环节可自定义中受固定流程约束推理性能可压榨到极致有少量额外开销适合场景复杂定制、性能极致优化快速上线、标准流程我实际的建议是如果是第一次用Atlas先用MindX的YOLO插件跑通一遍确认卡本身没问题再回头用AscendCL手写这样排查问题的范围会更清晰。纯AscendCL路线的好处是你能精确控制哪部分在CPU上做哪部分在NPU上做。比如图像预处理里的仿射变换DVPP硬件加速的抠图缩放只支持特定格式如果你的预处理逻辑特别复杂比如自定义马赛克增强DVPP可能满足不了这时需要自己写CPU侧代码AscendCL的可控性就体现出来了。3. 完整实操把YOLOv5转成om并跑通推理3.1 环境准备驱动、固件、CANN版本对应关系在部署前建议先把版本对应关系拿捏准。昇腾官方的版本配套表会标明CANN Toolkit、驱动固件、推理卡之间版本兼容性直接按配套表安装能少踩一半的坑。以我这次使用的组合为例服务器x86架构Ubuntu 20.04驱动固件Ascend HDK 24.1.rc1CANN ToolkitCANN 8.0.RC1PyTorch2.1.0torch_npu版本对应安装顺序不能乱先安装HDK驱动固件然后安装CANN Toolkit最后安装torch_npu。如果顺序反了之后使用npu-smi会找不到设备。安装完CANN后需要source一下环境变量source /usr/local/Ascend/ascend-toolkit/set_env.sh然后验证环境npu-smi info能看到类似这样的信息就说明卡处于正常状态------------------------------------------------------------------------------------------- | npu-smi 24.1.rc1 Version: 24.1.rc1 | | NPU Name | Health | Power | Hugepages-Usage | | Chip | Bus-Id | AICore | Memory-Usage | | 0 310P | OK | 32.2W | 0 | | 0 0000:C1:00.0 | 0 | 2% | 3697 / 12288 MB | 注意这里显示的是3697 / 12288 MB而不是24G因为这是单颗芯片的显存视图。如果你要看到整卡的两颗芯片需要加个参数查看npu-smi info -t board这样能看到两张芯片各自的状态和温度。3.2 ONNX导出与算子检查容易被忽略的第一步YOLOv5训练完得到的通常是.pt文件要转成om我的建议是先从.pt导出成ONNX再做ONNX到OM的转换。为什么不直接用PyTorch模型转换因为PyTorch模型转换需要额外依赖torch_npu做模型解析遇到动态算子容易报错而ONNX是一个稳定成熟的中间表示ATC对它支持得最充分。yolov5官方仓库自带了导出脚本python export.py --weights yolov5s.pt --include onnx --img-size 640 640 --batch-size 1 --simplify这一步有几个细节建议注意固定输入尺寸为正方形640x640ATC转换时如果模型输入是动态的性能会受影响。能固定就固定开启--simplify用onnx-simplifier进行图优化减去不必要的节点导出后务必看一下输出的节点名。你推理时需要知道三个输出分别对应哪个head层。YOLOv5的ONNX通常输出三个blob名称类似output0_yolov5s、output1_yolov5s、output2_yolov5s分别代表大中小三个尺度。拿到ONNX后先做一次算子检查。CANN提供了msopgen工具可以查看模型算子信息也可以直接用ATC转换时的报错来驱动修正。如果你用的YOLOv5很新可能引入了某些自定义算子ATC会明确提示不支持这时候就要做算子替换或使用MindX自带的插件规避。3.3 使用ATC完成ONNX到OM转换关键参数逐条说明模型转换是整条链路里最容易出幺蛾子的环节。我使用的转换命令如下atc --modelyolov5s.onnx \ --framework5 \ --outputyolov5s_640_bs1 \ --soc_versionAscend310P3 \ --input_shapeimages:1,3,640,640 \ --input_formatNCHW \ --output_typeFP16 \ --insert_op_confaipp_yolov5.cfg \ --enable_small_channel1 \ --optimize_level1逐条说参数--framework55代表ONNX格式。这是ATC识别输入格式的开关不要漏掉--soc_versionAscend310P3训练卡和推理卡的soc_version不一样310P芯片对应Ascend310P3。用npu-smi info能看到芯片具体型号这里必须对上不然转换后的模型很可能加载失败--input_shape输入名称和维度必须与你导出的ONNX一致。如果那个节点叫images就写images如果叫input.1就写input.1不能猜--output_typeFP16模型权重转为FP16输出。YOLOv5的精度在FP16下几乎没有损失但速度能翻倍。如果你的业务要求严格可以试试FP32性能会差很多--insert_op_conf这个参数用于配置AIPPAI PreProcessing把图像缩放、归一化、通道变换这些操作烧进模型里推理时直接从YUV或RGB原图得到归一化输入省掉CPU预处理时间。后文细说--enable_small_channel1小通道优化YOLOv5的CSP结构里很多特征图通道数不多如128、64开启后能进一步降低访存开销--optimize_level1优化等级0是不优化1是基础优化2是激进优化可能改变精度。一般选1。AIPP配置文件的写法如下aipp_op { aipp_mode: static input_format: RGB888_U8 src_image_size_w: 640 src_image_size_h: 640 crop: false load_start_pos_h: 0 load_start_pos_w: 0 resize: true resize_w: 640 resize_h: 640 csc_switch: true rbuv_swap_switch: true 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 }这里解释一下关键项input_format填RGB888_U8代表输入图像是8位的RGB数据resize设置成640x640会自动缩放min_chn系列是减均值var_reci_chn系列是乘以1/255做归一化。把这些预处理全部塞进模型后你在外部就只需要做“读图--转RGB--送入模型”缩放归一化全部在NPU里完成。转换完成后会生成yolov5s_640_bs1.om文件。建议同时生成一个带batch 4的版本之后做性能压测对比用atc --modelyolov5s.onnx \ --framework5 \ --outputyolov5s_640_bs4 \ --soc_versionAscend310P3 \ --input_shapeimages:4,3,640,640 \ --input_formatNCHW \ --output_typeFP16 \ --insert_op_confaipp_yolov5.cfg3.4 推理代码从零写一个Python目标检测服务模型转换完成后写推理代码。这里我使用的是纯AscendCL方式通过pyACL的Python接口。先说明一下整体流程读图像、预处理AIPP已在模型里做了这里只做图片解码和转RGB、创建输出内存、执行推理、解析三个输出头的数据、做NMS后处理、绘制结果。下面是一个极简但能跑的推理核心代码import acl import numpy as np import cv2 # 初始化 acl.init() ret acl.rt.set_device(0) # 选择device 0 context, ret acl.rt.create_context(0) # 加载om模型 model_path byolov5s_640_bs1.om model_id, ret acl.mdl.load_from_file(model_path) # 获取模型输入输出信息 input_desc acl.mdl.get_input_desc(model_id) output_desc acl.mdl.get_output_desc(model_id) input_size acl.mdl.get_input_size_by_index(model_id, 0) output_size [] for i in range(3): output_size.append(acl.mdl.get_output_size_by_index(model_id, i))输入数据需要放在昇腾设备侧内存。这里我申请device侧buffer然后把numpy数组拷贝过去# 将图像从numpy数组拷到device内存 def preprocess(img_rgb): # img_rgb 已经是640x640x3的RGB uint8数组 img_np np.ascontiguousarray(img_rgb) input_data img_np.reshape(1, 3, 640, 640).astype(np.uint8) return input_data img cv2.imread(bus.jpg) img_rgb cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img_resized cv2.resize(img_rgb, (640, 640)) input_data preprocess(img_resized) # 申请输入输出内存 input_buffer, ret acl.rt.malloc(input_size, 2) # 2表示内存对齐 acl.rt.memcpy(input_buffer, input_size, input_data.tobytes(), input_data.nbytes, 1) output_buffers [] for i in range(3): buf, ret acl.rt.malloc(output_size[i], 2) output_buffers.append(buf) # 执行推理 acl.mdl.execute(model_id, [input_buffer], output_buffers, 3) # 把输出拷回CPU侧并转numpy outputs [] for i in range(3): out_np np.zeros(output_size[i], dtypenp.float16) acl.rt.memcpy(out_np.tobytes(), output_size[i], output_buffers[i], output_size[i], 1) outputs.append(out_np.reshape(...)) # 具体reshape需要看输出shape这里要特别说明的是模型的三个输出对应YOLOv5的三个检测头shape分别是(1, 3, 80, 80, 85)、(1, 3, 40, 40, 85)和(1, 3, 20, 20, 85)。85的含义是cx、cy、w、h、obj_conf 80类置信度。后处理NMS部分和普通的YOLOv5后处理类似需要注意的值都在NPU上传回来的可能是FP16后处理前要转成float32然后用常规anchor decode再按obj_conf过滤低于阈值的框最后NMS。这部分逻辑可以参考yolov5官方detect.py的后处理但要把anchor信息按照自己的模型设置好。跑通推理后你大概率会看到一个现象单张图耗时几十毫秒感觉“也没有很快”。别急这只是没有优化的基线。接下来接触性能调优。3.5 性能优化三板斧batch、AIPP、stream并发Atlas 300V跑YOLO性能潜力非常大但要会压。我的实际经验是三个方向第一是batch化。单张图推理时芯片利用率往往不高。如果你把4张或8张图拼成一个batch送入模型吞吐量几乎线性增长。在我测试的YOLOv5s模型上bs1单图推理约4msbs4时四张合计约9ms即单图2.25ms吞吐提升明显。但要注意batch变大后延迟也变高适合离线批处理场景。第二是AIPP。前面已经说了图像缩放归一化可以放进模型里。这不止是省CPU关键是减少了host与device之间的数据传输量。原始JPEG图或RGB图进NPU后缩放归一化在芯片内部完成省掉一次内存拷贝对端到端延迟帮助显著。第三是使用stream并发。CANN的推理是异步的你可以创建多个stream每个stream负责不同的图像数据流在多个stream之间做流水线处理stream1, ret acl.rt.create_stream() stream2, ret acl.rt.create_stream()发请求时交替提交到两个stream让一张图在预处理时另一张图正在推理形成流水线。这种方法在视频流场景中效果显著可以把端到端的平均延迟压到接近单张推理时间。4. 部署中踩过的坑与排查技巧4.1 常见报错与解决方案速查表部署过程中我整理了一套高频报错速查表基本覆盖90%的新手问题报错现象可能原因解决方式npu-smi看不到设备驱动未装好或版本不匹配重装HDK驱动核对内核版本ATC转换报错E40010输入节点名错误用Netron打开ONNX查看输入名确保input_shape里的名字一致模型加载失败acl.mdl.load_from_file返回507008芯片型号不匹配用npu-smi info确认soc_version重新转换推理输出全为0或NaNAIPP通道顺序错误检查aipp配置中rbuv_swap_switch是否设置正确推理报错out of memory设备侧内存泄漏检查每次推理后是否acl.rt.free释放buffer图像颜色异常AIPP硬件预处理通道顺序与模型不一致确认模型输入是BGR还是RGB修改aipp配置对应通道变换多卡识别不到device 1BIOS未开启SR-IOV或PCIe链路异常用lspci确认设备枚举重插卡或换槽位性能远低于预期未转FP16或未启用AIPP重新做模型转换检查om文件是否包含预处理节点4.2 关于24G显存你该知道的三个真相第一个真相是这卡的内存不是“统一寻址”的。每一颗310P芯片只有12G显存对YOLOv5这类中等模型足够但如果你想跑一个超过12G的模型就必须采用模型切分或多卡方案。第二个真相是你以为的24G是两倍的12G但实际业务中往往只能把两个芯片当成两台独立的机器看待。很多从NVIDIA迁移过来的人默认一张卡是统一显存池然后惊讶于为什么TensorFlow/torch_npu不能利用全部24G。第三个真相显存占用可能比你预估的高。YOLOv5s本身模型很小但如果你用了AIPP、动态batch、多个stream显存开销会被放大。建议用npu-smi info查看实际占用如果长期超过10G就要检查是不是有内存泄漏。4.3 几个提高稳定性的实战建议部署到生产环境后稳定性往往比单次性能更重要。几个我实测有效的建议显存释放一定要放到finally块里。昇腾的device侧内存不像CPU内存那样自动回收如果进程崩溃设备侧内存可能残留越积越满。多线程推理必须做好device上下文隔离。每个线程建议显式创建自己的context避免默认上下文互相抢占导致性能抖动。监控芯片温度。310P在高负载下温度升高会触发降频有条件的话加装主动散热服务器里注意风道。如果发现推理延迟逐渐变大优先检查是不是大页内存碎片化。可以通过重启进程缓解长期运行建议每天定时重启一次服务。部署Atlas这一路走下来我的最大体会是昇腾平台的技术栈和CUDA生态确实有差异刚接触时会觉得处处不顺手但捋清CANN、ATC、AscendCL这条链路后它的推理性能和稳定性其实相当能打。特别是AIPP和batch化带来的收益在很多业务里比换一张更贵的卡更立竿见影。如果你也准备在300V上跑YOLO先把模型转换这步做扎实后续的麻烦会少一大半。
返回列表