端侧小模型上线三周就回退:五个维度的算账与判据

举报
上云 发表于 2026/11/10 09:29:48 2026/11/10
【摘要】 一次端侧小模型上线三周后回退云端的复盘。拆解延迟、内存、功耗、效果损耗与运维成本五个维度,给出硬门槛加加权评分的判据清单,明确哪些任务该下沉、哪些必须留云端,并附分层回退的落地动作。

一个 1.8B 的量化模型塞进手机,离线可用、单次边际成本几乎为零,评审会上一路绿灯。上线三周后我们把这条链路砍回云端——不是因为它跑不起来,而是跑起来之后,每一项隐性成本都在超支。「能不能跑」是工程可行性,「该不该跑」是成本与体验的分配,用同一套指标衡量这两件事,就会系统性误判。

先看账本:那次回退亏在什么地方

场景很典型:文档类 App 的划词改写 + 意图识别,输入通常是 30~300 token,输出 20~200 token。上线前的估算是「端侧零网络开销,延迟更低,还省云端推理费」,上线三周后的实测是:

维度 上线前估算 实测 偏差来源
延迟 P95 < 1s P95 6~18s 只算了「不用等网络」,没算手机算力
内存 峰值 1.2GB 峰值 1.9GB,低端机 OOM KV cache 与运行时开销漏算
功耗 忽略 30s 生成升温 8~11℃,二次调用降频 热设计功耗与持续负载
效果 量化掉点 < 2% 改写采纳率 92% → 78% 只测 perplexity,没测业务指标
运维 一次适配 每版本强绑定 App 发版 设备矩阵与可观测性

五条里有四条不是「跑不动」,而是「跑了不划算」。下面逐条拆。

延迟:端侧赢的是确定性,不是速度

这是最容易被「零 RTT」这个直觉骗过去的维度。把延迟拆成两段算:

def budget(prompt_tokens, out_tokens, *, prefill_tps, decode_tps, fixed_ms):
    ttft = fixed_ms / 1000 + prompt_tokens / prefill_tps
    return round(ttft, 2), round(ttft + out_tokens / decode_tps, 2)

# 云端:RTT 80ms + 排队 200ms,prefill 2000 tok/s,decode 80 tok/s
print(budget(300, 200, prefill_tps=2000, decode_tps=80, fixed_ms=280))  # (0.43, 2.93)
# 中端机端侧 1.8B INT4:无网络,prefill 60 tok/s,decode 15 tok/s
print(budget(300, 200, prefill_tps=60, decode_tps=15, fixed_ms=50))     # (5.05, 18.38)

按同一套参数铺开看不同 token 规模:

输入/输出 token 云端 TTFT 云端总时长 端侧 TTFT 端侧总时长
40 / 20 0.30s 0.55s 0.72s 2.05s
150 / 60 0.36s 1.11s 2.55s 6.55s
300 / 200 0.43s 2.93s 5.05s 18.4s

结论反直觉但很稳定:在正常网络下,端侧几乎在任何 token 规模上都不赢延迟,因为手机 NPU 与云端 GPU 的 prefill 吞吐差一到两个数量级,而 RTT 只有几十毫秒。端侧真正提供的是三件东西——延迟方差小(不受网络抖动、弱网、跨境路由影响)、离线可用、数据不出端。所以延迟维度的判据不该是「端侧更快吗」,而是:

  • 用户的网络环境里,云端 P95 是否超过端侧 P95?只有弱网场景(地铁、电梯、跨境)成立;
  • 你是否能接受首字 1s 以内但总时长 2s 以上?短输出可接受,长输出不可接受;
  • 是否可流式?不可流式的任务(必须整段返回再校验)会把端侧的总时长劣势放大成卡顿感。

内存:算峰值,不要算权重

权重只是内存账的第一项。一次 2048 上下文的会话,实际占用是权重 + KV cache + 激活 + 运行时缓冲:

def kv_gb(layers, kv_heads, head_dim, ctx, bytes_per=2):
    return 2 * layers * kv_heads * head_dim * ctx * bytes_per / 1e9

print(round(kv_gb(24, 8, 128, 2048), 3))   # 0.201 GB (fp16 KV)
print(round(1.8 * 4 / 8, 2), "+ 0.2 + 运行时")  # 0.9 GB 权重

实测峰值 1.9GB,其中权重约占 1.1GB(embedding 与 lm_head 常常不量化),KV cache 0.2GB,剩下是算子临时缓冲、tokenizer、以及推理框架自身的常驻开销。判定门槛建议写死两条:

  • 常驻内存 ≤ 目标机型内存分档最低档的 25%,峰值 ≤ 40%;
  • 模型必须可被系统随时回收,且回收后能在 500ms 内冷启或优雅降级到云端。

第二条比第一条更容易出事。端侧模型常驻会被 LMK(低内存杀手)优先干掉,用户切回 App 时表现成「功能坏了」,而不是「慢了一点」。

功耗:发热是延迟的第二重税

功耗不在于耗掉多少电,而在于热积累导致的降频曲线。5W 负载持续 30 秒,耗电约 42mWh,对 17Wh 电池只有 0.25%,用户完全无感;但机身表面温度会爬 8℃ 以上,SoC 触发降频后第二次调用延迟直接翻倍,用户感知是「手机烫」+「越用越卡」。

