从张量到NPU:一次端侧AI推理的硬件执行全解析

发布时间:2026/10/4 14:05:35
从张量到NPU:一次端侧AI推理的硬件执行全解析 这两年只要聊到端侧AI张量和NPU这两个词几乎绕不开。云端大模型再热闹手机、PC、车机、摄像头这些设备终究要自己扛一部分推理任务——本地推理靠什么跑靠的是把每一层神经网络都翻译成张量运算再把张量运算压到专门的硬件单元上执行。这就是NPU存在的全部理由。最近AMD、Intel、高通陆续把几十TOPS的NPU塞进消费级芯片各家大模型也在往端侧挤但真正能把一次推理在硬件上发生了什么讲清楚的文章并不多。这篇文章我想沿着一次推理的完整链路从数据怎么变成张量到张量怎么被NPU吃掉把端侧AI的底层执行逻辑彻底拆一遍。适合正在做端侧部署、被量化和算子兼容性折磨的工程师也适合想搞明白“端侧AI到底在跑什么”的产品、测试和对硬件好奇的朋友们。1. 端侧AI绕不开的两个“底层事实”张量与NPU1.1 张量为什么是AI的数据母语先说张量。很多人一听到“张量”就觉得高深实际上它就是多维数组是AI领域所有数据的统一表达格式。标量是一个数向量是一串数矩阵是二维的数表张量就是把这个概念推广到任意维度。一张720p的RGB图片在模型眼里就是一个形状为[3, 720, 1280]的张量一个64字符长度的文本序列映射成向量后形状可能是[1, 64, 512]一段3秒的音频切片也同理。为什么非要用张量因为神经网络从数学本质上就是一堆矩阵乘法和向量运算。卷积是对输入张量做滑动窗口乘加注意力机制里Q、K、V矩阵相乘全连接层就是一个大矩阵乘以输入向量。把这些操作统一成张量运算之后硬件设计者就不用去写各种五花八门的定制指令只要把张量运算做快整个AI推理就都快了。我做端侧部署时最直观的一个感受是模型的精度问题、速度问题、显存或者NPU片上内存问题追到最后几乎都能归到张量形状、张量排布和张量搬运上。你把自己的思考坐标系切换成张量之后很多看似玄学的问题就变成了数学题。比如[1, 3, 224, 224]的输入过一层卷积输出[1, 64, 112, 112]这中间要多少次乘法、产生多大的中间结果全部可算、可预估。1.2 NPU为了矩阵乘法而生的专用引擎CPU在复杂控制流上是王者GPU在通用并行计算上很强但AI推理场景里绝大部分时间花在矩阵乘累加MAC上。NPU就是针对MAC运算做了极限优化的专用芯片。CPU的ALU算术逻辑单元数量和寄存器带宽有限跑一遍大矩阵乘法需要反复从内存里取指令和取数据算力大部分浪费在搬运上这就是“冯·诺依曼瓶颈”。GPU用几千个流处理器并行做矢量运算看起来解决了问题但通用计算单元功耗高、调度贵放到手机或者X86轻薄本上实在不太划算。NPU的做法更极端直接把成百上千个乘法器排成阵列典型的就是脉动阵列结构数据在阵列里只进不出边移动边算A矩阵的每一行和B矩阵的每一列在阵列里不断碰撞、相乘、累加一个时钟周期能完成几千甚至几万次MAC操作。以目前市面上主流的端侧NPU为例最高16核心的配置能提供30~50 TOPS的整数算力INT8个别旗舰平台峰值能到80 TOPS以上。什么概念跑一个主流规模的视觉模型CPU上要几十毫秒NPU上可以做到个位数毫秒级。MAC阵列把“算力密度”推到了极致这就是NPU存在的全部理由。1.3 端侧与云端执行逻辑的根本差异端侧AI的“底层执行逻辑”之所以和云端不一样核心差距在三个词带宽、功耗、内存。云端GPU连的是HBM高带宽显存带宽动辄几个TB每秒端侧NPU和CPU共享一套内存访问带宽常常只有几十GB每秒。云端算力可以跑到几千TOPS功耗可以放到几百瓦端侧一个NPU的功耗预算往往只有几瓦能效比成了所有设计的指挥棒。这意味着端侧推理的每一步都必须精打细算。我做端侧部署时首先看的是“数据在动还是算子在算”因为搬运数据比计算本身更贵。很多新手把模型从云端端到端侧跑出来发现根本没快多少问题几乎都出在频繁的CPU和NPU数据拷贝上。端侧执行逻辑的本质是在有限内存和有限带宽的约束下最大化NPU计算单元的利用率。2. 一次推理在硬件上到底发生了什么2.1 模型文件只是一本“菜谱”训练好的模型文件里面包含两部分东西网络结构菜谱和权重参数食材。PyTorch的.pt文件、ONNX文件本质上都是序列化后的计算图和权重数值。真正要看懂它在NPU上怎么执行第一步是把模型“翻译”成硬件能懂的指令序列。这个过程通常分为三站第一站把模型转成开放中间表示ONNX统一算子边界第二站各家NPU工具链拿到ONNX图做算子融合、布局转换、量化插入得到一张“硬件友好的优化图”第三站把优化图编译成NPU原生指令生成的二进制文件最终由NPU驱动加载执行。你可以把这三站理解成厨师拿到菜谱网络结构和食材权重先改写成中央厨房的标准工序ONNX再按本店厨房的设备重新编排工序优化图最后把每道工序变成灶台上的一次点火操作NPU指令。每一站都有各自的优化空间但普通人遇到最多的问题往往出在第二站——算子映射不了、量化丢了精度基本都是这一层出的问题。2.2 从计算图到算子序列一次卷积的解剖以最典型的卷积算子为例输入张量[N, C, H, W]权重[K, C, R, S]输出[N, K, OutH, OutW]。在NPU的视角里这件事首先会被重构成GEMM通用矩阵乘法形式把输入特征图按照卷积窗口切块重排成一个大矩阵把权重展开成另一个大矩阵然后做一次大矩阵乘法。这个变换叫Im2Col空间换时间对于NPU这种以矩阵乘法为核心的架构这是一笔划算的买卖。接下来硬件把GEMM分解成“片”tile级别的计算任务。因为片上SRAM容量有限一般几百KB到几MB不可能一次把整个矩阵塞进去于是把A矩阵按行分块、B矩阵按列分块逐块加载到片上算完一块再换下一块。这个分块尺寸是由SRAM容量和带宽共同决定的块太大装不下块太小搬运次数太多、计算单元饿肚子。所有NPU的上限性能基本就看这个分块策略能不能把MAC阵列喂饱。2.3 算子在NPU上怎么“算”从INT8到脉动阵列现在主流端侧NPU都支持混合精度FP16、INT8、INT4都有对应的执行单元。FP16精度高但MAC阵列面积占用大、吞吐低INT8的面积只有FP16的一半左右吞吐直接翻倍INT4还能再翻一倍但精度损失就需要靠量化手段补回来。执行时每个乘法器接收两个整数输入输出累加结果。因为有大量的累加操作需要精度兜底累加器通常还是用INT32来保精度。如果你把真实网络过一遍NPU的反汇编会看到类似这样的命令层级先配置DMA搬运源地址和目标地址再配置MAC阵列的算子和权重地址然后触发计算等待中断再搬运结果。整个过程和数据仓库的出入库一模一样——计算本身很快真正决定性能的是物流调度。这里有个我踩过的坑刚开始做NPU适配时看到NPU理论算力很高觉得跑个模型肯定飞快结果实测速度是理论值的十分之一都不到。后来用Profiler一看MAC阵列利用率才15%大量时间都花在等待DMA搬运上。数据搬不到片上再强的计算阵列也只能空转。2.4 “编译器”是那条隐藏的暗线不同厂商的NPU有各自的指令集和积木结构CPU/GPU时代“一次编译到处运行”的美好在NPU这边行不通。这也是为什么现在围绕NPU的工具链五花八门Intel有OpenVINO的NPU插件AMD的Ryzen AI生态走的是自带工具链高通、联发科也各有各的SDK。这几年社区里还有一个现象值得注意ComfyUI跑英特尔NPU的尝试陆续有人做出来了。基本原理就是通过OpenVINO的NPU后端把Stable Diffusion的UNet和VAE部分算子映射到NPU上执行。我实测过一个社区版本SD1.5出图时间能从纯CPU的好几分钟压到几十秒但前提是很多算子要做规避替换文本编码器之类的部分还是得跑在CPU上。这说明一个事实NPU工具链的成熟度远不能跟CUDA生态比能不能跑、跑得快不快很大程度上取决于编译器和算子库的完善程度而不只是硬件算力。3. 端侧部署的硬件选型与资源博弈3.1 CPU、GPU、NPU端侧算力该怎么选端侧AI能用的算力从来不止NPU一种。CPU胜在万能什么算子都能跑不需要额外的数据搬运但速度慢、功耗也不低GPU在带核显的平台上是“次强的加速器”跑FP16模型还算顺手但发热和耗电在移动场景下属于灾难NPU精通矩阵运算INT8吞吐极高能效比碾压前两者但支持算子有限遇到不支持的结构就抓瞎。我的经验是能落到NPU的算子尽量落到NPU落到NPU会产生大量搬运的算子留在CPU剩下的冷门算子交给GPU兜底这就是所谓的混合执行。只有把三个引擎当成一个“异构计算池”来统一调度端侧性能才可能拉到最优。很多端侧推理框架里都有这层调度逻辑——算子切分、引擎选择、数据交换的决策全部自动完成但这套机制远没有成熟到可以完全当甩手掌柜。3.2 内存统一寻址带来的甜头与陷阱端侧NPU最大的内存优势是“统一寻址”——CPU和NPU共享同一块物理内存不用像PCIe设备那样通过总线拷贝数据。以AMD的Ryzen AI平台为例NPU直接通过内存映射访问系统DRAMCPU侧写入权重NPU侧直接就能读出来省掉了一大块拷贝开销。但这个优势也会变成陷阱NPU访问系统内存要跟CPU争带宽。你的模型如果同时有多个线程在CPU上跑内存控制器忙不过来NPU的DMA延迟就会飙升整个推理延迟直接崩掉。所以端侧部署时不光要看NPU的算子耗时还要开会看整机的内存带宽占用曲线。个人经验是推理时尽量把CPU侧的负载降下来比如AI任务单独绑核避免视觉管线、音频任务跟NPU抢内存。3.3 实时性为什么端侧推理要“异步”云计算场景里推理服务通常走同步请求-响应模式客户端发来请求服务端算完返回结果。但端侧AI的场景很多是持续的、实时的——摄像头画面一帧一帧进来NPU要一帧一帧地算模型输出和传感器采集天然就是一个管道。这就是端侧推理框架普遍提供异步接口的原因。通过把DMA搬运、计算、结果处理三级流水打起来让NPU永远在处理当前帧而CPU同时在准备下一帧的输入。我刚开始做时习惯等待推理完成再处理下一帧帧率只能到10fps改成流水线之后同样硬件直接干到30fps。没有异步端侧NPU的算力利用率至少损失一半。3.4 能效比每瓦特算出多少Token端侧AI为什么不用GPU硬扛归根结底是能效。一张高性能独显跑Llama 2 7B量化版生成速度可能比NPU快但整个平台的功耗直接飙到200W以上一块手机SoC的NPU虽然只能达到几十TOPS但跑一个3B参数量的对话模型整机功耗控制在5W以内不成问题。和能效比挂钩的是散热和续航。台式机可以不在乎但笔记本、手机、智能眼镜这些场景里功耗直接决定产品能不能用。AMD做锐龙AI系列的思路在这一代产品里体现得很明显NPU部分专为INT8低功耗设计适合常驻后台的轻量AI任务比如人像抠图、会议降噪、意图识别这类场景下NPU的能效比确实是CPU和GPU无法替代的。4. 实操参考在带NPU的平台上从模型到端到端推理4.1 环境和硬件准备以典型的Windows带NPU平台为例比如Intel Core Ultra系列或AMD Ryzen AI系列。第一步是安装对应厂商的NPU驱动和工具链。Intel的话OpenVINO Toolkit是主线命令行装一下就可以AMD需要确认系统的NPU设备在设备管理器中可见再装对应的SDK和运行时。驱动没装好的时候推理框架会直接报“找不到设备”。我强烈建议第一次做NPU部署的老哥先跑一遍厂商自带的例程而不是直接上自己的模型。例程会验证NPU驱动、工具链和运行时链路是通的确认通了再进入模型转换环节。有时候一个晚上都在排查问题最后发现就是驱动版本和SDK不匹配这种事我至少遇到过五次。4.2 模型导出与量化实操模型转换路径一般是这样PyTorch训练好的模型先用torch.onnx.export导出ONNX然后用工具链的模型优化器把ONNX转成厂商的中间格式接着做量化。量化这里分两种PTQ训练后量化和QAT量化感知训练。PTQ的做法是把训练好的浮点模型拿校准数据集跑一遍统计每一层激活值的分布然后选量化参数scale和zero point。校准集的选择很关键我试过直接用100张训练图片做校准和一个认真挑选、覆盖各种光线场景的校准集效果差距能到几个百分点。对于一个3B模型FP16权重大概6GB转成INT4后只有大约1.8GB这就是为什么端侧能塞下大模型的原因。但如果要精度必须用QAT——在训练时模拟量化误差让模型“适应”低比特表示。一般的经验是视觉模型PTQ基本够用语言模型生成任务建议上QAT否则输出可能开始胡说八道。量化之后一定要做精度验证而不是只对比精度曲线要真正跑一批业务数据看端到端的质量。4.3 推理脚本与性能观测真正部署时不要用Python裸跑可用的选择是ONNX Runtime或者厂商SDK。以ONNX Runtime为例只要安装了对应NPU的EPExecution Provider代码层面改动很小核心就是在SessionOptions里把NPU设为执行提供方然后正常走run接口。性能观测要用厂商的Profiler工具看每个算子在NPU上的时钟周期、MAC利用率、DMA搬运量。这一步很多人忽略但恰恰是“从跑通到跑得快”的分水岭。我前阵子调一个模型发现光数据格式转换算子就占了整个推理时间的35%因为这些算子跑在CPU上和NPU的计算管线串行执行。把格式转换合进模型前处理里推理时间直接砍掉三分之一。4.4 进阶优化算子替换、图融合与并行化工具链的优化器会做一些常规图融合比如把卷积BNReLU合并成一个算子减少中间张量的读写。但到了NPU上还不够很多时候需要手工介入。我常用的几个手段第一算子替换。比如把自定义的复杂激活函数换成NPU原生支持的近似实现只要精度不掉速度能涨一大截。第二改变张量布局。NPU亲赖CHW排列PyTorch默认NCHW有时会触发大量数据重排提前在导出阶段就处理好布局能少很多拷贝。第三并行化拆分。如果模型里有两个独立的子图如果内存带宽够可以手动把它们拆到两个引擎上并行跑不过这个优化比较危险因为两个引擎同时访问内存会互相争抢带宽不一定更快。5. 常见问题与排查技巧实录5.1 量化掉点先看激活值分布最常见的坑就是量化后精度崩了。这时候别急着骂量化工具先做逐层分析把ONNX模型分成几段分别跑浮点和量化版本看哪段输出偏差最大。我遇到过LayerNorm在量化后直接丢了一半精度的情况——因为LayerNorm的输出范围很窄量化步长太粗细节全丢了。解法是保留下沉到高精度FP16上跑或者单独用更细的量化粒度。这类逐层对比的脚本不复杂但能省掉一堆瞎猜。5.2 算子不支持硬切还是回退NPU不支持的算子底层执行逻辑一般会“算子回退”也就是自动放回CPU执行。回退本身不是问题问题出在频繁回退——每个算子都来回跳转CPU和NPU之间不断同步性能稀碎。遇到这种情况推荐的做法是先看一下计算图把回退算子尽量聚拢在图的某一段避免穿插执行。甚至可以在工具链的配置里强制指定某些层全部走CPU或全部走NPU人为制造“连续的大块”减少切换次数。还有一招是改模型结构如果回退算子是个不重要的自注意力模块可以换一个等价的、NPU支持的算子实现这样精度几乎不变速度能快非常多。5.3 内存暴涨排查DMA和中间张量端侧推理最怕内存涨到爆。排查第一步是看中间张量有没有被反复复制。有些框架为了对齐或者格式转换会为每个中间结果单独开缓冲区内存开销成倍上涨。可以看Profiler里的“buffer allocation”列表找出最大的一块缓冲区分析它是哪个算子产生的。如果是格式转换产生的改成“就地转换”或者干脆不做对齐能省下几百MB。另一个经常被忽视的点是图输入和输出的固定拷贝。推理完成后输出张量需要拷回CPU侧如果还带着整个批次维度和原始精度内存很恐怖。尽量在模型尾部直接输出你需要的那一个向量切片不要全量拷回来再切。5.4 工具链版本兼容版本锁定的艺术NPU工具链更新极快新SDK可能修了旧Bug但也可能引入新问题。我的经验是每个项目都要做版本锁定无论是驱动、SDK还是推理框架在项目文档里明确记录不要用“最新版本”。否则项目A跑得好好的给项目B搭环境顺手升了个级然后项目A的模型直接无法编译。这种事在NPU生态里太常见了。如果遇到“昨天能跑今天不能跑”这种灵异事件先看一眼是不是系统自动更新把NPU驱动换了。显卡驱动自动更新大家都有概念但NPU驱动的自动更新真很少人防在组策略里把NPU设备的驱动更新锁住是老经验了。结尾跑通NPU之后的几点真实体会做了大半年端侧NPU部署我自己最大的感触是NPU的“快”从来不是拿来即用的快而是“调出来的快”。理论TOPS只是开始张量布局、数据搬运、量化策略、算子支持边界每一个环节都可能把性能打回原型。如果只让我给一条建议那就是先把Profiler跑起来看一眼你的MAC阵列利用率。低于30%的时候别去优化算子先解决数据喂不满的问题低于10%的时候先检查是不是大量回退到CPU了。把NPU当成一个“专吃矩阵海鲜的大胃王”物流跟不上菜单再豪华也是白搭。另外一个小技巧供参考部署一个模型到新平台之前先拿一个典型场景的测试输入跑一遍全链路从模型加载、量化、编译到推理把每一步的耗时都打点记录下来。这个基线会是你之后所有优化工作的参照系。端侧AI还在每天变样但这些底层的执行逻辑短时间不会变。把这几条链路吃透将来不管NPU换成谁家的芯片上手都会快很多。