端侧 AI 这件事真正上手做过一个完整项目的人都知道最难的从来不是把模型跑起来。把一个大模型量化一下、塞进手机或者开发板里跑通推理这件事在 2024 年之后已经变得相当平民化了。难的是模型选完之后发现精度掉得没法看换了一个又发现内存直接爆掉好不容易调到一个能用的配置上线之后发现不同设备上表现天差地别再往后用户用了一段时间数据分布悄悄漂移了模型效果慢慢衰减而你根本没有一套机制能及时发现这件事。我在过去一年多的时间里前前后后参与了四个端侧 AI 的落地项目覆盖手机端图像处理、嵌入式语音唤醒、车载场景的目标检测以及一个工业质检的边缘盒子方案。这四个项目踩的坑各不相同但回过头来看它们暴露出来的问题高度一致大家把太多精力花在选模型这一步却对选完之后怎么办缺乏系统性的设计。模型选型、部署验证、线上监控、迭代回流这四个环节本应该是一个闭环但大多数团队做成了四条断开的线段。这篇内容我想把端侧 AI 系统工程这件事从头到尾捋一遍重点不在于教你某个具体工具怎么用而在于把每个环节背后的决策逻辑讲清楚。你如果是刚接触端侧部署的工程师可以把它当作一份路线图如果你已经做过一两个项目但总觉得哪里不对劲也许能在这里找到那个不对劲的根源。1. 端侧 AI 和云端 AI 的本质差异到底在哪很多人做端侧项目时习惯性地把云端那套流程搬过来结果处处碰壁。根本原因是没有真正理解端侧和云端的差异不是算力小一点这么简单而是整个约束条件体系都变了。1.1 算力、内存、功耗构成的三重枷锁云端推理你关心的是什么吞吐量、延迟、成本。GPU 不够就加卡内存不够就加机器这些都可以用钱解决。端侧完全不是这个逻辑。端侧设备的算力是固定的内存是固定的功耗预算更是卡得死死的。这三个约束互相牵制你优化了其中一个另外两个往往会恶化。举个具体的例子。在一个车载目标检测项目里我们最初选了一个参数量 8M 左右的检测模型在服务器上跑 mAP 能到 42看起来不错。但部署到车机芯片上之后发现单帧推理延迟到了 180ms而且芯片温度在连续跑十分钟后直接触发降频延迟进一步恶化到 300ms 以上。这就是典型的功耗约束反过来影响算力表现的情况。后来我们换了一个 3M 参数的轻量模型mAP 掉到 36但延迟稳定在 45ms温度也控制得住。对于车载场景来说稳定比峰值性能重要得多。这里有一个经验性的判断标准端侧模型的参数量一般控制在设备可用内存的 1/5 到 1/3 之间比较安全。比如设备有 2GB 可用内存模型参数量最好在 100M 到 200M 以内这里说的是 FP16 存储的情况。留出足够的余量给运行时、输入输出缓冲、以及系统本身的开销。我见过太多项目把内存算得刚刚好结果一跑起来就 OOM。1.2 数据不出端的隐私红利与代价端侧 AI 最大的卖点之一就是数据不出端这对隐私敏感的场景比如人脸、医疗、个人语音是刚需。但这件事是有代价的你拿不到原始数据就没法做集中式的数据分析没法快速发现模型在哪些样本上表现不好。云端模型出了问题你可以把 bad case 捞出来看一眼马上就知道问题在哪。端侧模型出了问题你只能拿到一些脱敏的统计指标甚至什么反馈都没有。这就要求你在设计阶段就要把如何获取有效反馈这件事想清楚。后面讲监控迭代的时候我会详细展开这里先埋一个伏笔端侧项目的监控设计必须在模型选型之前就开始考虑而不是上线之后才补。1.3 硬件碎片化带来的长尾噩梦云端你基本只需要适配几种 GPU 型号端侧你要面对的是几十种芯片、十几种操作系统版本、各种奇奇怪怪的驱动。同一个模型在高通芯片上跑得好好的换到联发科上可能精度就变了因为不同 NPU 对量化的支持程度不一样。我印象最深的一次一个模型在开发板上验证精度完全达标量产设备上却出现了明显的精度下降。排查了整整一周最后发现是量产设备用的 NPU 驱动版本对某个算子的实现有差异导致量化后的数值范围出了偏差。这种问题在云端几乎不可能遇到但在端侧是家常便饭。所以端侧项目的测试矩阵一定要覆盖目标硬件的所有主要型号不能只在一台设备上验证通过就认为万事大吉。2. 模型选型不是选最好的是选最合适的模型选型是端侧 AI 项目里最容易走偏的一步。走偏的方式通常有两种一种是追求 SOTA选了一个榜单上分数最高但根本跑不动的模型另一种是过度保守选了一个跑得飞快但效果差到用户能感知的模型。正确的做法是在效果和约束之间找到一个平衡点而这个平衡点需要通过系统性的评估来确定。2.1 先明确任务的精度底线再倒推模型规格我的习惯是在选模型之前先和产品侧确认一个精度底线。什么叫精度底线就是这个任务在业务上能接受的最低效果。比如语音唤醒唤醒率不能低于 95%误唤醒率不能高于每 24 小时 1 次。比如图像分类Top-1 准确率不能低于 90%。这个底线定下来之后选型就有了明确的目标。然后根据精度底线倒推需要的模型容量。这里有一个粗略的经验公式对于分类任务参数量每增加一倍精度大约提升 1 到 3 个百分点但边际收益递减很快。对于检测和分割任务情况更复杂因为还涉及到输入分辨率的影响。实际操作中我会准备一个候选模型池通常包含 5 到 8 个不同规格的模型从超轻量到中等规模都有。然后在目标设备上逐一测试记录精度、延迟、内存占用、功耗四个维度的数据最后画一张散点图找到那个精度刚好达标、资源占用最低的模型。2.2 量化友好度比原始精度更值得关注这一点是很多新手容易忽略的。一个模型在 FP32 下的精度很高不代表它量化到 INT8 之后还能保持。有些模型结构对量化特别敏感比如含有大量深度可分离卷积的模型量化后精度可能掉十几个点。而有些模型天生就适合量化掉一两个点就能稳住。怎么判断一个模型的量化友好度最直接的办法就是实际量化一遍在验证集上跑一下看精度损失。但在这之前你可以先看几个信号模型是否使用了大量 Group Convolution这类操作对量化不友好激活函数的分布是否集中分布越集中越容易量化是否有 BatchNorm 层BN 层在量化时通常可以融合掉反而有利于量化我在一个图像分割项目里最初选了一个基于 MobileNet 的轻量分割模型FP32 下 mIoU 是 72量化到 INT8 之后掉到了 63直接不达标。后来换了一个专门为量化设计的网络结构FP32 下 mIoU 是 70量化后是 68反而更符合要求。所以选型阶段一定要把量化后的精度作为核心指标而不是只看 FP32 的分数。2.3 算子兼容性检查别等到部署时才发现跑不了选好模型之后在正式部署之前一定要做算子兼容性检查。具体来说就是把你模型里用到的所有算子列出来逐一确认目标推理框架和设备 NPU 是否支持。不支持怎么办要么换等价的算子实现要么把不支持的部分回退到 CPU 执行但这样会拖慢整体速度。我一般会用一个简单的脚本把模型的算子统计出来然后和推理框架的支持列表做比对。这个步骤花不了多少时间但能避免后期大量的返工。有一次我们选了一个用了自定义算子的模型部署时才发现设备根本不支持最后不得不重新训练了一个用标准算子搭建的版本白白浪费了两周。检查项目的常见问题算子支持列表确认所有算子都能在目标设备上执行自定义算子、特殊激活函数不支持输入尺寸约束确认模型输入尺寸符合设备要求某些 NPU 要求输入尺寸是 16 的倍数量化支持确认量化方案与设备兼容设备只支持对称量化模型用了非对称量化动态 shape确认是否支持动态输入多数端侧 NPU 只支持固定 shape3. 部署验证从实验室到真机的最后一公里模型选好了在开发板上也跑通了很多人就觉得大功告成了。但从开发板到量产设备中间还有很长一段路要走。这一段路走不好前面所有的努力都可能白费。3.1 开发板验证通过不等于量产设备通过开发板通常是理想环境散热好、电源稳、系统干净、没有其他进程抢占资源。量产设备则是恶劣环境空间狭小散热差、电池供电电压波动、后台一堆系统进程在跑。同一个模型在这两种环境下的表现可能完全不同。我在一个手机端项目里遇到过这样的情况开发板上推理延迟稳定在 30ms到了真机上变成了 80ms 到 200ms 之间剧烈波动。排查后发现是两个原因叠加一是真机上后台有其他 AI 任务在抢占 NPU 资源二是手机在发热降频后 NPU 频率被压低。这两个问题在开发板上都不存在。解决办法是在真机上做资源隔离给我们的推理任务分配独立的 NPU 优先级同时在模型层面做降级策略当检测到设备温度过高时自动切换到更轻量的模型或者降低推理频率。这种降级策略在端侧项目里几乎是必备的因为端侧设备的运行环境太不可控了。3.2 精度验证要在真实数据分布上做开发阶段我们通常用公开数据集或者自己构造的验证集来评估模型。但这些数据的分布和真实场景的数据分布往往有差距。如果不做真实数据验证上线后精度可能大打折扣。我的做法是在部署验证阶段尽可能收集一批真实场景的数据可以是内部测试人员采集的也可以是脱敏后的用户数据在这个真实数据集上重新评估模型精度。如果发现精度明显低于验证集就要分析原因是光照条件不同是设备摄像头差异还是目标类别分布不同有一次做工业质检验证集上准确率 98%真实产线上只有 85%。原因是验证集里的缺陷样本都是实验室环境下拍摄的光照均匀、背景干净而真实产线上有油污、有反光、有震动模糊。后来我们重新采集了一批真实产线数据做微调才把精度拉回来。3.3 端侧推理的耗时拆解与优化优先级很多人看推理耗时只看一个总数但真正要优化的时候必须把耗时拆开看。一个典型的端侧推理流程包含这几个部分数据预处理、模型推理、后处理。这三部分的耗时占比在不同任务里差别很大。以目标检测为例预处理resize、归一化可能占 10%模型推理占 60%后处理NMS占 30%。如果你只优化模型推理把推理时间砍了一半总耗时也只降低了 30%。但如果你优化后处理比如用更高效的 NMS 实现可能一下子就把总耗时降了 20%。所以优化之前一定要先做 profiling搞清楚时间花在哪里。我常用的工具包括推理框架自带的 profiler以及设备厂商提供的性能分析工具。优化的优先级永远是先优化占比最大的部分而不是先优化最容易优化的部分。4. 监控体系端侧模型上线后的眼睛端侧模型上线之后最大的挑战是你看不见它。云端模型你可以实时看日志、看指标端侧模型跑在用户设备上你拿不到原始数据甚至不知道它有没有在正常工作。所以监控体系的设计是端侧 AI 系统工程里技术含量最高的部分之一。4.1 端侧能上报什么不能上报什么首先要明确一个边界端侧能上报什么数据不能上报什么数据。这个边界由隐私要求和带宽限制共同决定。能上报的通常是推理耗时、内存占用、设备温度、模型版本、调用次数、以及一些脱敏后的统计指标比如分类结果的分布、置信度的分布。不能上报的是原始输入数据、原始输出数据、任何能关联到具体用户的信息。但这里有一个技巧你可以上报模型输出的统计特征而不是原始输出。比如对于一个分类模型你可以上报每个类别的预测次数分布而不是每次预测的具体结果。这样既能监控模型的行为是否正常又不涉及隐私。我在一个语音项目里上报的是唤醒词置信度的直方图而不是具体的语音数据。通过观察这个直方图的分布变化就能判断模型是否在正常工作。如果直方图整体左移说明唤醒变得越来越困难可能是环境噪声变大了也可能是模型老化了。4.2 用影子模式做无标注的精度监控端侧最头疼的问题是没有标注怎么知道模型精度有没有下降影子模式是一个很实用的方案。具体做法是在设备上同时跑两个模型一个是当前线上模型一个是候选的新模型两个模型的输出都上报只上报统计特征。通过对比两个模型的输出差异可以间接判断线上模型是否出现了问题。另一种方案是置信度监控。模型对每个样本都会输出一个置信度如果一段时间内低置信度样本的比例明显上升说明模型遇到了它不擅长的数据精度很可能在下降。这个方法不需要标注实现成本也低是我最常用的手段之一。还有一种更主动的做法在端侧做轻量的数据筛选把那些模型不确定的样本置信度处于中间区间的样本挑出来在用户授权的前提下回传到云端做标注。这样既能控制回传数据量又能拿到最有价值的那部分数据用于迭代。4.3 监控指标的阈值设定与告警策略监控指标设好了接下来要定阈值。阈值定得太松出了问题发现不了定得太紧天天告警没人看。我的经验是分两级警告阈值和严重阈值。警告阈值触发时只是记录不告警用于趋势分析。严重阈值触发时才发告警。严重阈值一般设在业务不可接受的水平上。比如推理延迟警告阈值设在 80ms严重阈值设在 150ms。延迟超过 150ms 时用户已经能明显感知到卡顿了这时候必须告警。另外告警一定要做聚合和去重。端侧设备数量可能成千上万如果每台设备都发一条告警你的告警系统会被淹没。正确的做法是按设备型号、系统版本、地域等维度做聚合只在某个维度的异常比例超过阈值时才告警。监控维度具体指标警告阈值严重阈值性能推理延迟 P9580ms150ms性能内存占用峰值可用内存 60%可用内存 80%稳定性推理失败率0.5%2%效果低置信度样本比例15%30%资源设备温度45 度55 度5. 迭代闭环让模型越用越好而不是越用越差监控的目的是为了迭代。如果监控发现问题却不迭代那监控就白做了。端侧 AI 的迭代闭环比云端更难建立因为数据回流链路长、标注成本高、发版周期慢。但这件事必须做否则模型上线即巅峰之后只会越来越差。5.1 数据回流的三种策略与取舍数据回流是迭代闭环的起点。端侧的数据回流有三种常见策略各有取舍。第一种是全量回流所有数据都传回云端。这种方式数据最全但带宽成本高隐私风险大基本只适用于内部测试设备。第二种是采样回流按一定比例随机采样回传。成本可控但可能漏掉关键的长尾样本。第三种是定向回流只回传那些模型不确定或模型出错的样本。这种方式数据价值最高但需要端侧有筛选能力实现复杂度也最高。我的建议是项目初期用采样回流快速积累一批真实数据项目稳定后用定向回流把有限的带宽和标注预算花在最有价值的样本上。全量回流只在特殊情况下使用比如新模型上线前的灰度验证阶段。5.2 增量训练与全量重训的选择依据拿到新数据之后是做增量训练还是全量重训这个问题没有标准答案取决于几个因素。如果新数据的分布和旧数据接近只是补充了一些样本增量训练就够了成本低、见效快。但如果新数据的分布和旧数据差异很大比如场景变了、设备换了那必须全量重训否则模型会偏向新数据而遗忘旧数据。还有一个判断依据是数据量。如果新数据量不到旧数据量的 10%增量训练的风险比较大容易过拟合到新数据上。如果新数据量超过旧数据量的 30%全量重训通常更稳妥。我在一个项目里吃过增量训练的亏新数据只占 5%增量训练后模型在新数据上表现很好但在旧数据上精度掉了 8 个点。后来改成新旧数据混合全量重训才把两边都兼顾到。5.3 灰度发布与回滚机制的设计端侧模型的发布不像云端那样可以随时回滚因为模型是打包在 App 或者固件里的。所以灰度发布和回滚机制必须提前设计好。灰度发布的核心是分批放量。第一批只推给 1% 的用户观察一周没问题再推到 10%再观察一周然后 50%、100%。每一批都要看监控指标特别是精度相关的指标。回滚机制的关键是模型可切换。也就是说设备上要同时保留新旧两个模型通过配置开关来决定用哪个。一旦新模型出问题远程下发一个配置就能切回旧模型不需要重新发版。这个设计在端侧项目里非常重要因为端侧发版的成本太高了不能每次都靠发版来解决问题。6. 几个容易翻车的细节和我的应对经验前面讲的都是框架性的东西最后这部分我想聊几个具体的、容易翻车的细节。这些都是我在实际项目里踩过的坑希望能帮你少走弯路。6.1 模型文件大小对下载和存储的影响端侧模型是要打包进 App 或者固件里的模型文件大小直接影响下载量和存储占用。一个 50MB 的模型和一个 200MB 的模型对用户的影响完全不同。特别是在一些网络条件不好的地区模型太大可能导致下载失败率上升。我的经验是模型文件大小尽量控制在 30MB 以内超过这个数就要考虑做更激进的量化或者模型裁剪。如果实在压不下来可以考虑按需下载App 只带一个基础模型高级模型在用户首次使用时下载。但这样会增加首次使用的等待时间需要权衡。另外模型文件在打包时要注意压缩。很多推理框架的模型文件本身已经压缩过了再压缩效果不大。但有些框架的模型文件是未压缩的用 zip 压缩后能小 30% 到 50%。这个细节看起来小但对下载量影响很大。6.2 多线程与并发推理的资源竞争端侧设备上你的推理任务不是唯一在跑的。相机、音频、UI 渲染都在抢资源。如果你的推理任务开了多线程很可能和其他任务产生资源竞争导致整体体验下降。我的做法是推理任务尽量用单线程把并发控制交给上层调度。如果确实需要多线程比如同时处理多路输入一定要做线程池管理限制最大并发数。另外推理任务的优先级要设得合理不能太高会抢占 UI 资源也不能太低会被其他任务饿死。还有一个细节推理任务结束后要及时释放资源。我见过一个项目每次推理都创建一个新的推理引擎实例用完不释放结果跑了几十次之后内存就爆了。正确的做法是复用推理引擎实例只在必要时重建。6.3 设备休眠唤醒后的模型状态恢复端侧设备经常休眠休眠再唤醒后模型的状态可能出问题。比如推理引擎的上下文丢失了、内存被回收了、NPU 的句柄失效了。如果不处理这些情况唤醒后第一次推理很可能失败。我的做法是在设备唤醒后先做一次健康检查跑一个简单的推理确认引擎状态正常。如果不正常就重建引擎。这个检查的耗时很短但能避免很多莫名其妙的失败。另外模型的一些内部状态比如 RNN 的隐藏状态、跟踪算法的轨迹在休眠后可能需要重置。这些状态如果不清空唤醒后的第一次推理结果可能是错的。这个坑我在一个跟踪项目里踩过休眠唤醒后目标框直接飘到了画面外排查了很久才发现是状态没重置。6.4 版本管理与模型-代码的兼容性端侧项目里模型和代码是强耦合的。模型换了预处理和后处理的代码可能也要跟着改。如果版本管理没做好很容易出现模型和代码不匹配的问题。我的做法是给每个模型分配一个唯一的版本号这个版本号要同时记录在模型文件和代码里。推理时先检查版本号是否匹配不匹配就报错。这样能避免用错模型的情况。另外模型的输入输出格式也要做版本管理。比如输入从 RGB 改成 BGR输出从 4 个值改成 5 个值这些变化都要在版本号里体现。我一般用语义化的版本号主版本号变化表示输入输出格式变了次版本号变化表示模型结构变了修订号变化表示只是重新训练了。端侧 AI 系统工程这件事说到底是一个约束下的最优化问题。你永远在精度、速度、内存、功耗、隐私这几个维度之间做权衡没有完美的方案只有最适合当前场景的方案。而闭环设计的价值在于它让你在每一次迭代中都能基于真实反馈做调整而不是凭感觉拍脑袋。我做了这么多项目最大的体会就是前期多花时间在监控和迭代机制的设计上后期能省下十倍的时间。那些上线后才发现问题、然后手忙脚乱救火的项目几乎都是因为闭环没建好。