最近大模型上车这件事,讨论热度一直很高。某新势力车企内部做了一次很务实的工程实践:通过硬件协同设计算出了一个真正跑在车规级边缘设备上的最佳边缘模型,而不是把云端大模型直接砍一刀后硬塞进车机。大模型上车最难的其实不是模型本身,而是算力、带宽、功耗这几条硬约束,算法、芯片、系统三个角色必须一起做决策。这篇文章把整个思路、评价方法、实操流水线和常见的坑理一遍。适合正在做车载AI、边缘部署,或者想搞懂端侧大模型落地门槛的工程师。
1. 为什么大模型上车必须谈“硬件协同设计”,而不是单谈模型调优
1.1 车端的资源账单:算力、功耗、带宽和成本
先看一组现实数据。车规域控里给AI芯片的持续功耗通常在20W到50W之间,而且大多数方案是被动散热,没有数据中心那种暴力风扇;芯片一旦热起来,频率往下掉,算力也跟着缩水。这跟云端动辄几百瓦的GPU完全是两个物种。
算力方面,现在车规SoC动不动宣传“几十TOPS、上百TOPS”,但那是峰值,是短时间能冲到的高度。真正持续运行时会受功耗墙、温度墙限制,通常要打个六到八折。带宽更紧张,一颗车规级SoC的内存带宽很少超过100GB/s,而云端的HBM带宽动辄几TB/s。这个差距让很多在云端跑得飞起的模型上车后直接卡死。
还有一个常被忽略的问题:显存和内存容量。一个7B参数的模型,FP16权重就14GB,普通车规域控总共才8GB到16GB内存,模型都塞不进去。就算把模型压到1B参数、INT8量化,权重也有1GB,仍然要在一次推理里被完整读一遍。边缘部署不是“能不能跑”的问题,而是“在功耗、带宽、时延都有限的情况下,还能不能稳定跑”的问题。
我见过太多项目,模型在云端精度漂亮,上车后延迟直接超预期三倍,原因就是只看算力不看访存。真正上车之前,必须把车端的资源账单拉出来:功耗预算多少、内存预算多少、带宽预算多少、P99延迟要求多少。这些数字不是装饰,是后面所有技术决策的边界条件。
1.2 算法与芯片到底在协同什么
传统的做法是:算法团队先训一个模型,训完了再丢给部署团队做适配。适配团队一看,发现模型里有个算子NPU不支持,就换成CPU回退;CPU太慢,就改模型结构;改完结构精度掉了,再回头调模型。这个循环非常痛苦,经常一个模型来回拉锯两三个月。
硬件协同设计解决的就是这个错位。它的核心思想是:在模型结构设计阶段就把硬件行为放进约束条件里。比如芯片提供多少 MAC 运算能力、内存带宽多少、哪些算子有硬件加速、哪些只能CPU走、缓存大小和内存对齐规则是什么。算法团队选骨干网络、定层数、选激活函数时,就要对着这份“硬件成本清单”打分。
我用一个生活类比解释:装修房子如果先按图纸买好沙发和柜子,再发现插座位置不对、承重墙不能拆,只能返工。协同设计相当于水电工、木工、设计师在开工前坐在一起定好点位和尺寸,后面才不用砸墙。
具体到模型上,协同设计至少包含三个层面:一是模型结构选择,比如把密集的Transformer模块换成更规整的卷积混合结构;二是算子层适配,比如把复杂动态范围的归一化算子替换成硬件友好的近似实现;三是部署优化,比如做模型分块、算子融合、内存复用。这三件事必须在同一个迭代闭环里同时发生,才能找到真正的“最佳边缘模型”。
2. 算出“最佳边缘模型”之前,先定义评价指标
2.1 评价边缘模型的四类关键指标
很多人一说边缘模型就只看精度,这是最容易翻车的。精度只是其中一个维度,边缘场景必须同时看四类指标。
精度类指标还好理解:分类任务看Top-1/ mAP,语音意图理解看准确率和F1,多模态模型要看最终业务指标。但光有精度没用,还要看性能类指标:单帧延迟、P99延迟、吞吐,而且必须是在目标硬件上的真实测量值。第三类是资源类指标:权重文件大小、峰值内存、NPU占用率、功耗增量,这些决定模型能不能和其他任务共存。第四类是稳定性指标:温度升高后延迟是否抖动、极端光照输入下精度是否断崖式下跌、多模型并发时是否稳定。
| 指标类别 | 典型指标 | 为什么重要 | 怎么测 |
|---|---|---|---|
| 精度 | 准确率 / mAP / F1 | 决定业务可用性 | 离线数据集 + 车端回灌 |
| 性能 | P99延迟 / 帧率 | 决定用户体验和行车安全 | 真机或硬件仿真器实测 |
| 资源 | 内存占用 / 功耗 / NPU占用 | 决定能否与其它任务共存 | 功耗仪 + 内存监控 |
| 稳定性 | 温度降频 / 多路争抢抖动 | 决定量产可用性 | 长时间老化 + 台架压力测试 |
这四个维度不能分开看。一个模型可能精度最高,但内存占用超过预算;另一个模型精度略低,但P99延迟非常稳。所谓“最佳”,一定是四项约束的交集,不是排行榜上的最高分。
2.2 模型压缩三板斧:量化、剪枝、蒸馏的硬件收益
要把一个大模型塞进车规级边缘设备,模型压缩是绕不开的。最常用的三个手段是量化、剪枝、蒸馏,但它们对硬件的收益差异很大。
量化是目前收益最直接的手段。把FP32模型变成INT8,权重和激活的访存量直接减少四倍,计算单元也能用上定点的MAC阵列,整体性能提升非常明显。INT4甚至更低比特能进一步压缩,但精度风险高,而且在车规安全场景下我一般不建议全面铺开。量化有两种路线:PTQ(训练后量化)和QAT(量化感知训练)。PTQ快,但对敏感层很不友好;QAT精度稳,但需要训练资源和更多时间。两者不是单选,我后面会讲一种混合做法。
剪枝要分清结构化还是非结构化。非结构化剪枝就是把不重要的权重置零,参数稀疏了,但很多NPU加载权重时仍然按稠密矩阵读,内存带宽一点没省,硬件收益很低。结构化剪枝则是直接剪掉整个通道、头或者层,让矩阵形状变小,访存和算力都降,配合硬件对齐效果好得多。所以选剪枝方案前,先问芯片的稀疏加速支持到什么程度。
蒸馏则是用一个大的教师模型去教一个小学生模型,小模型结构可以重新设计,走硬件友好的路线,精度又比从头训练好很多。它通常是前三板斧里最后的兜底手段。实际项目里,我先量化,再结构化剪枝,最后用蒸馏补精度,三者也按这个顺序决定改动幅度。
2.3 一道算术题:用Roofline模型判断瓶颈
在选模型之前,我强烈建议先做一次Roofline模型分析。这个概念听起来高深,其实特别简单:一块芯片能达到的实际性能,等于“算力峰值”和“带宽×算术强度”两者中较小的那个。
算术强度就是一次推理中“计算量/访存量”,单位是MACs/Bytes。比如一个1.2B参数的Transformer模型,每处理一个token大约要执行1.2B MACs。这个模型INT8量化后,权重文件大约1.2GB,推理时至少要把这些权重从DDR里读一遍。那么它的算术强度就是:
算术强度 = 1.2B MACs / 1.2GB ≈ 1 MAC/Byte
这是一个非常“内存密集”的数字。假设芯片的NPU峰值是50G MAC/s,内存带宽是60GB/s。只按算力算,单token延迟下限是 1.2B / 50G ≈ 24ms;只按带宽算,单token延迟下限是 1.2GB / 60GB/s ≈ 20ms。还没算激活读写、中间结果搬运和缓存命中率,真实延迟已经逼近30ms以上。如果换成INT4量化,权重降到约0.6GB,带宽时间变成10ms,瓶颈一下子就移到了算力边界。
| 方案 | 权重大小 | 每token计算量 | 算力时间下限 | 访存时间下限 | 主要瓶颈 |
|---|---|---|---|---|---|
| FP16 | 2.4GB | 1.2B MACs | 24ms | 40ms | 带宽 |
| INT8 | 1.2GB | 1.2B MACs | 24ms | 20ms | 算力接近带宽 |
| INT4 | 0.6GB | 1.2B MACs | 24ms | 10ms | 算力 |
所以,光看“芯片有几TOPS”没有意义。很多车规模型跑不快,不是算力不够,而是权重访存把DDR带宽吃满了。这也是硬件协同设计存在的根本原因:算法选型时必须知道自己到底算力密集还是访存密集,才能针对性地压缩访存量,而不是盲目增大模型。
3. 实操:从候选模型池到车规级部署的完整闭环
3.1 定义场景需求与硬件基线
实操的第一步不是选模型,而是把需求写清楚。某新势力车企这个项目做的场景是一位座舱多模态语义理解:输入的是摄像头图像和语音文本,输出的是用户意图和关键属性。业务侧给出的硬指标是:端侧单帧P99延迟小于80ms,整机功耗增量不超过5W,内存占用小于1.5GB,INT8量化后的准确率相对云端模型掉点小于1%。
拿到需求后,我们先把目标硬件的能力基线梳理出来。芯片是一颗车规级SoC,内置NPU,INT8定点算力大概有80G MAC/s,但持续运行只能按六成算;DDR带宽标称60GB/s,L2缓存只有几MB;算子层面支持Conv、MatMul、Elementwise等常见算子,LayerNorm、Softmax这类归一化算子有基础实现,但动态shape支持非常差。这些特征直接决定候选模型的结构不能太“花哨”。
建议你也这样做:把硬件基线整理成一张表,贴在项目墙上。每次模型结构改动,都对照这张表问一句:新增的算子硬件支持吗?访存会不会爆?功耗预算够不够?
3.2 构造候选模型池并快速筛除
不要只拿一个大模型调研,要做候选池。我们当时挑了三种典型结构:一个是0.8B的纯Transformer编码器,适合处理文本;一个是1B的CNN+Transformer混合结构,适合图像和文本联合输入;还有一个是0.5B但带额外跨模态注意力模块的结构。
这一步不能用“看起来精度差不多”来选,要直接跑硬件编译器提供的profiler,统计每个候选模型在NPU上的算子级延迟、内存占用、带宽消耗,以及是否有算子落到CPU回退。跑完之后,那个0.8B纯Transformer模型因为动态序列长度导致NPU需要反复重新分配缓存,P99延迟高得离谱,直接被淘汰。
筛完后剩下两个候选进入量化环节。我建议候选池不要超过五个,否则后面每一步的迭代成本都会倍增。快速筛除的核心标准只有一个:在硬件上跑过的真实profile,而不是纸面FLOPs。
3.3 校准数据采集与量化
候选模型确定后,量化是最关键的一环。我们的做法是先做PTQ,并保留混合精度的可能性。
第一步是准备校准集。可以从车端真实采一些数据,覆盖白天、夜晚、雨天、地下车库等场景,数量不一定多,500到2000条足够,但一定要覆盖模型部署后的数据分布。直接用云端训练集做校准是常见错误——云端数据和车端摄像头拍到的数据分布完全不一样,校准出来的量化参数根本不靠谱。
第二步是导出计算图,插入量化节点,用校准集跑PTQ。跑完以后不能只看最终精度,要逐层比较量化前后的feature map余弦相似度,定位掉点严重的层。比如我们遇到某个归一化算子量化后相似度只有0.9,单独给它保留FP16,整体精度就回来了。这种“敏感层少量高精度、非敏感层INT8”的混合精度方案,在车规场景里非常实用。
如果PTQ后整体精度仍然掉点超过1%,再考虑QAT。QAT需要在训练框架里模拟量化误差,微调几个epoch,不是从头训练,成本可控。实际经验是:85%的模型用PTQ加敏感层混合精度就能过关,真正需要QAT的只有少数结构特殊的模型。
3.4 算子映射与结构微调
量化完了,模型要真正落到NPU上,这时算子映射问题会浮出水面。很多NPU对Transformer的支持并不完美,尤其是Softmax和LayerNorm这类带归约的算子,在硬件上实现要跨行读写,访存很慢。
我们的做法是:尽量把模型结构改造成硬件友好的形态。比如用近似归一化替代标准LayerNorm、把GELU换成SiLU或ReLU、限制Attention的序列长度、把动态shape固定成静态长度。还有一个很实用的手段是算子融合,把“Conv+BN+ReLU”或者“线性层+激活”融合成单个NPU指令,减少中间结果写回DDR。
这一步其实是硬件协同设计的核心:模型结构不是不能改,而是要在精度和硬件效率之间找到最优折点。我们当时为了适配NPU,把原始的12层Transformer精简成8层,加了一个轻量视觉塔,并在最后一层做通道剪枝,最终业务精度只掉了0.3%,但端侧延迟直接降了40%。
这种结构调整不能靠感觉,要每次改完都在硬件上跑一遍。我们内部定了一个规矩:任何模型结构的修改,必须附一份NPU延迟和内存变化报告,否则不进入下一轮。
3.5 编译、部署与验收
模型结构固定后,进入编译部署阶段。流程通常是这样的:先把模型导出成通用的中间表示,再由硬件编译器做算子映射、内存分配和调度,最终生成目标芯片的二进制文件。这里我放一个通用步骤示例,具体指令名称各家工具链不同,但思路一致:
edge_compile --model model.onnx \ --quant int8 \ --calib_path ./calib_data/ \ --output ./deploy_model.bin \ --target soc_npu_v2 \ --memory_budget 1.5G编译完成后,先不要上车,一定要在台架或者域控开发板上验证。需要测的数据包括:P99延迟、内存峰值、整板功耗、连续运行24小时后的温度曲线。同时还要做多任务并发测试,因为车端不可能只跑一个大模型,DMS、OMS、语音唤醒都在抢同一块芯片和同一份带宽。
4. 常见问题与排查技巧实录
4.1 上板精度“崩了”:先查前后处理一致性
这是所有人都绕不过去的坑。我们在PTQ校准阶段精度已经接近99%,但上板之后识别错误率突然增高。一开始怀疑是量化问题,后来逐层对比才发现,根因是前处理完全不一致。PC端用了双线性插值Resize和RGB输入,车端ISP默认输出BGR,而且YUV转RGB的系数也不一样。
排查方法很简单:固定一张图,分别经过PC端的预处理管线和车端管线,把输入张量导出来比对;再逐层dump特征图,计算余弦相似度。第一个出现显著差异的层,往往就是问题源头。这类问题大部分时候与模型无关,却会浪费你两三天时间,先排查它永远是最高优先级。
4.2 算子被“静默替换”:看懂编译器IR
还有一个诡异现象:模型上板后精度下降,但日志里明明显示“所有算子已支持”。后来发现有些归一化算子、Softmax算子被工具链悄悄替换成了低精度近似实现,比如把指数函数做成查表、把LayerNorm的倒数用近似指令替代,精度损失被隐藏在了编译产物里。
遇到这种情况,要善用编译器提供的调试模式,保留每一层算子输出,与原始模型逐层比对。同时要检查中间表示,看看哪些算子被替换成了什么。另外,尽量在模型设计阶段就选用硬件明确支持的算子组合,不要给编译器太多“自由发挥”的空间。
4.3 多个模型并发抢带宽:内存与调度问题
车端AI任务不是单个模型独享NPU。实测发现,语音模型和视觉模型并发时,大模型延迟从50ms飙升到110ms,原因就是两者同时读DDR,带宽被占满。
解决方法分两层:软件层面,给大模型预留固定物理内存池,避免动态分配碎片;同时利用硬件优先级机制,给安全相关视觉模型更高带宽配额。系统层面,把轻量级周期性任务调度到CPU或DSP上,减少与主推理任务的资源竞争。切记,边缘模型不是孤岛,它的“最佳”一定是站在整个域控资源视角下的最佳。
4.4 常见问题速查表
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 上板后精度明显下滑 | 预处理不一致 / 算子被近似替换 | 逐层特征图对比,检查IR |
| 延迟比标称性能高出一倍 | 带宽争抢 / 访存密集算子过多 | 跑Roofline分析和per-op profile |
| 内存占用超标 | 动态shape / KV Cache未限制 | 固定输入长度,使用静态内存池 |
| 高温下性能下降 | 功耗墙触发降频 | 限制多核并发,增加温控调度 |
| 偶发死锁或崩溃 | 动态shape越界 / 内存越界 | 静态化输入,开启边界保护 |
4.5 避坑小建议
最后分享几条实践经验:第一,从项目第一天就让硬件编译器介入,不要等模型训练完再找适配;第二,每次修改模型结构都跑一次真实NPU延迟,用数据代替感觉;第三,不要盲目追求4bit量化,车规安全场景优先INT8或混合精度;第四,校准集一定不要怕麻烦,真实车端数据比干净数据集重要一百倍。
我个人做下来最大的感受是:硬件协同设计不是摆在PPT上的概念,而是实打实的迭代流程。最佳边缘模型本身也会随着芯片迭代、场景数据变化而移动,所以最好把它当作一个持续演进的基线,而不是交付物。如果你也在做类似的事,建议第一周就把功耗和延迟预算贴在工位上,让模型师、编译器工程师、系统工程师每天都看到;很多返工,其实可以靠这张预算表提前避免。