ARTICLE DETAIL

资讯详情

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

Atlas 300V 24G推理卡上部署YOLO目标检测模型实战指南

Atlas 300V 24G推理卡上部署YOLO目标检测模型实战指南 搞过AI部署的兄弟应该都有这种感觉很多方案在GPU上跑得好好的一挪到别的硬件上就开始怀疑人生。今年我有个项目要把YOLO目标检测服务从GPU集群迁到Atlas平台一开始查资料全是零散片段折腾了快两周才把pipeline彻底跑通。这篇就完整记录一下我在Atlas 300V 24G推理卡上部署YOLO的全过程包括选卡逻辑、环境搭建、模型转换、推理代码和性能调优以及一堆从文档里根本查不到的坑。如果你也准备在Atlas系列上部署目标检测模型这篇文章应该能帮你省下不少时间。先说结论Atlas 300V 24G确实是一块运算加速卡而且是专门为推理场景设计的加速卡不是训练卡。这块卡基于昇腾Nano架构板载24GB内存支持最大约200TOPS的INT8算力板卡功耗控制在75W左右支持PCIe 4.0接口整卡最多可以解码36路1080P视频流。对大批量视频流做实时目标检测这个规格在同等级推理卡里相当能打。我这次主要是在上面跑YOLOv5s和YOLOv8s两个模型整体体验和性能数据下文会详细讲。1. 硬件选型为什么最后选了Atlas 300V 24G1.1 从GPU迁移到Atlas的关键考量先说选型这件事。最初定的方案其实是用NVIDIA的T4或者A10做推理模型代码基本不用改CUDA生态太成熟了torch直接调就行。但项目遇到两个现实问题一是整机功耗和部署密度卡得比较死GPU方案的功耗预算超了二是硬件采购渠道和后续运维的自主可控要求最终把方案锁定在了国产推理加速卡上。Atlas 300V 24G这个型号的定位很明确它不碰训练场景就是干推理的。和训练卡相比它把大量晶体管用在了低精度计算单元、视频编解码硬件模块、以及数据搬运通道上所以能效比非常突出。用大白话说训练卡是“多面手”什么活都能干但推理卡只干一件事就是把训练好的模型以最低的延迟和功耗跑起来。1.2 300V 24G的核心规格和适用场景参数这种东西看官方文档就行我说几个实际部署中真正影响体验的点内存24GB跑YOLOv5s这种规模的模型非常宽裕甚至可以同时常驻多个模型做多任务推理。算力INT8约200TOPSFP16性能会低一些但跑推理足够用。功耗整卡75W左右不用额外供电线PCIe供电就能带起来服务器和工控机都能插。视频解码内置硬件解码模块支持H.264/H.265的硬解36路1080P同时解这个对视频流检测场景是杀手级功能能把CPU从解码工作中完全解放出来。接口PCIe 4.0 x16兼容3.0插槽只是带宽减半实测对单路视频推理影响不大。适用场景也很清晰视频结构化、智慧安防、工业质检、自动驾驶边缘节点、以及任何需要高吞吐、低功耗目标检测的后端服务。如果你要做模型训练那300V不是干这个的得看昇腾910系列或者老老实实用GPU。1.3 为什么这块卡适合跑YOLO系列YOLO系列本身就是目标检测领域的常青树YOLOv5和YOLOv8的工程化程度很高PyTorch权重开源部署时转成ONNX或者直接转CANN格式都很方便。Atlas 300V 24G的推理引擎对卷积神经网络做了深度优化YOLO这种以卷积为主的网络结构在昇腾上运行效率非常高。另外YOLO模型里大量的算子Conv、BN、ReLU、Concat、Upsample等在CANN算子库中都有深度优化实现模型转换时几乎不会遇到算子不支持的尴尬情况。这一点很关键不是所有模型都能这么顺利我之前转过一些Transformer结构的目标检测模型比如DETR在昇腾上就费了不少劲。2. 环境搭建CANN工具链的安装和配置2.1 版本匹配是第一个大坑Atlas部署最容易翻车的就是版本匹配问题——驱动、固件、CANN工具包、甚至Python版本如果对不上CANN初始化时就会报错。我一开始就是在官方文档下载了最新的CANN 8.1结果和板卡固件不兼容初始化报错白白浪费了半天。我的建议是先把固件、驱动和CANN版本对齐到一套经过验证的组合。以我这次用的稳定组合为例固件Ascend-hdk-310p-npu-firmware_6.3.0_linux.run驱动Ascend-hdk-310p-npu-driver_6.3.0_linux-aarch64.runCANN工具包Ascend-cann-toolkit_6.3.0_linux-aarch64.runPython3.8或3.9注意如果服务器是x86架构记得选带x86_64标识的安装包aarch64是ARM服务器用的。很多ARM开发板用户习惯了aarch64但在普通Intel/AMD服务器上反而搞反了。2.2 安装步骤实录安装顺序有讲究从底层往上依次是驱动、固件、CANN工具包。第一步安装驱动chmod x Ascend-hdk-310p-npu-driver_6.3.0_linux-aarch64.run ./Ascend-hdk-310p-npu-driver_6.3.0_linux-aarch64.run --full --install-for-all驱动装完以后安装固件chmod x Ascend-hdk-310p-npu-firmware_6.3.0_linux.run ./Ascend-hdk-310p-npu-firmware_6.3.0_linux.run --full这里我踩过一个坑如果驱动和固件版本不匹配安装时会提示固件与驱动版本不一致有时候还会自动回滚固件。解决方法是严格安装配套的版本不要混搭。然后安装CANN工具包chmod x Ascend-cann-toolkit_6.3.0_linux-aarch64.run ./Ascend-cann-toolkit_6.3.0_linux-aarch64.run --install --install-for-all安装完成后设置环境变量把以下内容写入/etc/profile.d/ascend.shsource /usr/local/Ascend/ascend-toolkit/set_env.sh export ASCEND_DEVICE_ID0敲命令npu-smi info能看到板卡温度、显存占用和芯片使用率说明驱动和固件已经就位了。2.3 验证环境是否正常环境搭好以后建议先跑一个小例子验证CANN能不能正常调用芯片。在Python里写一段最基础的程序import acl acl.init() ret acl.rt.set_device(0) print(device set ret:, ret) ret acl.rt.create_context(0) print(context create ret:, ret) acl.finalize()能正常打印出ret为0说明CANN和驱动已经正常工作了。遇到报错也不要慌绝大多数情况都是前面版本不匹配导致的重新对齐版本即可。3. 模型转换从PyTorch权重到昇腾om模型3.1 为什么要转成om格式PyTorch训练出来的模型不能直接在Atlas上用要先转成CANN的离线模型格式——omOffline Model。om是经过算子融合、内存复用和指令重排的优化产物推理时不再依赖任何深度学习框架只依赖昇腾的推理运行时。打个比方PyTorch模型就像一个半成品的菜到哪个厨房都得现场加工om模型则是已经做好的预制菜到昇腾这个微波炉里热一下就能直接出餐。所以部署时我们提前把它“烧制”成om格式推理时省去了大量框架初始化和计算图构建的开销。转换工具有两个旧版的是ATCAscend Tensor Compiler新版的是MindStudio里集成的Model Converter界面。命令行方式我用得最多因为方便写进自动化流水线。3.2 从YOLOv5导出ONNX再转om官方YOLOv5仓库提供了export.py脚本可以直接导出ONNX、TorchScript等多种格式。我们的目标是先把PyTorch权重转成ONNX再交给ATC转om。导ONNX的命令python export.py --weights yolov5s.pt --include onnx --opset 12这里有一个细节opset版本最好用12某些高版本的ONNX算子集在ATC转换时会有兼容性问题。另外YOLOv5导出的ONNX里包含了非极大值抑制NMS后处理吗不同版本不一样。为了在硬件上效率最大化我一般导出时不带NMS把NMS留在后处理代码里用CPU做。原因很简单把NMS放进om模型里会限制batch size和输入shape的灵活性而且硬件上的NMS算子不一定比CPU上优化过的OpenCV实现快。3.3 ATC转换参数详解拿到ONNX文件之后核心问题是输入shape的设定。YOLOv5的输入是640x640建议直接固定输入尺寸不要用动态shape原因后面会讲。推荐一份我实际验证过的转换命令atc --modelyolov5s.onnx \ --framework5 \ --outputyolov5s_640 \ --input_shapeimages:1,3,640,640 \ --insert_op_confaipp_yolov5.cfg \ --output_typeFP16 \ --soc_versionAscend310P3解释一下几个关键参数--framework5表示输入模型是ONNX格式1是Caffe2是MindSpore5是ONNX。--input_shapeimages:1,3,640,640固定输入为一张640x640的RGB图batch size为1。如果要做多batch可以写成1,3,640,640;4,3,640,640用分号隔开支持动态batch。--insert_op_conf是AIPPAI Preprocessing配置文件的路径后面详细说。--output_typeFP16让模型以FP16精度推理IOU精度损失很小速度提升明显。--soc_versionAscend310P3是关键必须和板卡芯片对应。300V 24G对应的SoC版本就是Ascend310P3如果这个参数写错转换能通过但上板会报错。3.4 AIPP配置让硬件帮你做前处理AIPP是Atlas推理卡上一个很实用的硬件加速模块可以把图像缩放、颜色空间转换、归一化这些前处理操作直接做进模型输入之前省得在CPU/GPU上单独做一整套变换。我的aipp_yolov5.cfg配置aipp_op { aipp_mode: static input_format: YUV420SP_U8 src_image_size_w: 640 src_image_size_h: 640 csc_switch: true rbuv_swap_switch: false matrix_r0c0: 256 matrix_r0c1: 0 matrix_r0c2: 359 matrix_r1c0: 256 matrix_r1c1: -88 matrix_r1c2: -183 matrix_r2c0: 256 matrix_r2c1: 454 matrix_r2c2: 0 input_format_origin: YUV420SP_U8 crop: false load_start_pos_h: 0 load_start_pos_w: 0 min_chn_0: 0 min_chn_1: 0 min_chn_2: 0 min_chn_3: 0 var_reci_chn_0: 0.003921569 var_reci_chn_1: 0.003921569 var_reci_chn_2: 0.003921569 }这里面的matrix_r*就是YUV到RGB的颜色空间转换系数var_reci_chn_*是归一化系数1/255约等于0.003921569。注意要改input_format和src_image_size要和你的输入图格式保持一致。这个配置的好处是输入图像直接喂YUV数据板卡硬件帮你转RGB并做归一化解放CPU。如果输入的是JPEG直接解码后的BGR图也可以把input_format改成BGR888_U8跳过颜色转换直接做归一化。3.5 静态shape和动态shape的取舍这里多说一句到底要不要用动态shape我的经验是非必要不用。动态shape的好处是输入尺寸可以变比如同时检测小图和细节图不需要外接resize。但坏处是ATC在转换时会插入额外的shape推断和内存分配逻辑推理延迟比固定shape高10%-20%左右而且Atlas的动态shape上限和内存管理策略复杂调试成本也高。在我这个视频流场景里所有帧都会被统一resize到640x640再做推理所以固定shape完全够用性能也最优。如果你要检测的物体尺寸跨度特别大才考虑多档shape比如加一个1280档位或者动态shape。4. 推理代码基于pyACL的完整实现4.1 推理流程总览om模型在Atlas上的推理流程可以拆成几步初始化ACL、加载模型、准备输入输出内存、执行推理、取出输出、后处理解码过滤复现坐标。import acl import numpy as np import cv2 # 初始化 acl.init() acl.rt.set_device(0) context, ret acl.rt.create_context(0) # 加载模型 model_path b./yolov5s_640.om model_id, ret acl.mdl.load_from_file(model_path) print(load model ret:, ret) # 获取模型输入输出信息 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 acl.mdl.get_output_size_by_index(model_id, 0)上面的代码已经把模型加载到内存了。注意acl.mdl.load_from_file返回的model_id是后续所有推理操作的句柄一个进程可以加载多个模型对应多个model_id。4.2 准备输入数据因为AIPP接管了前处理所以我们只需要把BGR图像按照AIPP的要求摆好位置就行。# 读取图像并resize到640x640 img cv2.imread(test.jpg) img_resized cv2.resize(img, (640, 640)) img_rgb cv2.cvtColor(img_resized, cv2.COLOR_BGR2RGB) img_rgb np.ascontiguousarray(img_rgb) # 申请device内存 data img_rgb.astype(np.uint8).tobytes() input_buffer, ret acl.rt.malloc(input_size, 2) # 第二个参数2是默认内存对齐 acl.rt.memcpy(input_buffer, input_size, data, len(data), acl.rt.MEMCPY_DEVICE_TO_DEVICE)这一段的重点是把数据从CPU内存拷到设备内存。Atlas上的数据搬运必须走acl.rt.memcpy不能直接指针赋值。另外输入图在送入模型前要确保已经是连续内存np.ascontiguousarray这个调用是防止某些图像处理库返回不连续数组。4.3 创建输出缓冲区输出空间有多大要先查模型输出张量的shape和数据类型。YOLOv5s的输出是一个三维张量结构是[batch, 25200, 85]。其中25200是三个检测尺度80x8040x4020x20的候选框总和85是4个坐标x,y,w,h1个目标置信度80个类别概率。具体计算方法640输入下80x80640040x40160020x20400加起来等于8400一个尺度三个尺度是25200。# 输出shape为(1, 25200, 85)Float32 output_shape (1, 25200, 85) output_bytes int(4 * np.prod(output_shape)) output_buffer, ret acl.rt.malloc(output_bytes, 2)这里要特别注意模型的输出顺序。建议先用一个小样例把输出dump出来用Python侧解析一下确认维度顺序是[batch, anchors, attributes]还是[batch, attributes, anchors]。不同版本的YOLO导出ONNX时顺序可能不一样写错了解析全乱。4.4 执行推理并拷贝结果创建一个dataset结构把输入输出内存地址绑定上去# 创建dataset input_dataset acl.mdl.create_dataset() output_dataset acl.mdl.create_dataset() input_data_buffer acl.create_data_buffer(input_buffer, input_size) output_data_buffer acl.create_data_buffer(output_buffer, output_bytes) acl.mdl.add_dataset_buffer(input_dataset, input_data_buffer) acl.mdl.add_dataset_buffer(output_dataset, output_data_buffer) # 执行推理 ret acl.mdl.execute(model_id, input_dataset, output_dataset) print(execute ret:, ret) # 取出结果到CPU output_data np.zeros(output_shape, dtypenp.float32) acl.rt.memcpy(output_data.ctypes.data, output_bytes, output_buffer, output_bytes, acl.rt.MEMCPY_DEVICE_TO_DEVICE) output_data output_data.reshape(1, 25200, 85)这一步执行完成后output_data就是模型输出的原始张量接下来就是YOLO经典的后处理。4.5 后处理解析候选框、置信度与NMS后处理包括解析坐标、过滤低置信度候选框、用NMS去掉重叠框最后把640x640坐标系下的坐标换算回原图尺寸再画框或输出结构化结果。def postprocess(pred, conf_thres0.25, iou_thres0.45, orig_w1280, orig_h720): boxes [] scores [] class_ids [] for det in pred[0]: x, y, w, h, obj_conf, *class_conf det max_cls_conf max(class_conf) score obj_conf * max_cls_conf if score conf_thres: continue x1, y1 x - w/2, y - h/2 x2, y2 x w/2, y h/2 boxes.append([x1/640*orig_w, y1/640*orig_h, x2/640*orig_w, y2/640*orig_h]) scores.append(score) class_ids.append(int(np.argmax(class_conf))) return boxes, scores, class_ids实际的NMS可以用cv2.dnn.NMSBoxes快速实现indices cv2.dnn.NMSBoxes(boxes, scores, conf_thres, iou_thres)注意如果你的ONNX导出时已经带了一些自定义算子可能后处理结构不一样最终以你导出的ONNX实际输出为准。5. 性能实测与调优经验5.1 基准性能数据把我实测的一组数据分享出来给大家一个参考基准。测试环境是同一台x86服务器Atlas 300V 24G单卡输入640x640batch1FP16精度模型帧率(单流)延迟(ms)功耗(W)备注YOLOv5s约180fps约5.5ms约30资源占用很少很稳YOLOv5m约90fps约11ms约45精度更高速度略降YOLOv8s约150fps约6.5ms约32和v5s性能相当YOLOv5sbatch4约400fps约10ms约55发挥多卡流水线优势单说延迟可能感觉不出什么做个参照T4 GPU跑YOLOv5s单batch延迟也就6-8msAtlas 300V能跑到5.5ms左右这个成绩在同类推理卡里是相当能打的。5.2 多batch和异步推理调优如果你追求吞吐量建议优先用多batch。把多张图片拼成一个batch喂进去让芯片一次算完比单张跑多次效率高很多。我前面也提到了转换om时用--input_shapeimages:1,3,640,640;4,3,640,640这类带分号的写法就能让模型支持多个固定batch档位。推理侧也简单把多路视频帧攒够4张再统一推理即可batch_data np.zeros((4, 3, 640, 640), dtypenp.uint8) for i in range(4): batch_data[i] preprocess(frames[i]) # 再把batch_data传给acl设备内存另外在代码里可以开一个线程专门做推理另一个线程做图像解码和前处理用队列把数据串起来。因为Atlas的解码和前处理是硬件加速的把数据准备好之后真正调用acl.mdl.execute的时间很短这样流水线式的处理能让GPU始终保持繁忙也能显著提升整体吞吐。5.3 显存资源占用和监控24GB显存在跑YOLOv5s时非常宽裕单模型占用大约500MB-1GB。我甚至试过在一个模型实例上加载YOLOv5s、YOLOv8s和一个轻量分类模型三个模型同时常驻显存还有富余。用npu-smi info能实时监控芯片使用率、显存占用、温度和功耗。我建议把这块卡的单路视频推理功耗控制在30W以内整个服务器基本不用额外散热。做多路1080P视频结构化一块300V可以同时处理8-12路实时流几乎零CPU开销这个性价比就很舒服了。6. 常见问题与排查技巧实录6.1 初始化失败CANN版本和固件不对齐现象调用acl.init()返回非0或者报E10002之类的错误码。排查思路确认驱动和固件版本匹配用npu-smi info查看固件版本用npu-smi device -l查看驱动版本确认CANN环境变量有没有source对应版本是否一致查看/var/log/npu/slog/下的日志日志会给出具体错误码和模块如果不是版本问题跑一遍固件升级程序覆盖安装这个问题的根因大多数时候都是多个版本的驱动/固件CANN混在一起。卸载干净再重装是最省心的办法。6.2 模型转换失败算子不支持ATC转换时偶尔会报某个算子不支持。比如某个ONNX里的自定义算子或者更高版本的opset里新增的算子。我的处理套路先查一下该opset下是不是有多余算子比如某些模型在导出时会把NMS带进去NMS算子在不同版本的行为差异很大建议导出时排除掉。尝试降低opset版本比如从13降到12很多情况下能解决。如果模型中确实有CANN不支持的算子且不能替换只能回ONNX层面把对应算子用标准算子组合改写。6.3 推理结果全黑或坐标完全错乱现象输出的目标框位置完全不对或者图像颜色明显偏色。大几率是AIPP配置和输入图像格式对不上。比如程序里送的是BGRAIPP配置却写成了RGB或者归一化系数写错。处理办法先用一张简单的单色图跑推理观察输出的中间结果确定到底是颜色通道顺序问题还是归一化问题。AIPP配置里的csc_switch、rbuv_swap_switch这两个开关要多试。6.4 推理速度突然下降现象刚开始跑能到200fps跑了半小时后掉到几十fps。排查思路热降频散热不好导致芯片降频功耗和温度直接看npu-smi info如果温度一直80度以上大概率是散热问题。内存泄漏如果在代码里频繁acl.rt.malloc但没有释放设备内存耗尽后系统开始交换性能会大幅下降。用完的数据要记得acl.rt.free。信号干扰如果和CPU绑定在同一降频策略下CPU负载过高也会影响推理效率。6.5 视频流场景CPU占用过高如果发现用了Atlas卡CPU视频解码还是跑满了大概率没有走硬件解码。Atlas 300V内置了视频解码模块在代码里调用acldvpp做解码能大幅降低CPU占用。如果只是拿OpenCV的cv2.VideoCapture解码那解码还是走CPU就很浪费这块卡了。7. 一些实用心得和总结最后分享几个从实际项目里沉淀下来的经验CANN的版本管理一定要严谨。给团队用的服务器最好固定一套经过验证的驱动、固件和CANN组合不要随意做小版本升级很多时候升级完就能遇到奇怪的算子落盘问题或者推理结果差异。ATC转换建议形成自动化脚本。模型迭代以后重新转换部署如果还是手动敲命令效率太低且容易配错参数。我后来都是把ATC命令封装成一个函数输入ONNX路径和shape档位自动输出om同时写一份JSON记录这个om的转换参数方便回溯和问题定位。AIPP前处理能省尽量省。把resize和归一化交给板卡硬件做不只是省CPU还能省一次CPU到设备内存的数据搬运。数据搬运在很多推理场景里是隐藏的瓶颈耗时不亚于模型计算。不要忽视后处理的性能。YOLO的原始输出是25200个候选框如果每路视频每秒几十帧NMS后处理的耗时累计起来也会吃CPU。遇到这个问题可以用TensorRT那种经过优化的GPU NMS或者在CANN侧通过算子融合把NMS放进模型里。前提是测试确认你的后处理确实成了瓶颈否则没必要上这个复杂度。在高并发推理服务里建议叠加一个请求队列层。Atlas本身没有任务调度概念多个线程直接调acl.mdl.execute会增加并发冲突实测在模型层前面加一个队列做串行化配合多batch整体吞吐反而更高。这个部署过程走完一遍我对Atlas平台的评价是在推理能效和部署密度上它确实有自己的独到优势和GPU方案相比迁移成本主要集中在模型转换和算子适配而不是推理调整。希望这篇实操记录能帮到正在折腾类似方案的朋友少走点弯路。
返回列表