def thermal_debt(watts, seconds, ratio_at_throttle=0.6):
    steady = seconds * ratio_at_throttle
    return round(watts * seconds / 3600 * 1000, 1), round(seconds - steady, 1)
print(thermal_debt(5, 30))  # (41.7 mWh, 12.0s 的等效性能损失)

可操作判据:单次连续推理 ≤ 10s,日内单用户累计端侧推理时长设上限,超限自动上云。任何需要「一次生成几百字」的任务都不该走端侧——它必然踩到热降频区。

效果损耗:换成分母可算的业务指标

量化掉点、上下文裁剪、蒸馏压缩会叠乘。用 perplexity 涨 3% 判断是自欺,因为端侧失败往往是「看起来还行但微妙地不对」,用户不会报错,只会不用。我们当时看的三个指标比离线评测有用得多:

  1. 采纳率:改写结果被直接接受的比例,92% → 78%;
  2. 回退率:用户撤销、重新生成、手改的比例;
  3. 可检测性:错误能否被 schema、语法或规则自动判出。

第三项是分水岭。结构化抽取类任务(JSON、字段归一、分类)有校验器兜底,错误能当场拦下来重试,端侧损耗是可承受的;生成类任务没有校验器,错误直接进用户视野,端侧损耗会全部转成体验损失。经验阈值:关键指标相对损失 ≤ 3%,且失败样本 90% 以上能被自动检出,达不到就不要下沉。

运维成本:被低估最严重的一项

云端模型灰度以小时计,端侧模型灰度以「App 发版周期」计。一次下沉,等于把自己绑进了移动端发布节奏,并且要维护 芯片 × OS 版本 × 内存档位 的适配矩阵——几百种组合,回归测试只能抽样,线上问题只能靠采样日志回溯,没有 trace,复现依赖用户机型。

粗略折算:

端侧总成本 = 多端适配人力 × 版本周期
           + 模型增量包的分发带宽
           + 回退率 × 每次回退的云端推理成本
           + 因发热/卡顿造成的卸载与留存损失

最后一项通常是最大项,也是上线前最不会进预算表的一项。判断标准:如果这个任务的调用频次不足够高、单次价值不足够低,端侧省下的推理费永远覆盖不了适配与流失成本。

判据清单:什么下沉,什么留云端

先过三条硬门槛,任一不满足直接放弃端侧:

  • 峰值内存 ≤ 最低目标机型可用内存的 40%;
  • 单次推理 ≤ 10s,且可流式或可异步;
  • 关键业务指标相对损失 ≤ 3%,失败可自动检出并回退。

再打分(每项 0~3 分,加权后 ≥ 12 分才值得下沉):

因子 权重 3 分 1 分 0 分
调用频次 ×2 人均日调用 > 20 2~20 < 2
单次 token 预算 ×2 < 100 100~400 > 400
隐私等级 ×2 数据不可出端 敏感但可脱敏 公开数据
错误代价 ×2 可校验可重试 用户可感知可修正 直接影响结论
网络环境 ×1 大量弱网/离线 偶发弱网 稳定网络
迭代速度要求 ×1 季度级 月度级 周级以内

对照下来边界相当干净:

  • 该下沉:输入法联想与纠错、端侧意图路由、离线语音唤醒与命令词、敏感文本的本地脱敏与打码、相机/相册的本地打标与检索、表单字段归一化。共同特征是短、高频、可校验、隐私敏感。
  • 必须留云端:长文档理解与摘要、多轮规划与工具调用、需要强推理的代码/数学、高质量生成与改写、需要周级迭代的策略模型、有审计留存要求的场景。共同特征是长上下文、不可校验、快速迭代。

介于两者之间的,用分层回退而不是二选一:

def route(task, device):
    if task.tokens > 400 or task.needs_tools:
        return "cloud"
    if device.mem_class < 6 or not device.npu_ready:
        return "cloud"
    if task.privacy == "strict" or task.offline:
        return "edge_with_cloud_check"   # 端侧出结果,云端只做校验
    return "edge" if task.verifiable else "cloud"

落地建议

  1. 先用 token 预算做粗筛。 输入 + 输出超过 400 token 的任务直接排除端侧候选,不必进入后续评估。这一条能砍掉大半错误方案。
  2. 把延迟指标从 P50 改成 P95 与方差。 端侧的价值是延迟确定性,不是平均更快。监控里同时上报云端 P95 与端侧 P95,只有弱网分桶里端侧占优才保留。
  3. 内存与热预算写进准入清单,作为版本门禁。 常驻 ≤ 25%、峰值 ≤ 40%、单次 ≤ 10s 三条不过就不许上车,别指望上线后调优。
  4. 端侧默认走「端侧出草稿 + 云端校验」的混合链路。 有校验器的任务才允许端侧直出,其余一律带云端复核通道,回退率超阈值自动切回云端并告警。
  5. 埋点三件事再谈收益。 设备档位、端侧耗时分布、回退率。这三个数据不齐,任何「端侧省了多少钱」的结论都是估算,不是账。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。