最近公司采购了一批带NPU(神经网络处理单元)的新服务器,领导让我把现有的AI推理服务从旧的CPU/GPU系统迁移到新的NPU系统上,目标是提升推理性能,降低成本。
最开始我觉得这应该不难,不就是把模型转一下格式,然后用NPU的推理框架跑起来嘛。结果真正做起来才发现,NPU迁移坑很多,从模型转换到性能优化,从兼容性处理到精度损失,每一步都有不少问题。花了将近三周时间,才把整个迁移工作做完,性能提升了不少,但也踩了很多坑。
这篇文章就来分享一下这次NPU迁移的实战经历,包括迁移前的评估、模型转换、性能优化、兼容性处理、踩坑经验,以及迁移后的效果对比和总结。希望能给准备做NPU迁移的朋友一些参考。
什么是NPU
先简单介绍一下NPU。
NPU(Neural Processing Unit,神经网络处理单元)是一种专门用于神经网络计算的硬件加速器,和CPU、GPU类似,都是计算芯片,但设计目标不同。
CPU是通用处理器,什么都能算,但针对神经网络的大规模并行计算效率不高。GPU是图形处理器, originally 是用来做图形渲染的,但因为它的并行计算能力很强,后来被广泛用于AI训练和推理。NPU是专门为神经网络计算设计的,它在架构上针对神经网络的常见运算(比如卷积、矩阵乘法、激活函数等)做了专门的优化,在AI推理方面的性能和能效比都比CPU和GPU高很多。
简单来说,CPU是通用计算,GPU是并行计算,NPU是专门的神经网络计算。对于AI推理这种特定的工作负载,NPU的性能更高,功耗更低,成本也更低。
最近几年,随着AI应用的普及,NPU越来越火。不仅手机、平板等移动设备上普遍搭载了NPU(比如苹果的Neural Engine、高通的Hexagon NPU、华为的达芬奇NPU等),服务器和数据中心也开始使用NPU来加速AI推理(比如华为的昇腾、寒武纪的思元、谷歌的TPU等)。NPU正在成为AI基础设施的重要组成部分。
我们公司这次采购的新服务器,就是搭载了某国产NPU芯片的服务器,主要用来跑AI推理服务。领导希望用NPU替代原来的GPU推理,降低硬件成本,同时提升推理性能。
迁移前的评估
迁移的第一步是评估,搞清楚现有的系统是什么样的,迁移到NPU上有没有什么障碍,预期的收益有多大。
我先梳理了一下现有的AI推理服务。我们总共有大概10个AI模型在生产环境运行,包括图像分类、目标检测、人脸识别、OCR文字识别、语义分割、自然语言处理等不同类型的模型。模型框架主要是PyTorch和TensorFlow,模型格式有PyTorch的.pt、TensorFlow的.pb、ONNX等。推理框架用的是ONNX Runtime和TensorRT,运行在GPU服务器上。
然后我评估了NPU的支持情况。我们采购的NPU有自己的推理框架和工具链,支持常见的模型框架和算子。但不是所有的算子都支持,有些不常见的算子可能需要自定义实现,或者用其他算子替代。而且NPU的推理框架有自己的模型格式,需要把现有模型转换成NPU支持的格式。
我还做了一个简单的性能测试,选了几个代表性的模型,分别在GPU和NPU上跑了一下,看看性能差异。测试结果显示,对于大部分模型,NPU的推理延迟比GPU低30%到50%,吞吐量高50%到100%,而且功耗只有GPU的三分之一左右。这个结果还是很让人振奋的,说明迁移到NPU确实能带来明显的收益。
但评估中也发现了一些问题:
第一,有些模型的算子NPU不支持,需要做模型改造或者自定义算子。比如某个OCR模型用了一个比较特殊的后处理算子,NPU的推理框架不支持,需要把这个算子移到CPU上做,或者用其他支持的算子重新实现。
第二,模型转换过程中可能会有精度损失。NPU为了提升性能,通常会使用量化技术(比如INT8量化),把浮点模型转换成定点模型。量化可能会带来一定的精度损失,特别是对于对精度要求高的模型(比如人脸识别、医疗影像等),需要仔细评估精度损失是否在可接受范围内。
第三,NPU的推理框架和现有系统的集成需要做一些开发工作。现有的推理服务是基于ONNX Runtime和TensorRT的,API和NPU的推理框架不一样,需要修改推理服务的代码,适配NPU的推理框架。
第四,运维和监控体系需要适配。现有的监控体系是针对GPU服务器的,NPU的监控指标和GPU不一样,需要开发新的监控指标和告警规则。
评估完之后,我给领导做了一个汇报,说明了迁移的预期收益、存在的问题、需要的时间和资源。领导认可了迁移方案,让我开始正式实施。
模型转换
迁移的核心工作是模型转换,把现有的PyTorch/TensorFlow/ONNX模型转换成NPU支持的模型格式。
NPU的工具链提供了模型转换工具,支持从ONNX、TensorFlow、PyTorch等格式转换到NPU的模型格式。转换的基本流程是:先把模型转换成ONNX格式(如果还不是ONNX的话),然后用NPU的转换工具把ONNX模型转换成NPU的模型格式,转换过程中可以选择是否量化(FP16、INT8等)。
听起来很简单,但实际做起来坑很多。
第一个坑是ONNX导出的问题。PyTorch模型导出ONNX的时候,有些操作可能导出失败,或者导出的ONNX模型和原模型的行为不一致。比如某个模型用了动态shape,导出ONNX的时候需要指定固定的输入尺寸,这就导致模型只能处理固定尺寸的输入,不能处理可变尺寸的输入。还有些模型用了PyTorch的一些高级操作,ONNX不支持,导出的时候会报错,需要修改模型代码,用ONNX支持的操作重新实现。
为了解决这些问题,我花了不少时间修改模型代码,确保能正确导出ONNX。对于动态shape的问题,我采用了多种尺寸导出多个模型的方式,或者在推理的时候做padding,把输入pad到固定尺寸。对于不支持的操作,我查了ONNX的算子文档,用支持的算子组合实现了相同的功能。
第二个坑是NPU转换工具的兼容性问题。NPU的转换工具虽然支持ONNX,但不是所有的ONNX算子都支持,特别是一些比较新的或者比较少见的算子。转换的时候经常会遇到"unsupported operator"的错误,需要一个个处理。
处理不支持算子的方法有几种:第一种是用其他支持的算子替代,比如某个不支持的激活函数,可以用支持的激活函数组合实现;第二种是把这个算子移到CPU上做,模型在NPU上推理到这个算子的时候,把数据拷回CPU,在CPU上执行这个算子,然后再拷回NPU继续推理,这种方法叫"异构计算",虽然会有一定的性能损失,但能保证模型能跑起来;第三种是自定义算子,用NPU提供的自定义算子接口,自己实现这个算子的NPU版本,这种方法性能最好,但开发成本高,需要了解NPU的底层架构。
我根据不同算子的情况,分别采用了不同的处理方法。对于容易替代的算子,用支持的算子替代;对于不容易替代但计算量不大的算子,移到CPU上做;对于计算量大且频繁使用的算子,花时间做了自定义实现。
第三个坑是量化的精度损失。为了充分发挥NPU的性能,我对模型做了INT8量化。量化的过程是用一批校准数据跑一遍模型,统计每个张量的数值范围,然后把浮点数值映射到INT8的定点数值。量化之后模型的体积变小了,推理速度变快了,但精度可能会有一定的损失。
我在量化的时候遇到了几个问题:一是校准数据的选择很重要,如果校准数据不能代表真实的输入分布,量化后的精度损失会很大。我花了不少时间准备有代表性的校准数据集,确保覆盖各种典型的输入场景。二是有些层对量化比较敏感,量化后精度损失大,需要把这些层保留为浮点(FP16或FP32),其他层用INT8,这种混合量化的方式能在性能和精度之间取得平衡。三是量化后需要做精度验证,用测试数据集对比量化前后的模型输出,确保精度损失在可接受范围内。
经过这些处理,大部分模型都成功转换到了NPU格式,精度损失控制在了1%以内,对于大部分应用场景来说是可以接受的。
性能优化
模型转换完成之后,下一步是性能优化,让模型在NPU上跑得尽可能快。
虽然NPU本身的性能就比GPU高,但如果不做优化,可能只能发挥出NPU 60%到70%的性能。通过一系列优化,我把大部分模型的性能又提升了20%到30%。
我做的优化主要有以下几个方面:
第一,批处理(Batch)优化。NPU和GPU一样,批处理能提升吞吐量。原来的推理服务是单条推理(batch size=1),延迟低但吞吐量不高。我根据不同模型的特点,调整了batch size,对于对延迟要求不高的离线推理任务,用大batch(比如8、16、32),大幅提升吞吐量;对于对延迟要求高的在线推理任务,用小batch(比如1、2、4),在延迟和吞吐量之间取得平衡。
第二,输入数据预处理优化。推理过程中,输入数据的预处理(比如图像的resize、归一化、颜色空间转换等)往往也会占用不少时间。原来的预处理是在CPU上用Python做的,效率不高。我把部分预处理操作移到了NPU上做,比如归一化、颜色空间转换等,NPU处理这些操作比CPU快很多。对于必须在CPU上做的预处理(比如图像解码),我用了多线程和SIMD指令优化,提升了预处理速度。
第三,内存和数据传输优化。NPU推理过程中,数据需要在CPU内存和NPU内存之间传输,数据传输的开销有时候会占总推理时间的很大比例。我做了几个优化:一是减少不必要的数据传输,能在NPU上做的操作都在NPU上做,减少CPU和NPU之间的数据拷贝;二是使用零拷贝技术,让CPU和NPU共享内存,避免数据拷贝;三是流水线处理,把数据传输和NPU计算重叠起来,当前一个batch在NPU上推理的时候,同时准备下一个batch的输入数据,隐藏数据传输的开销。
第四,模型结构优化。有些模型本身的结构就不够高效,在NPU上推理的时候性能不好。我对部分模型做了结构优化:一是去掉冗余的层和操作,有些模型为了训练方便加了一些推理时不需要的层(比如Dropout、BatchNorm的训练态等),推理的时候可以去掉或者融合;二是算子融合,把多个小算子融合成一个大算子,减少算子调度的开销,比如把卷积+BN+激活融合成一个卷积算子;三是用更高效的模型结构替代,比如用深度可分离卷积替代标准卷积,用更高效的backbone(比如MobileNet、EfficientNet)替代原来的重型backbone。
第五,推理框架参数调优。NPU的推理框架有很多可调参数,比如线程数、内存池大小、调度策略等。我根据服务器的硬件配置和模型的特点,调整了这些参数,让推理框架的性能达到最优。比如对于计算密集型的模型,增加NPU的计算单元分配;对于内存密集型的模型,增加内存池的大小;对于多模型并发的场景,调整调度策略,避免资源竞争。
这些优化做完之后,大部分模型的推理延迟又降低了20%到30%,吞吐量提升了30%到50%,NPU的性能得到了比较充分的发挥。
兼容性和稳定性处理
性能优化之后,还需要处理兼容性和稳定性的问题,确保迁移后的服务能稳定运行在生产环境。
第一个问题是和现有系统的兼容性。现有的推理服务是基于ONNX Runtime和TensorRT的,有一套完整的服务框架,包括模型管理、请求调度、结果缓存、监控告警等。迁移到NPU之后,推理框架变了,需要修改服务框架的代码,适配NPU的推理框架。
我没有完全重写服务框架,而是做了一个推理引擎的抽象层,定义了统一的推理接口,然后分别实现了GPU版本和NPU版本的推理引擎。这样服务框架只需要调用统一的接口,不需要关心底层是GPU还是NPU,切换起来很方便。而且这样做也方便以后支持其他推理硬件(比如不同厂商的NPU、FPGA等)。
第二个问题是多模型管理。我们有10个不同的模型,每个模型的输入输出格式、预处理后处理、推理参数都不一样。NPU的推理框架对同时加载多个模型的支持和GPU不太一样,需要仔细管理模型的加载和卸载,避免内存不足。
我做了一个模型管理器,负责模型的加载、卸载、调度。根据模型的使用频率和内存占用,动态地加载和卸载模型:常用的模型常驻内存,不常用的模型按需加载,内存不足的时候自动卸载最久未使用的模型。这样既保证了常用模型的响应速度,又避免了内存不足的问题。
第三个问题是异常处理和容错。NPU推理过程中可能会出现各种异常,比如模型加载失败、推理超时、内存不足、硬件故障等。需要完善的异常处理机制,确保服务不会因为某个请求的异常而崩溃,也不会因为某个模型的故障而影响其他模型。
我在推理引擎里加了完善的异常捕获和处理逻辑,每个请求都有超时控制,异常请求会被快速失败并返回错误信息,不会阻塞其他请求。对于模型加载失败或者硬件故障的情况,有自动重试和降级机制,比如NPU不可用时自动降级到CPU推理,保证服务的可用性。
第四个问题是监控和告警。迁移到NPU之后,原来的GPU监控指标(比如GPU利用率、显存使用、温度等)不再适用,需要开发NPU的监控指标。NPU的工具链提供了一些性能计数器和状态查询接口,我基于这些接口开发了监控指标,包括NPU利用率、NPU内存使用、推理延迟、吞吐量、错误率等,并接入了现有的监控系统(Prometheus+Grafana),设置了告警规则,确保能及时发现和处理问题。
这些兼容性和稳定性处理做完之后,迁移后的服务就具备了在生产环境运行的条件。
踩坑经验
整个迁移过程中踩了很多坑,这里总结几个印象最深的,希望大家能避免。
第一个坑:不要迷信官方文档和benchmark。NPU厂商提供的官方文档和性能benchmark看起来都很美好,但实际用起来可能不是那么回事。官方的benchmark通常是在理想条件下测的,用的是优化得很好的模型,数据也是精心准备的。但实际项目中的模型可能有各种奇怪的算子和结构,数据分布也不一样,实际性能可能比benchmark低很多。所以在迁移之前一定要自己做性能测试,用自己的模型和数据测,不要只看官方的数据。
第二个坑:模型转换不是一键完成的。很多人以为模型转换就是点一下按钮的事情,实际上远不是这样。不同框架之间的差异、算子的不支持、动态shape的问题、量化的精度损失等,每一个都可能让你卡很久。一定要预留足够的时间做模型转换,不要以为一周就能搞定,实际可能需要两三周甚至更久。而且最好先挑一两个代表性的模型做试点,验证可行性,再全面铺开,不要一上来就把所有模型都转,结果发现有问题返工。
第三个坑:量化的精度损失要认真评估。量化是提升NPU性能的重要手段,但精度损失不能忽视。特别是对于对精度要求高的应用(比如人脸识别、医疗影像、金融风控等),1%的精度损失可能就会导致业务问题。一定要用真实的测试数据集做精度验证,对比量化前后的模型输出,确保精度损失在可接受范围内。如果精度损失太大,可以考虑混合量化(敏感层保留浮点),或者用更高级的量化算法(比如QAT量化感知训练)。
第四个坑:预处理和后处理的性能不要忽视。很多人只关注模型推理本身的性能,忽视了预处理和后处理的性能。实际上在很多应用中,预处理和后处理的时间可能占总推理时间的30%到50%,特别是对于轻量级模型,模型推理很快,但预处理很慢,整体性能就上不去。一定要把预处理和后处理也纳入性能优化的范围,能移到NPU上做的就移到NPU上做,必须在CPU上做的也要优化代码,用多线程、SIMD等技术提升速度。
第五个坑:运维和监控要提前考虑。很多人在迁移的时候只关注模型和性能,忽视了运维和监控。结果迁移完了才发现,原来的监控体系不适用NPU,出了问题都不知道,排查起来也很困难。一定要在迁移初期就考虑运维和监控的问题,了解NPU提供了哪些监控接口,提前开发监控指标和告警规则,确保迁移后的服务可观测、可运维。
第六个坑:要有回滚方案。迁移不是100%成功的,可能会遇到各种预料之外的问题,导致迁移后的服务不稳定或者性能不达标。一定要有回滚方案,迁移过程中保留原来的GPU服务,出了问题能快速切回GPU。不要把原来的服务停了才发现新服务有问题,导致业务中断。可以用灰度发布的方式,先把一部分流量切到NPU,观察一段时间没问题再全量切换,有问题随时回滚。
这些坑都是我用时间和精力换来的,希望大家在做NPU迁移的时候能少走弯路。
迁移效果
经过将近三周的工作,10个模型全部成功迁移到了NPU上,并且在生产环境稳定运行了一段时间。整体效果还是很让人满意的。
性能方面,大部分模型的推理延迟比GPU降低了30%到50%,吞吐量提升了50%到100%。特别是对于计算密集型的模型(比如图像分类、目标检测),NPU的优势非常明显,延迟降低了将近一半,吞吐量翻了一倍。对于计算量不大的轻量级模型,NPU的优势相对小一些,但也有20%到30%的提升。
成本方面,NPU服务器的采购成本只有GPU服务器的60%左右,功耗只有GPU的三分之一,运维成本也更低。算下来,单路推理的硬件成本降低了将近60%,对于我们这种推理量比较大的业务来说,一年能节省不少钱。
精度方面,通过混合量化和仔细的校准,大部分模型的精度损失控制在了0.5%以内,对业务没有明显影响。只有一个对精度要求特别高的人脸识别模型,精度损失稍微大一点(1.2%),我们对这个模型做了量化感知训练(QAT),把精度损失降到了0.3%以内,满足了业务要求。
稳定性方面,迁移后的服务在生产环境运行了一个月,没有出现重大故障,可用性达到了99.9%以上。监控和告警体系也运行正常,能及时发现和处理问题。运维团队反馈,NPU服务器的运维比GPU服务器更简单,因为NPU的驱动和推理框架更稳定,不需要像GPU那样频繁更新驱动和CUDA版本。
总的来说,这次NPU迁移是成功的,达到了预期的目标,既提升了性能,又降低了成本。领导对迁移结果也很满意,决定后续新上的AI模型都优先部署在NPU上,逐步把所有的GPU推理服务都迁移到NPU上。
给准备做NPU迁移的朋友的建议
最后给准备做NPU迁移的朋友一些建议。
第一,先做充分的评估和试点。不要一上来就全面迁移,先做充分的评估,了解NPU的能力和限制,用自己的模型和数据做性能测试和精度验证。然后挑一两个代表性的模型做试点,跑通整个迁移流程,验证可行性,积累经验,再全面铺开。
第二,预留足够的时间。NPU迁移不是一周两周就能搞定的事情,模型转换、性能优化、兼容性处理、测试验证,每一步都需要时间。一定要预留足够的时间,不要把排期排得太紧,避免因为时间不够而仓促上线,导致问题。
第三,重视精度和稳定性,不要只看性能。NPU的性能确实好,但不能为了性能牺牲精度和稳定性。一定要用真实的测试数据做精度验证,确保精度损失在可接受范围内。也要做充分的稳定性测试,确保服务能长时间稳定运行。性能再好,精度不达标或者不稳定,也是没用的。
第四,建立完善的运维和监控体系。迁移不是把模型跑起来就完事了,还要考虑长期的运维和监控。一定要建立完善的监控体系,能实时观测NPU的运行状态、推理性能、错误率等指标,设置合理的告警规则,出了问题能及时发现和处理。也要有完善的运维流程,包括模型更新、版本发布、故障排查、回滚等。
第五,保持学习和开放的心态。NPU是一个比较新的技术,不同厂商的NPU架构和工具链差异很大,而且技术更新很快。一定要保持学习的心态,不断学习新的技术和工具,了解NPU的最新发展。也要保持开放的心态,不要局限于某一家厂商的NPU,不同的场景可能适合不同的NPU,要根据实际需求选择最合适的方案。
第六,不要神化NPU,也不要贬低NPU。NPU不是银弹,不是所有模型在NPU上都能跑得又快又好。有些模型(比如有大量不支持算子的模型、对精度要求极高的模型、动态shape特别复杂的模型)在NPU上可能效果不好,甚至跑不起来。这时候不要强求,该用GPU还是用GPU,或者用CPU+NPU异构的方式。但也不要因为遇到一些问题就否定NPU,大部分常见的AI模型在NPU上都能取得不错的效果,只要花时间优化,性能和成本优势还是很明显的。
这些建议希望能帮到大家,祝大家的NPU迁移都能顺利成功。
写在最后
这次NPU神经网络单元迁移实战,从旧系统到新系统,花了将近三周时间,踩了很多坑,也收获了很多。
我对NPU有了更深入的理解,对AI推理的硬件加速有了更实际的认识,也积累了模型转换、性能优化、兼容性处理等方面的经验。更重要的是,我认识到技术迁移不是简单的换个硬件跑起来,而是一个系统工程,需要从评估、转换、优化、测试、运维等多个方面全面考虑,才能真正做好。
随着AI应用的普及,NPU作为AI推理的专用硬件,会越来越重要,越来越普及。未来不仅服务器和数据中心会用NPU,边缘设备、终端设备也会越来越多地使用NPU。作为AI开发者,了解和掌握NPU的迁移和优化技术,会成为一项重要的技能。
如果你正在做NPU迁移,或者准备做NPU迁移,希望我的经历能给你一些参考。如果有什么问题或者更好的经验,欢迎在评论区交流,一起学习,一起进步。
技术在发展,硬件在迭代,我们一直在学习的路上。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录