ARTICLE DETAIL

资讯详情

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

昇腾推理引擎开源:从模型转换到性能调优的完整实践指南

昇腾推理引擎开源:从模型转换到性能调优的完整实践指南 1. 昇腾推理引擎开源这件事到底在解决什么问题第一次接触昇腾推理引擎的开发者大概率会经历一个很拧巴的阶段模型训练跑通了权重也导出了但一到部署上线就卡住——要么是算子不支持要么是精度对不上要么是吞吐量上不去。过去几年里昇腾生态在训练侧的文档和案例相对丰富但推理侧的开源资料一直比较零散很多关键实现藏在二进制包里出了问题只能靠猜。这次昇腾把推理引擎的核心部分开源出来本质上是在补这块短板。我先把结论摆在前面昇腾开源推理引擎的核心价值不是多了一个能跑模型的工具而是把模型怎么在昇腾NPU上高效跑起来这件事从黑盒变成了白盒。你可以看到图优化怎么做的、算子怎么融合的、内存怎么调度的、量化精度怎么权衡的。对于做端侧部署、做私有化交付、做推理性能调优的人来说这个变化的意义远大于多一个推理框架。这篇文章适合三类人看第一类是在昇腾310P、310P3这类推理卡上做模型部署的工程师你们最关心的是精度和吞吐第二类是从CUDA生态迁移过来的开发者需要搞清楚昇腾推理和TensorRT、ONNX Runtime的差异在哪第三类是做开源项目集成的想把昇腾推理能力嵌到自己的系统里。我会从整体设计思路讲到具体实操再到踩坑记录尽量把我知道的都倒出来。需要提前说明的是下面涉及的具体参数和配置一部分来自官方开源仓库的文档一部分是我在实际项目里验证过的经验值。昇腾的软件栈迭代很快CANN版本不同某些接口和行为会有差异你在复现的时候务必先确认自己的版本号。2. 推理引擎整体架构与设计思路拆解2.1 为什么昇腾要自研推理引擎而不是直接套用现成方案这个问题我被问过很多次。ONNX Runtime、TensorRT、OpenVINO这些推理框架已经很成熟了昇腾为什么还要自己搞一套答案藏在硬件架构的差异里。昇腾NPU的计算单元是AI Core它的指令集、内存层级、数据搬运方式和GPU完全不同。GPU有大量的SM单元和统一的显存地址空间而昇腾的AI Core更接近一个带本地缓存的专用处理器片上存储L1/L0 Buffer容量有限但带宽极高。这意味着一个在GPU上跑得很好的推理引擎直接搬到昇腾上会因为内存搬运策略不匹配而性能暴跌。昇腾推理引擎的设计核心围绕三个点展开图编译优化、算子融合策略、内存复用机制。图编译阶段会把ONNX或MindIR格式的模型转成昇腾内部的图表示然后做常量折叠、死代码消除、算子融合。算子融合这块是重点比如把ConvBNReLU融成一个算子减少中间结果的写回这在片上存储紧张的NPU上收益非常明显。提示昇腾推理引擎的开源部分主要覆盖图优化pass、部分算子实现和运行时调度底层驱动和部分高性能算子库仍然以二进制形式提供。这是理解整个开源范围的前提。2.2 开源范围与闭源边界的实际影响很多人看到开源两个字就以为全部代码都能看到实际不是这样。昇腾推理引擎的开源策略是分层的上层图编译和优化框架开源中间层的算子注册和调度机制开源底层的AI Core指令生成和部分高度优化的算子实现仍然是闭源的。这个边界对开发者的实际影响是什么如果你只是做模型部署和调优开源部分足够你理解整个执行流程也足够你自定义算子或者修改图优化策略。但如果你想深入到指令级别做极致优化那还是得依赖官方提供的算子库。我个人的判断是这个开源程度对于大多数应用层开发者是够用的。真正需要碰底层指令的场景要么是极其特殊的算子要么是对性能有极端要求的场景这类需求通常也会有官方的技术支持渠道。2.3 与主流推理框架的定位差异把昇腾推理引擎和TensorRT放在一起对比是不可避免的。两者都是硬件厂商推出的专用推理引擎都强调图优化和算子融合但设计哲学有区别。TensorRT的生态更成熟对主流模型的覆盖更全工具链也更完善。昇腾推理引擎的优势在于和昇腾硬件的深度绑定特别是在310P3这类推理专用卡上它能利用到一些GPU上没有的硬件特性比如更灵活的量化策略和更细粒度的功耗控制。另一个差异是昇腾推理引擎对MindSpore模型的原生支持更好。如果你的模型是用MindSpore训练的直接走昇腾推理引擎的转换流程会比先转ONNX再转过去少很多麻烦。PyTorch模型则需要先导出ONNX再通过ATC工具转成昇腾的om模型。对比维度昇腾推理引擎TensorRTONNX Runtime硬件绑定仅昇腾NPU仅NVIDIA GPU多硬件后端模型格式MindIR/ONNXONNX/TensorRTONNX量化支持INT8/INT4INT8/FP16INT8开源程度部分开源闭源完全开源动态Shape支持但有限制支持较好支持较好3. 核心细节解析与实操要点3.1 模型转换从训练框架到昇腾om模型模型转换是整个流程的第一步也是最容易出问题的一步。昇腾提供了ATC工具来做格式转换输入可以是ONNX、MindIR或者Caffe模型输出是om格式。转换命令的基本结构是这样的atc --modelmodel.onnx \ --framework5 \ --outputmodel_ascend \ --soc_versionAscend310P3 \ --input_shapeinput:1,3,224,224 \ --precision_modeallow_mix_precision \ --logerror这里有几个参数需要重点解释。--framework5表示输入是ONNX模型这个编号在不同CANN版本里可能有变化建议查对应版本的文档。--soc_version必须和你实际使用的硬件匹配310P3和310B的转换结果是不通用的。--precision_mode控制精度模式allow_mix_precision允许混合精度会在保持精度的前提下尽量使用FP16计算。--input_shape这个参数很关键。如果你的模型支持动态batch这里可以写成input:1,3,-1,-1但动态shape在昇腾上会带来额外的编译开销而且不是所有算子都支持动态shape。我的经验是如果实际部署时batch size是固定的就写死性能会好很多。注意ATC转换时如果遇到不支持的算子会报错并列出算子名称。这时候有两个选择一是用昇腾提供的自定义算子开发框架自己实现二是修改模型结构用支持的算子替换。前者工作量大但一劳永逸后者快但可能影响精度。3.2 精度选择310P3上FP16、INT8到底怎么选昇腾310P3使用什么精度这个问题在搜索里出现频率很高说明大家确实纠结。我直接给结论默认用FP16对吞吐有极致要求且能接受精度损失时用INT8FP32基本不用考虑。310P3的硬件设计对FP16有原生支持算力是FP32的数倍。FP16的精度损失在大多数视觉模型上可以忽略不计分类模型Top-1准确率下降通常在0.1%以内检测模型的mAP下降也在可接受范围。INT8量化就复杂一些。昇腾支持训练后量化PTQ和量化感知训练QAT两种方式。PTQ用校准数据集统计激活值分布然后确定量化参数。QAT在训练阶段就模拟量化误差精度更好但需要重新训练。我实测过一个ResNet50的分类模型FP16下Top-1是76.2%INT8 PTQ之后掉到75.1%差了1.1个百分点。换成一个轻量级的人脸检测模型INT8之后mAP掉了将近3个点这就比较伤了。所以INT8不是无脑上的得看模型对量化误差的敏感度。精度模式算力利用率典型精度损失适用场景FP32基准无精度验证、调试FP162-4倍0.5%大多数推理场景INT84-8倍1-3%对吞吐敏感、精度容忍度高INT48倍以上5%实验性慎用3.3 图优化pass的实际作用昇腾推理引擎开源后图优化pass的代码是可以看到的。我花了一些时间读这部分发现几个比较有意思的优化策略。第一个是算子融合的粒度控制。不是融合得越多越好因为融合后的算子如果太大会超出片上存储容量反而导致数据反复搬运。开源代码里能看到融合策略会根据算子的输入输出大小和片上存储容量做权衡。第二个是内存复用规划。推理过程中会产生很多中间张量如果每个都单独分配内存显存很快就不够了。昇腾推理引擎会分析张量的生命周期把不再需要的张量内存回收给后续张量使用。这个复用策略在开源代码里有比较清晰的实现。第三个是数据排布转换的消除。不同算子对输入数据的排布要求可能不同比如有的要求NCHW有的要求NHWC。如果每个算子都做一次排布转换开销很大。图优化会尽量把排布转换合并或者消除。3.4 自定义算子开发的关键步骤当ATC报出不支持的算子时自定义算子开发就成了必经之路。昇腾提供了算子开发框架流程大致是定义算子原型、实现算子计算逻辑、注册算子信息、编译部署。算子原型定义需要指定输入输出个数、数据类型、shape推导规则。这部分用C写有固定的模板。计算逻辑可以用TBE DSL或者TIK两种方式实现前者更接近Python的编程习惯后者更底层但控制力更强。我建议优先用TBE DSL除非性能实在达不到要求再考虑TIK。TBE DSL的调试体验好很多而且昇腾的很多官方算子也是用DSL写的。提示自定义算子开发完成后需要用ST测试框架做单元测试确保算子在各种shape和数据类型下都能正确运行。这一步不能省否则上线后出问题排查成本极高。4. 实操过程与核心环节实现4.1 环境搭建与版本对齐昇腾的开发环境搭建有个坑CANN版本、驱动版本、固件版本三者必须匹配。我见过太多次因为版本不匹配导致模型转换成功但推理报错的案例。推荐的做法是先确定CANN版本然后根据CANN版本查对应的驱动和固件版本。安装顺序是驱动→固件→CANN→推理引擎。每一步装完都用npu-smi info确认设备状态正常。Python环境方面昇腾推理引擎的Python接口依赖特定版本的numpy和protobuf。我建议用conda创建一个独立环境避免和系统Python冲突。conda create -n ascend_infer python3.9 conda activate ascend_infer pip install numpy1.23.5 protobuf3.20.34.2 模型转换的完整流程与参数计算以一个PyTorch训练的ResNet50为例完整流程是PyTorch→ONNX→ATC→om。PyTorch导出ONNX时要注意opset版本昇腾对opset 11和13的支持比较好。导出命令torch.onnx.export(model, dummy_input, resnet50.onnx, opset_version11, input_names[input], output_names[output], dynamic_axes{input: {0: batch}})ATC转换时的参数需要根据实际部署场景计算。比如batch size如果部署时最大并发是8那--input_shape就写input:8,3,224,224。但要注意batch size越大编译时间越长内存占用也越大。我一般会先用小batch转换验证正确性再用大batch做最终部署。精度模式的选择前面说过了这里补充一个细节--precision_mode有五个可选值force_fp16会强制所有算子用FP16allow_mix_precision会智能选择must_keep_origin_dtype保持原始精度。我通常用allow_mix_precision然后在精度不达标时针对特定算子做调整。4.3 推理服务部署与性能调优om模型生成后部署方式有几种直接用pyACL做单模型推理、用MindX SDK做多模型流水线、或者集成到Triton Inference Server里。单模型推理的代码结构比较固定初始化ACL→加载模型→创建输入输出dataset→执行推理→获取结果。关键优化点在于输入输出的内存复用和异步推理。内存复用是指不要每次推理都重新分配输入输出内存而是在初始化时分配好推理时直接往固定地址写数据。异步推理是指用多个stream并行执行推理任务充分利用NPU的计算单元。我实测过一个场景单stream同步推理ResNet50310P3上单帧耗时约8ms。改成4个stream异步推理后吞吐量提升了将近3倍平均单帧耗时降到3ms左右。这个提升在视频分析类应用里非常关键。4.4 精度调优的实操记录精度调优是个细致活。我的一般流程是先用FP32跑一遍记录每个层的输出作为基准。然后换FP16逐层对比输出差异。如果某一层差异特别大就单独把这层设成FP32。昇腾的ATC工具支持通过配置文件指定某些层保持FP32。配置文件格式是JSON指定层名和精度模式。这个功能在混合精度调优时非常有用。INT8量化的话校准数据集的选择很关键。校准集要能覆盖实际推理时可能出现的各种输入分布数量不用太多几百张就够但分布要全。我见过用单一场景的图片做校准结果换场景后精度暴跌的案例。5. 常见问题与排查技巧实录5.1 模型转换失败类问题问题一ATC报Unsupported op type这是最常见的。先确认这个算子在当前CANN版本里是否支持查算子支持列表。如果不支持考虑用支持的算子组合替换或者开发自定义算子。问题二转换成功但推理结果全零或异常大概率是输入数据格式不对。昇腾的输入数据需要是连续的NDArray而且排布要和模型定义一致。检查一下输入数据的shape和dtype以及是否需要做归一化。问题三动态shape转换失败昇腾对动态shape的支持有限特别是某些算子不支持动态shape。如果必须用动态shape尝试把动态维度设在batch维度上空间维度尽量固定。5.2 推理性能不达预期类问题问题一吞吐量远低于理论值先检查是不是同步推理改成异步多stream。然后看内存分配是不是每次都在重新分配改成预分配。再检查有没有不必要的Host-Device数据拷贝。问题二首帧耗时特别长这是正常的首次推理会触发模型加载和内存分配。如果首帧耗时过长检查模型文件大小和加载方式。可以把模型预加载到内存里避免每次启动都重新加载。问题三多模型并行时性能下降昇腾310P3的算力是有限的多模型并行会争抢计算资源。建议根据模型的计算量做合理的stream分配计算量大的模型单独占一个stream小模型可以共享。5.3 精度异常类问题问题一FP16精度损失过大逐层对比FP32和FP16的输出找到差异大的层把这层设成FP32。通常是一些对数值范围敏感的层比如Softmax之前的层。问题二INT8量化后精度暴跌检查校准数据集是否具有代表性。尝试用QAT代替PTQ。如果还不行考虑混合量化只对部分层做INT8。问题三不同batch size下精度不一致这通常是因为batch size变化导致某些算子的计算路径变化。检查是否有算子对batch size敏感必要时固定batch size。问题类型典型现象排查方向解决手段转换失败Unsupported op算子支持列表替换算子/自定义算子推理异常输出全零输入格式检查shape/dtype/排布性能不足吞吐低推理模式异步多stream/内存复用精度损失准确率下降逐层对比混合精度/校准集优化5.4 独家避坑经验第一个坑不要迷信官方提供的性能数据。官方benchmark通常是在理想条件下测的实际业务场景的输入分布、并发模式都不一样。一定要用自己的数据实测。第二个坑版本升级要谨慎。昇腾的软件栈迭代快新版本可能引入不兼容的变更。升级前先在测试环境验证确认模型转换和推理都正常再上生产。第三个坑日志级别不要开太高。调试时开DEBUG日志能看到详细信息但生产环境开DEBUG会严重影响性能。建议生产环境用ERROR级别需要排查问题时再临时调整。第四个坑模型文件要版本管理。om模型和CANN版本绑定不同版本的CANN生成的om模型可能不通用。建议把om模型和对应的CANN版本一起做版本管理避免部署时搞混。6. 开源生态下的扩展玩法与个人体会6.1 把昇腾推理引擎集成到现有系统里开源之后最直接的好处是可以把昇腾推理能力嵌到自己的系统里。比如你有一个用FastAPI写的推理服务之前可能是调ONNX Runtime现在可以换成昇腾推理引擎的Python接口。集成的关键点是错误处理和资源管理。昇腾的ACL接口在出错时会返回错误码需要做完善的错误处理避免设备资源泄漏。资源管理方面建议用上下文管理器或者装饰器来确保设备初始化和去初始化成对出现。另一个玩法是自定义图优化pass。开源代码里图优化的框架是开放的你可以注册自己的pass来做特定模型的优化。比如你知道某个模型的某几个算子总是连续出现可以写一个pass把它们融合成一个自定义算子。6.2 和开源模型社区的联动现在开源模型越来越多很多模型直接提供了ONNX导出脚本。用昇腾推理引擎跑这些开源模型流程是通的但需要注意几点。一是算子兼容性。开源模型可能用到一些比较新的算子昇腾的算子库不一定都支持。建议先在ATC里转一遍看报不报错。二是输入预处理。开源模型的预处理逻辑通常写在Python代码里部署到昇腾上时这部分预处理要么在Host侧做要么用昇腾的DVPP硬件做。DVPP做图像解码和缩放效率很高建议优先用。三是后处理。检测模型的后处理NMS等如果放在NPU上做需要算子支持。如果不支持就放到CPU上做但要注意数据在Host和Device之间的拷贝开销。6.3 我个人在实际操作中的体会折腾昇腾推理引擎这段时间最大的感受是开源带来的最大价值不是省了钱而是省了猜的时间。以前遇到问题只能靠试现在可以读代码看逻辑排查效率完全不一样。另一个体会是昇腾的生态还在快速演进中文档和社区资源不如CUDA生态丰富。这意味着你需要有更强的自学能力和排查能力。但反过来看这也是机会——早期投入学习的人在项目里会更有竞争力。最后分享一个小技巧昇腾的官方示例仓库里有不少端到端的案例从模型转换到推理部署都有。建议新手先从这些示例跑通建立信心然后再改造成自己的模型。直接上手自己的复杂模型很容易在某个环节卡住然后失去耐心。这个领域后续还可以往几个方向扩展一是多模型流水线的编排二是动态batch和动态shape的深度使用三是和Kubernetes等容器编排系统的集成。每个方向都有不少值得挖的细节等后面有机会再展开聊。
返回列表