端侧AI算力方案全解析:从SoC选型到Transformer部署实战
1. 端侧 AI 算力方案到底在解决什么问题1.1 从云端推理到本地计算的必然转向过去几年做 AI 应用绝大多数人的第一反应是把模型扔到云端买几张推理卡搭个服务客户端只管发请求收结果。这条路在早期确实省事模型迭代快、算力弹性好、客户端压力小。但真到了产品落地阶段问题就一个个冒出来了网络延迟不稳定用户在地铁里、电梯里、工厂车间里请求发不出去数据要上传到远端涉及隐私的场景根本过不了合规并发一上来云端推理成本线性增长用户量越大亏得越多还有一些场景要求毫秒级响应比如工业质检、机器人避障、实时翻译走一趟网络往返根本来不及。端侧 AI 算力方案要解决的就是这些事——把推理这件事从远端拉回到设备本地让智能发生在离数据最近的地方。所谓端侧范围其实很宽手机、平板、笔记本、智能摄像头、车载座舱、工业网关、机器人控制器甚至一块带 NPU 的开发板都算端侧设备。它们的共同点是算力有限、功耗受限、内存不大但对延迟、隐私、离线可用性有硬性要求。我接触过的项目里最典型的几类需求是这样的一类是消费电子比如手机相册的本地修图、实时字幕、语音助手唤醒要求低延迟、低功耗、不联网也能用一类是行业应用比如电力巡检、矿山监控、医疗影像初筛数据不能出本地必须在设备上完成推理还有一类是成本敏感型产品云端推理按调用量付费量大了之后端侧一次性投入反而更划算。1.2 端侧 AI 和云端 AI 的本质差异很多人一开始会把端侧 AI 简单理解成“把云端的模型搬到设备上跑”这个理解只对了一半。端侧和云端最大的差异不在模型本身而在约束条件完全不同。云端推理可以堆算力一张卡不够就加一张显存不够就换大卡散热和功耗基本不用太操心。端侧不行SoC 的功耗预算通常只有几瓦到十几瓦内存带宽有限散热靠被动散热或者小风扇模型稍微大一点就跑不动。所以端侧 AI 的核心矛盾是在有限的算力、内存、功耗预算下尽可能把模型跑起来并且跑得够快、够省电。这就引出了端侧方案设计的几个关键维度算力平台选型用哪颗 SoC、有没有 NPU、NPU 算力多少、模型压缩与转换量化、剪枝、算子替换、推理框架选择用什么 runtime 把模型跑起来、以及工程层面的调度与内存管理。这几个维度是相互耦合的选型的时候不能只看单点指标。1.3 这篇文章适合谁看如果你正在做端侧 AI 产品选型纠结用哪颗芯片、用哪个推理框架或者你手里已经有一块开发板想把 Transformer 类模型跑起来但不知道从哪下手又或者你是做算法出身模型训好了要往设备上部署被量化、算子不支持、内存溢出这些问题卡住——那这篇内容应该能帮到你。我会从算力平台选型讲起把 SoC、NPU、CPU、GPU 这几个概念理清楚然后讲模型侧怎么适配端侧再讲推理框架和工程落地最后给一份常见问题的排查清单。内容偏实操参数和步骤尽量给到能直接抄的程度。2. 端侧算力平台选型SoC、NPU 与异构计算2.1 SoC 是什么为什么端侧都绕不开它SoC 全称 System on Chip中文叫片上系统。你可以把它理解成把 CPU、GPU、NPU、内存控制器、ISP、DSP、各种外设接口全部集成到一颗芯片上。手机里的骁龙、天玑、麒麟开发板上的瑞芯微 RK 系列、晶晨 A 系列、地平线征程系列本质上都是 SoC。端侧 AI 之所以绕不开 SoC是因为端侧设备的形态决定了它不可能像服务器那样插一堆独立板卡。手机就那么大点地方散热和电池都有限必须把尽可能多的功能集成到一颗芯片里减少外围器件、降低功耗、缩小面积。所以端侧 AI 的算力本质上是 SoC 内部各个计算单元的协同结果。一颗典型的 AI SoC内部大概有这么几块跟推理相关的单元CPU通用计算负责调度、控制流、以及 NPU 不支持的算子兜底。ARM Cortex-A 系列是主流大小核架构常见。GPU并行计算能力强适合做浮点密集的预处理、后处理部分框架也支持 GPU 推理。NPU神经网络处理单元专门为矩阵乘加、卷积这类神经网络核心运算设计的加速器能效比远高于 CPU 和 GPU。DSP数字信号处理单元部分场景下承担音频、视觉的前处理。内存与带宽LPDDR 的容量和带宽直接决定模型能不能装下、跑得快不快。选型的时候很多人只盯着 NPU 的 TOPS 数字看这其实是个误区。TOPS 只是理论峰值实际能跑出多少取决于内存带宽、算子支持度、框架适配程度、散热降频策略。我见过标称 6 TOPS 的 NPU实际跑某个检测模型还不如另一颗标称 4 TOPS 的原因就是前者算子支持不全大量算子回退到 CPU 跑。2.2 NPU 算力怎么看TOPS、INT8 与有效算力NPU 的算力指标通常写的是 TOPS也就是每秒万亿次操作。但这里有几个坑要注意。第一精度不同算力数字不可直接比较。厂商标 TOPS 的时候通常标的是 INT8 的峰值。如果模型是 FP16 或者 FP32实际算力会打折扣。有些厂商会同时标 INT8 和 FP16 的算力比如 8 TOPS INT8 / 4 TFLOPS FP16这种相对透明一些。第二峰值算力不等于有效算力。有效算力受限于内存带宽和算子效率。举个直观的例子一个卷积层计算量是固定的但如果数据搬运的速度跟不上计算速度NPU 就会空转等数据。这就是所谓的“内存墙”。所以看 NPU 的时候除了 TOPS还要看内存带宽GB/s和 NPU 的架构代际。第三算子支持度决定实际可用性。NPU 通常只对常见算子做了硬件加速比如 Conv、MatMul、Pooling、ReLU、Add 这些。遇到不支持的算子框架会把它切回 CPU 跑这一来一回的数据搬运开销很大整体速度可能断崖式下跌。Transformer 类模型里的 LayerNorm、Softmax、GELU早期很多 NPU 支持得都不好需要做算子替换或者融合。下面这张表是我整理的主流端侧算力平台对比参数基于公开资料和实测经验具体以厂商最新文档为准平台类型代表芯片NPU 算力INT8内存带宽典型功耗适用场景旗舰手机 SoC骁龙 8 系 / 天玑 9 系30-70 TOPS50-70 GB/s3-8W手机端大模型、实时影像中端手机 SoC骁龙 7 系 / 天玑 8 系10-20 TOPS25-40 GB/s2-5W语音、轻量视觉边缘计算 SoCRK35886 TOPS约 25 GB/s5-15W工业网关、NVR、机器人边缘计算 SoC地平线征程 5128 TOPS较高15-30W车载、自动驾驶低功耗 MCU带 NPU 的 MCU0.1-1 TOPS低毫瓦级唤醒词、关键词识别注意TOPS 数字只能作为初筛参考真正选型一定要拿目标模型在目标平台上实测。厂商给的 benchmark 往往是最优条件下的结果跟你的实际模型差距可能很大。2.3 CPU、GPU、NPU 怎么分工端侧推理不是只靠 NPU 一个单元实际是异构计算。合理的分工策略是这样的前处理交给 CPU 或 GPU。图像解码、缩放、归一化、颜色空间转换这些操作 NPU 通常不擅长用 CPU 的 SIMD 指令或者 GPU 的并行能力更合适。很多框架会把前处理做成独立的 pipeline跟推理并行减少串行等待。核心推理交给 NPU。卷积、矩阵乘、激活函数这些NPU 的能效比最高。把模型的主干部分放到 NPU 上是端侧推理性能的关键。后处理交给 CPU。NMS、解码、坐标变换这些逻辑复杂但计算量不大的操作CPU 处理更灵活。不支持的算子做融合或替换。如果某个算子 NPU 不支持优先考虑能不能用支持的算子等价替换或者把它融合进相邻算子。实在不行才回退 CPU并且要评估回退带来的性能损失。我做过一个实测同一个 YOLO 系列检测模型在 RK3588 上纯 CPU 推理大概 8-10 FPSNPU 加速后能到 30 FPS 以上但如果模型里有几个不支持的算子频繁回退可能只有 15 FPS。所以算子适配这件事直接决定端侧方案能不能用。2.4 选型时容易踩的几个坑坑一只看算力不看生态。有些芯片 NPU 算力标得很高但工具链不成熟模型转换文档缺失算子支持列表不透明社区几乎没有讨论。这种平台上手成本极高出了问题只能等原厂支持。相比之下瑞芯微 RK 系列、英伟达 Jetson 系列、以及手机端的高通和联发科生态相对成熟遇到问题更容易找到资料。坑二忽略内存容量。模型权重、中间激活值、输入输出张量都要占内存。一个 100MB 的模型推理时峰值内存可能是权重的两三倍。如果设备只有 2GB 内存还要跑系统和其他应用留给模型的空间就很紧张。选型时一定要算清楚内存账。坑三低估散热影响。端侧设备散热条件差芯片跑满一段时间后会降频。标称算力是短时峰值持续推理的实际算力可能只有峰值的 60%-70%。做产品定义的时候要按持续算力来规划。坑四忽视框架兼容性。你用的训练框架PyTorch、TensorFlow导出的模型能不能顺利转到目标平台的推理格式中间要过几道转换每道转换都可能丢精度或者丢算子。选型阶段最好先拿一个代表性模型跑通全流程再决定。3. 模型侧适配Transformer 与轻量化改造3.1 Transformer 为什么在端侧这么难跑Transformer 现在是主流架构从 NLP 到视觉ViT、Swin Transformer到语音到处都是。但它天生对端侧不太友好原因有几个。计算量和参数量大。标准 Transformer 的自注意力是 O(n²) 复杂度序列长度一长计算量爆炸。ViT 把图像切成 patchpatch 数量多了注意力矩阵就很大。端侧 NPU 的算力和内存都扛不住。算子对 NPU 不友好。Transformer 里的 LayerNorm、Softmax、GELU、以及各种 reshape 和 transpose早期 NPU 支持都不好。尤其是 Softmax涉及指数运算和归一化很多 NPU 要么不支持要么效率很低。内存访问模式复杂。自注意力需要频繁的矩阵转置和拼接内存访问不连续对带宽压力大。NPU 擅长的是规整的卷积和矩阵乘这种灵活的内存操作反而吃力。动态形状问题。NLP 任务的序列长度是变化的而很多 NPU 编译器要求静态形状动态 shape 会导致反复编译或者回退 CPU。3.2 轻量化 Transformer 的几条改造路线针对上面这些问题业界有几条比较成熟的改造路线。路线一用轻量注意力替换标准注意力。比如把标准自注意力换成线性注意力、窗口注意力Swin 的思路、或者 Linformer 这类低秩近似。核心思路是降低注意力的复杂度从 O(n²) 降到 O(n) 或者 O(n log n)。Swin Transformer 用窗口划分把注意力限制在局部窗口内再通过窗口移位实现跨窗口信息交互在视觉任务上效果很好计算量也降下来了。路线二知识蒸馏。用大模型教小模型让小模型在参数量小很多的情况下逼近大模型的效果。DistilBERT、TinyBERT 都是这个思路。端侧部署时蒸馏后的小模型更容易塞进内存预算。路线三量化。把 FP32 权重和激活值量化成 INT8 甚至 INT4模型体积缩小 4 倍到 8 倍NPU 对 INT8 的支持通常也最好。量化分训练后量化PTQ和量化感知训练QATPTQ 简单但精度损失可能大QAT 精度好但需要重新训练。路线四算子融合与替换。把 LayerNorm 融合进相邻的线性层把 GELU 近似成计算更简单的函数把 Softmax 用 NPU 支持的近似实现替代。这些改动需要框架和 NPU 编译器配合。路线五结构剪枝。去掉注意力头里冗余的部分或者剪掉不重要的层。剪枝后模型变小但可能需要微调恢复精度。3.3 量化实操从 FP32 到 INT8 的关键步骤量化是端侧部署绕不开的一步我以 PyTorch 模型转 INT8 为例讲一下典型流程。第一步准备校准数据。PTQ 需要一个校准集通常从训练集或验证集里抽几百张图或者几百条文本覆盖主要的数据分布。校准集的质量直接影响量化精度不能随便拿几张图糊弄。第二步插入量化观察器。PyTorch 里用torch.quantization.observe或者 FX Graph Mode 的量化 API在模型里插入 observer统计激活值的分布。import torch from torch.quantization import get_default_qconfig, prepare, convert # 以静态量化为例 model.eval() model.qconfig get_default_qconfig(fbgemm) # 服务器端用 fbgemm端侧 ARM 用 qnnpack model_prepared prepare(model) # 用校准数据跑一遍收集统计信息 with torch.no_grad(): for data in calib_loader: model_prepared(data) # 转换成量化模型 model_quantized convert(model_prepared)第三步评估精度损失。量化后一定要在验证集上跑一遍对比 FP32 和 INT8 的精度差异。如果掉点严重考虑换 QAT或者对敏感层保留 FP32。第四步导出并转换到目标平台格式。PyTorch 量化模型导出成 ONNX再用目标平台的工具链转成 NPU 能跑的格式比如 RKNN、SNPE、TensorRT 等。实操心得量化掉点最严重的往往是第一层和最后一层以及 LayerNorm 和 Softmax 附近。如果整体掉点超过 2%优先检查这几处考虑对这些层做混合精度处理。3.4 算子替换的常见手法NPU 不支持某个算子时有几个处理思路。用支持的算子组合等价替换。比如 GELU 可以用 tanh 近似或者用 sigmoid 组合近似。LayerNorm 可以拆成均值、方差、归一化几步如果 NPU 支持 reduce 和 element-wise 操作就能拼出来。用查表法。对于激活函数这种一维映射可以预先算好一张查找表推理时直接查表插值。精度可控速度也快。调整模型结构规避。比如把某些需要动态 shape 的操作改成固定 shape把 transpose 提前到模型转换阶段处理掉。回退 CPU 并做流水线优化。实在不支持的算子回退 CPU但要把它跟 NPU 的计算做成并行流水线减少串行等待。3.5 一个视觉 Transformer 端侧部署的实例我之前做过一个基于 Swin Transformer 的缺陷检测模型往 RK3588 上部署。原始模型 FP32 大概 120MB输入 224x224在服务器上跑一张图 30ms 左右。直接转 RKNN 后发现几个问题LayerNorm 不支持Softmax 效率低部分 reshape 导致形状推断失败。处理过程是这样的先把 LayerNorm 替换成 RKNN 支持的等效实现用 reduce_mean 和 rsqrt 拼出来Softmax 用 RKNN 的 softmax 算子但把维度调整成 NPU 友好的布局reshape 的问题通过固定输入形状和调整模型结构解决。量化用 PTQ校准集用了 500 张缺陷图。最终模型 INT8 大概 30MB单张推理 25ms 左右精度掉了 1.2 个百分点在可接受范围内。这个例子里最关键的经验是不要指望模型原封不动就能转过去一定要预留算子适配和结构调整的时间。我一开始估计两天能搞定实际花了一周多大部分时间都在处理算子兼容问题。4. 推理框架与工程落地4.1 端侧推理框架怎么选推理框架是把模型跑起来的最后一环选错了框架前面所有工作都可能白费。主流的端侧推理框架有这么几类。芯片厂商自带框架瑞芯微的 RKNN、高通的 SNPE/QNN、联发科的 NeuroPilot、地平线的 Horizon Inference。这类框架跟自家 NPU 深度绑定能榨出最高性能但通用性差换芯片就要重写。通用推理框架ONNX Runtime、TensorFlow Lite、NCNN、MNN、TNN。这类框架跨平台支持多种后端CPU、GPU、NPU部署灵活但性能通常不如厂商自带框架极致。大模型推理框架llama.cpp、MLC LLM、Ollama 等。这类主要面向 LLM 端侧部署对 Transformer 结构有专门优化。选型的时候我的建议是如果产品锁定某颗芯片优先用厂商框架性能最好如果需要跨平台用 ONNX Runtime 或 MNN 这类通用框架牺牲一点性能换灵活性如果是 LLM直接用 llama.cpp 这类专门框架。4.2 模型转换的完整链路从训练好的模型到端侧跑起来中间要过好几道转换。以 PyTorch 到 RKNN 为例链路大概是这样的PyTorch 模型 → 导出 ONNX → ONNX 简化onnx-simplifier→ RKNN 工具链转换 → RKNN 模型 → 板端推理。每一步都有坑。导出 ONNX 时动态轴设置不对会导致后续转换失败ONNX 简化能去掉冗余算子但有时会改坏模型RKNN 转换时量化配置、目标平台版本、算子支持列表都要对。# ONNX 简化示例 python -m onnxsim model.onnx model_sim.onnx # RKNN 转换示例Python API from rknn.api import RKNN rknn RKNN() rknn.config(mean_values[[0, 0, 0]], std_values[[255, 255, 255]], target_platformrk3588) rknn.load_onnx(modelmodel_sim.onnx) rknn.build(do_quantizationTrue, datasetcalib.txt) rknn.export_rknn(model.rknn)注意量化数据集文件里写的是校准图片的路径每行一张。校准集要覆盖实际场景的数据分布否则量化精度会崩。4.3 内存与性能调优的几个手段模型能跑起来只是第一步跑得好还需要调优。内存复用推理过程中中间张量的内存可以复用。很多框架支持内存池把不同时刻的中间张量分配到同一块内存降低峰值内存。开启内存复用后峰值内存通常能降 20%-40%。多线程与流水线前处理、推理、后处理可以分到不同线程做成流水线。摄像头采集的同时上一帧在推理上上帧在后处理整体吞吐能提升不少。批处理如果场景允许把多帧或者多路输入拼成一个 batchNPU 的利用率更高。但批处理会增加延迟要权衡。动态频率与功耗管理端侧设备可以根据负载调整 CPU/GPU/NPU 的频率。推理时拉高频率空闲时降频省电。这个需要跟系统层配合。算子融合把连续的 ConvBNReLU 融合成一个算子减少内存访问和 kernel 启动开销。大部分框架在转换阶段会自动做但有时需要手动指定。4.4 监控与可观测性端侧设备部署后怎么知道跑得好不好需要一套监控。基础的指标包括推理延迟P50、P95、P99、吞吐FPS、NPU 利用率、内存占用、功耗、温度。这些数据可以本地记录定期上报。如果设备跑的是 Linux可以用 Prometheus Grafana 搭监控。NPU 的利用率通常通过厂商提供的接口读取比如瑞芯微有 rknn 的查询接口高通有 SNPE 的性能接口。把这些指标暴露成 Prometheus 格式Grafana 做可视化能直观看到设备运行状态。# 伪代码读取 NPU 利用率并暴露给 Prometheus from prometheus_client import Gauge, start_http_server npu_util Gauge(npu_utilization, NPU utilization percent) npu_temp Gauge(npu_temperature, NPU temperature celsius) def collect_metrics(): npu_util.set(read_npu_util()) npu_temp.set(read_npu_temp()) start_http_server(8000) while True: collect_metrics() time.sleep(1)监控的价值在于很多问题只有长期运行才会暴露比如内存泄漏、温度累积导致降频、长时间运行后精度漂移。没有监控这些问题很难定位。4.5 端侧 LLM 部署的现实考量现在端侧跑 LLM 是个热点但要说清楚现实情况。手机端跑 7B 模型INT4 量化后大概 3-4GB旗舰手机勉强能装下但推理速度大概每秒几个 token体验只能说能用。真正流畅的端侧 LLM目前参数量大多在 1B-3B 这个级别。llama.cpp 是端侧 LLM 部署用得比较多的框架支持 CPU、部分 GPU 和 NPU 后端。它的优势是量化方案成熟Q4_0、Q4_K_M 等内存管理做得好社区活跃。但要注意llama.cpp 对 NPU 的支持还在演进中很多平台的 NPU 后端并不完善实际可能还是跑在 CPU 上。如果要在端侧跑 LLM我的建议是先明确场景是不是真的需要 LLM能不能用更小的专用模型替代如果一定要 LLM参数量控制在 3B 以内量化到 INT4并且做好用户预期管理别指望跟云端大模型一样的体验。5. 常见问题与排查技巧实录5.1 模型转换失败类问题问题ONNX 导出后算子不支持。常见于自定义算子或者较新的算子。解决思路是查目标框架的算子支持列表不支持的算子做替换或者用自定义算子实现。ONNX 的 opset 版本也要注意版本太高可能目标框架不支持适当降低 opset 版本。问题形状推断失败。多半是动态 shape 导致的。解决办法是固定输入形状或者在转换工具里显式指定 shape。有些框架支持动态 shape但性能会打折。问题量化后精度暴跌。先检查校准集是否覆盖实际分布再看是不是某些层对量化敏感。可以尝试混合量化敏感层保留 FP16其他层 INT8。5.2 运行时性能问题问题NPU 利用率低。可能是算子回退 CPU 导致的。用框架提供的 profiling 工具看每个算子的执行时间和执行单元找出回退的算子。也可能是内存带宽瓶颈检查数据布局是否连续。问题推理延迟波动大。检查是否有其他进程抢占资源检查散热是否导致降频检查是否有动态内存分配导致的抖动。固定内存池、绑核、锁频都能改善。问题长时间运行后变慢。典型的内存泄漏或者温度累积。用监控工具看内存和温度曲线定位是哪个环节泄漏。5.3 常见问题速查表现象可能原因排查方向解决手段转换报算子不支持算子不在支持列表查框架算子文档替换算子或自定义实现量化后精度掉太多校准集不具代表性检查校准数据分布扩充校准集混合量化NPU 利用率低算子回退 CPUprofiling 看算子执行单元算子融合或替换推理延迟高内存带宽瓶颈看数据布局和带宽占用优化数据布局减少搬运运行一段时间变慢温度降频或内存泄漏监控温度和内存曲线改善散热修复泄漏动态 shape 报错编译器要求静态 shape检查输入 shape 设置固定 shape 或填充多线程下结果异常线程安全问题检查共享状态加锁或隔离上下文5.4 几条踩坑总结第一条选型阶段一定要跑通全流程。不要只看参数拿一个代表性模型从训练框架导出一路转到目标平台跑通推理测出实际性能。这一步能筛掉大部分不合适的平台。第二条量化不是万能的。有些模型对量化就是敏感硬量化会掉点严重。这时候要么换模型结构要么接受混合精度带来的性能损失要么重新训练。第三条留足算子适配时间。尤其是 Transformer 类模型算子适配的工作量经常被低估。项目排期时模型转换和调优至少要留出总时间的 30%。第四条监控要提前做。不要等出了问题才想起来加监控。部署第一天就把延迟、内存、温度、NPU 利用率这些指标采集起来后面排查问题会轻松很多。第五条别忽视散热。端侧设备的散热设计直接决定持续性能。做产品定义时按持续算力而不是峰值算力来规划否则用户体验会很差。端侧 AI 算力方案这件事说到底是在约束条件下做取舍。没有哪颗芯片、哪个框架是万能的关键是搞清楚自己的场景需要什么然后在这个约束下找到最合适的组合。我个人的经验是先把场景和指标定清楚再选平台再调模型最后做工程优化顺序不能乱。反过来先选平台再想场景大概率会走弯路。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →