27届大模型面试准备(三十四):端侧大模型部署与压缩工程——把 7B 塞进手机与显卡的实战
本篇是 A 系列第 34 篇。前面 A19 讲过量化原理、A30 讲过服务端推理优化、A27 讲过蒸馏。但"把模型跑在手机、PC、边缘盒子、车载芯片"是另一套完全不同的工程:显存不是问题、内存带宽才是瓶颈,NPU 是主角、CUDA 不是,功耗和发热是硬约束。本文聚焦端侧(on-device)大模型部署,把压缩、推理引擎、内存管理串成一条可落地的链路。A33 讲的是"怎么取分布里的 token",本文讲的是"在哪取、取得起取不起"——两者共同构成大模型落地的最后一公里。
一、为什么端侧是大模型落地的必答题
云端推理有三大原罪:隐私(数据出端)、延迟(网络往返 + 排队)、成本(每 token 都烧钱)。端侧推理恰好对症:
云端推理 端侧推理 延迟组成: 网络RTT+排队+前向 仅前向(本地) 数据: 上传到服务器 不出本机 成本: 按 token / 调用计费 一次性部署, 边际成本≈0 离线: 依赖网络 飞行模式也能用 功耗: 服务器吃电 受设备电池/发热限制所以端侧不是"性能差的将就",而是"隐私敏感、实时、离线、低成本"场景的唯一解。手机语音助手、车载对话、本地代码补全、隐私文档分析,都指向端侧。面试被问"为什么不直接调 API",要能讲清这四条账。反过来,端侧也不是万能:复杂长上下文、需要最新知识的任务,还是得靠云端。所以"端还是云"本质是路由问题,本文第七节会讲。
二、端侧的三座大山:带宽、内存、算力
服务端优化常盯着"显存够不够",端侧第一瓶颈其实是内存带宽(memory bandwidth)。
端侧推理时间 ≈ 计算时间 + 搬运时间 小算子: 受算力限制 大矩阵(权重读取): 受 内存带宽 限制 <- 端侧主战场 例: 7B 模型 FP16 权重 ~14GB, 哪怕 100GB/s 带宽, 仅"读一遍权重"就要 0.14s 要实时(30 tok/s)就得反复读, 带宽直接决定吞吐三座大山:
1.带宽墙:权重要从存储(DRAM/磁盘)搬到计算单元,带宽决定上限。量化直接砍权重体积,是最有效的手段。
2.内存墙:手机的可用内存可能只有 4~8GB,模型权重 + KV Cache + 运行时必须都装下,常要靠mmap 按需分页而非一次性加载。
3.算力墙:NPU/CPU 峰值算力有限,且持续算力受发热限制(手机跑几秒就降频)。
┌──────────────┬─────────────┬────────────────────────────┐ │ 瓶颈 │ 端侧表现 │ 对应解法 │ ├──────────────┼─────────────┼────────────────────────────┤ │ 内存带宽 │ 致命 │ 量化(4bit)、权重复用 │ │ 可用内存 │ 紧张 │ mmap 分页、KV Cache 压缩 │ │ 持续算力 │ 受发热限制 │ 低比特、算子融合、NPU │ │ 功耗/电池 │ 硬性约束 │ 早退、动态算力分配 │ └──────────────┴─────────────┴────────────────────────────┘三、压缩:端侧的第一刀永远是量化
端侧模型几乎必做量化。回顾量化家族(A19 详细讲过,这里讲"部署视角"):
- INT4 / INT2:端侧主流。7B 模型 INT4 后权重约 3.5GB,能塞进手机。AWQ、GPTQ 是常用 PTQ 方案。
- FP8:服务端友好,端侧 NPU 逐步支持,精度损失小。
- 混合精度:敏感层(如 attention 的某些投影)留高精度,其余压低——比"全层统一 4bit"更稳。
- 训练后量化(PTQ)vs 量化感知训练(QAT):端侧追求极致时,QAT 用少量数据微调恢复精度,但成本高;多数场景 PTQ + 校准集足够。
# 用 llama.cpp / GGUF 做端侧量化(概念示意)# 1) 原始模型转 GGUFpythonconvert_hf_to_gguf.pyQwen2.5-7B/--outfileqwen.gguf# 2) 量化到不同比特./llama-quantizeqwen.ggufqwen-Q4_K_M.ggufQ4_K_M./llama-quantizeqwen.ggufqwen-Q2_K.ggufQ2_K# 更小更失真# Q4_K_M: 质量与体积平衡, 端侧 7B 首选# Q5_K_M: 质量更好, 体积更大一个容易被忽视的点:端侧的"精度损失"比服务端更敏感,因为端侧常跑小模型(1B~3B),本来就脆弱,压到 INT2 可能直接崩。所以端侧量化要更谨慎地做"逐层敏感度分析",把对 perplexity 影响大的层保留高精度。这一点和 A19 讲的"服务端量化可接受更大压缩比"形成对照——端侧容错更低。
四、推理引擎:谁在端侧跑模型
端侧没有 vLLM 这种"大一统",而是按平台分裂:
┌────────────────┬──────────────────────┬──────────────────────┐ │ 引擎/框架 │ 平台 │ 特点 │ ├────────────────┼──────────────────────┼──────────────────────┤ │ llama.cpp │ 跨平台(C/CPU/GPU) │ GGUF、量化生态最成熟 │ │ MLC LLM │ 移动/iOS/Android/Web │ TVM 编译, NPU 友好 │ │ Ollama │ 桌面(Mac/Win/Linux) │ llama.cpp 封装, 易用 │ │ ExecuTorch │ 端侧(PyTorch 官方) │ 移动端推理, 边缘部署 │ │ TensorRT-LLM │ NVIDIA GPU/数据中心 │ 极致优化, 非严格端侧 │ │ CoreML/MNN │ Apple/移动 NPU │ 厂商加速, 闭源生态 │ │ onnxruntime │ 跨平台 │ ONNX 中间表示 │ └────────────────┴──────────────────────┴──────────────────────┘选型逻辑:要最大兼容性与量化生态选 llama.cpp;要真·手机 NPU 加速选 MLC LLM / ExecuTorch;要桌面开发者体验选 Ollama。面试能列出这张表并说清"为什么端侧没有统一引擎",已经超出多数候选人。根本原因是端侧硬件(NPU 架构、驱动、内存模型)极度碎片化,一个引擎很难在所有芯片上都做到最优。
五、内存管理:mmap 与 KV Cache 的端侧腾挪
端侧内存紧张,两个关键技术:
5.1 mmap 按需加载权重
大模型权重文件可能比可用内存大。用内存映射(mmap)把文件映射到虚拟地址,只在实际访问的页才真正读入物理内存,OS 按 LRU 回收。这样"模型能开、但不一次性占满内存"。
// llama.cpp 用 mmap 加载权重(概念)void*ptr=mmap(NULL,file_size,PROT_READ,MAP_PRIVATE,fd,0);// 访问某层时 OS 才把对应页换入; 不需要的页被回收// 手机上可配合 MADV_DONTNEED 主动释放不用的层5.2 KV Cache 压缩
端侧上下文长了 KV Cache 占内存爆炸。手段(A30 提过,端侧更重要):
-量化 KV Cache(8bit/4bit),省一半以上。
-滑动窗口 / 对话裁剪:只保留最近 N 轮。
-前缀缓存复用:系统 prompt 的 KV 只算一次。
端侧 7B, 上下文 4k, FP16 KV ~ 每层都要存 k,v -> 量化到 8bit 直接省 50% 内存 -> 配合只保留最近对话, 长聊天也不崩六、NPU 与算子:端侧的算力真相
手机的"AI 算力"宣传很猛(几十 TOPS),但工程上要清醒:
-峰值算力 ≠ 持续算力:NPU 跑几秒就因发热降频,实际可持续算力可能只有峰值的 1/3。
-算子覆盖不全:NPU 对常见算子快,但对新型算子(如某些 MoE 路由、自定义激活)可能不支持,被迫回退 CPU,反而更慢。
-精度支持有限:很多 NPU 只高效支持 INT8/INT4,FP16 反而慢,所以端侧量化不是"可选项"而是"必选项"。
# MLC LLM 编译到手机 NPU(概念)# 1) 定义模型 + 量化model=mlc.Model("Llama-3-8B",quantization="q4f16_1")# 2) 用 TVM 编译为目标硬件 (Android NPU / Apple Neural Engine)model.build(target="android",gpu="mali")# 3) 打包 apk / 动态库, 端上加载七、端云协同:不是二选一,而是分工
最成熟的落地往往是端云协同,而非纯端侧:
用户请求 │ ├─> 简单/隐私任务 ──> 端侧小模型直接答 (低延迟、零成本、离线) │ └─> 复杂/长上下文 ──> 路由到云端大模型 (高质量) 路由判断: 意图复杂度 / 是否需要检索 / 隐私等级这其实是 B26(Agent 成本工程)和 A30(推理优化)在端侧的投影:用路由把"贵的大模型"用在刀刃上。端侧小模型做"第一道过滤 + 隐私兜底",云端做"重活"。面试能画出这个路由架构,会显得你有真实落地经验而非只会调 API。
八、端侧特有的质量坑
- 量化掉点:4bit 下某些任务(尤其是需要精确计数、长链推理)明显退化。对策:敏感层升精度、关键任务走端云协同。
- 首 token 延迟(TTFT):端侧加载权重 + 编译图可能要几秒,用户以为卡死。对策:预加载、预热、进度提示。
- 发热降频:连续对话后越聊越慢。对策:限流、动态降 batch、必要时提示"切云端"。
- 系统碎片化:Android 机型 NPU 各异,一套引擎难全适配。对策:CPU 兜底路径 + 分机型灰度。
九、生产落地深潜:部署 checklist 与工程真相
把端侧模型真正推上线,我建议按这张清单逐条过关。第一关是体积与内存:目标设备可用内存多少?模型量化后权重 + KV Cache 峰值是否压得进?用 mmap 能否避免一次性 OOM?第二关是延迟预算:TTFT(目标<1.5s)、解码速度(目标>15 tok/s 才不憋屈)、长对话下是否仍能维持?第三关是精度验收:在目标任务的本地测试集上,4bit 相比 FP16 掉点是否<2%?掉点超标的层要升回 8bit 或 16bit。第四关是功耗与发热:连续 5 分钟对话,设备温度与降频曲线如何?是否需要在系统层限流。
这张清单的精髓是"先定设备规格,再反向选压缩档位",而不是"先训好模型再想办法塞"。很多团队拿着一个 14GB 的 FP16 模型去适配 6GB 内存的手机,怎么做都难受——正确做法是立项时就锁定"目标设备 + 目标延迟 + 目标精度",据此选基座大小(1B/3B/7B)和量化档(Q4/Q5/Q8)。这和普通服务端"模型越大越好、显存不够就加卡"的思维方式正好相反,是端侧工程最反直觉、也最容易被 junior 忽略的一点。
9.1 量化格式与引擎强绑定
再补一个真实踩坑:端侧量化格式和引擎强绑定。llama.cpp 的 GGUF Q4_K_M、MLC 的 q4f16_1、CoreML 的 4bit,三者数值实现不同、跨引擎不能混用。曾经有团队在服务器用 llama.cpp 测了 Q4 精度达标,上线却用 MLC 重新量化,结果同一模型精度差了 5 个点——因为校准集和量化粒度不同。所以端侧压缩必须"在哪个引擎跑,就在哪个引擎量化",把量化步骤固化进 CI,而不是本地手动敲命令。能讲出这个细节,面试基本直接判定你做过真端侧,而不是纸上谈兵。
9.2 端侧与服务端优化的本质差异
补一节"思维转换"——很多做惯服务端推理优化的工程师,上手端侧会本能地用同一套打法,结果处处碰壁。核心差异有四点。其一,瓶颈不同:服务端卡显存和算力,端侧卡内存带宽,所以服务端靠堆卡、端侧靠量化压缩带宽需求。其二,可观测性不同:服务端有完善的 GPU 监控、profiling,端侧 NPU 常常是黑盒,连"这一层跑了多久"都拿不到细粒度数据,只能靠端到端延迟反推。其三,失败模式不同:服务端失败多是 OOM 或超时,端侧失败多是发热降频导致的"前几轮快、后几轮慢"、以及机型碎片化导致的"这台能跑那台崩"。其四,迭代速度不同:服务端改个配置秒级生效,端侧改量化要重新编译、重新打包、重新上架,迭代以天计。把这四点刻进脑子,才能避免"用服务端思维做端侧"的错位。
9.3 实测解读、跨平台适配与成本账
补一节"看懂数字"的能力,这是工程师和调参侠的分界。端侧部署报告里常写"Q4 下 perplexity 只涨 0.3",但你要会追问:这个 0.3 是在什么测试集上测的?通用语言模型基准的 0.3 可能毫无意义,业务任务的掉点才是真金。我习惯在目标业务的小测试集(比如真实的客服问答、真实的代码补全片段)上做"精度验收",而不是信通用 benchmark。曾经有个项目在 WikiText 上 Q4 几乎无损,但实际代码补全任务掉点 12%——因为代码对精确 token 更敏感,通用基准掩盖了。所以端侧验收的铁律是:在业务数据上测,不在公开基准上自我安慰。
跨平台适配是另一块硬骨头。同一份 GGUF 在 llama.cpp 上跑得欢,到了某 Android 机型用 MLC 重编译,速度可能差三倍,原因是 NPU 驱动版本和算子支持不同。我的经验是建立机型矩阵测试:列出目标用户 TOP 10 机型,每台实测 TTFT、解码速度、内存峰值、发热降频曲线,据此决定"哪些机型走 NPU 加速、哪些机型降级 CPU、哪些机型直接提示用云端"。这其实是一张"能力分级表",和 B26 的成本工程、B34 的路由思想一脉相承——不是所有设备都该跑同一个模型,按能力分级是端侧落地的常识。另外,iOS 和 Android 的 NPU 生态完全割裂(CoreML vs NNAPI/Mali),一套引擎很难两端都最优,往往要接受"一端体验略差"的现实,或者分平台投入。
关于趋势也补几句,面试常问"端侧未来怎么走"。其一是端侧小模型能力快速逼近:1B~3B 级别模型经过高质量数据和蒸馏,已能 cover 大量日常任务,端侧可行性越来越高。其二是NPU 生态标准化:各芯片厂商在推统一推理接口,跨机型适配成本有望下降。其三是端云协同成为默认架构:纯端或纯云都非最优,路由 + 协同是主流。其四是端侧 Agent:把本文的端侧模型 + 轻量编排跑在手机上,做离线可用的个人助理,是接下来的兵家必争之地。把这些趋势和前面的工程现实结合着讲,会让你的端侧认知显得既落地又有前瞻性。
9.4 哪些服务端技巧在端侧不成立
最后收个口:端侧大模型不是"把云端模型砍小搬过来"这么简单,它是一套独立的方法论——以带宽而非算力为第一约束、以量化而非堆卡为第一手段、以机型分级而非统一部署为第一策略。补充一个常被问的工程细节:端侧要不要做投机解码(A25/A30)?理论上投机解码能加速自回归,但端侧 NPU 对"草稿模型 + 验证"这种双模型来回调度支持很差,且草稿模型本身也要占内存,收益常常被 overhead 吃光。所以端侧主流加速手段仍是"量化 + 算子融合 + KV Cache 量化",投机解码在端侧尚未成主流。这也说明一个道理:服务端的加速技巧不能无脑平移到端侧,每个技巧都要重新在端侧约束下评估收益。能指出"哪些服务端优化在端侧不成立、为什么",比罗列一堆优化名词更显深度——它体现的是"按平台重新推导"的工程思维,而非"背优化清单"。
9.5 端侧 MoE 与"首次加载"体验
补两个常被低估的实战点。第一是端侧 MoE。端侧 MoE(如某些 1B-active 的小 MoE)是个有意思的方向——总参数大但每 token 只激活一小部分,推理算力接近小模型、能力接近大模型。但它对 NPU 的算子支持和内存布局要求更高,工程成熟度还不如稠密小模型。所以当下端侧主力仍是"稠密小模型 + 激进量化",MoE 端侧是值得关注但尚早的趋势。面试能讲清"为什么端侧 MoE 还没成主流",比泛泛说"MoE 好"更显老练。
第二是首次加载体验。很多 demo 在电脑上跑得飞快,因为权重早进内存;真机上第一次冷启动要 mmap + 可能的图编译,TTFT 可能 5~10 秒,用户直接以为卡死。对策是预加载常驻、或先推一条"正在加载"的过渡反馈、或在 App 启动时后台预热。这看似小事,却是端侧产品口碑的分水岭——用户不在乎你模型多先进,只在乎"点下去多久出字"。把首响体验当一等指标,是端侧团队和玩具项目的区别。
9.6 端侧部署的完整 timeline 与格式治理
给一个真实的端侧落地排期,让抽象方法论落到节奏上。第 1~2 天:锁定目标设备清单(TOP 机型 + 最低内存规格)和验收指标(TTFT、tok/s、精度掉点上限、温升曲线)。第 3~5 天:选基座大小 + 量化档位,在本地做精度验收,确定 Q4 还是 Q5。第 6~8 天:接入推理引擎(llama.cpp / MLC),打通 mmap 加载、KV Cache 量化、流式输出。第 9~12 天:做端云协同路由(简单走端、复杂走云),接业务。第 13~15 天:机型矩阵实测,分级策略上线,灰度 5% 看崩溃率与满意度。这个 timeline 的关键不是天数,而是"先定规格再选方案"的顺序——反过来(先训大模型再想办法塞)一定反复返工。
最后一句实务经验:模型格式与生态碎片化的 CI 化治理。GGUF(llama.cpp)、Safetensors(HuggingFace)、GGML 旧格式、各厂商私有格式彼此不通用,一篇模型常有多个格式的社区转换版,质量参差。选型时要确认"目标引擎官方支持该格式、且该格式版本与引擎版本匹配",否则轻则精度异常,重则加载崩溃。建议把"格式转换 + 校验"固化进 CI 流水线,每次模型更新自动转目标格式并跑精度验收,而不是人工临时敲命令。这和服务端"模型上线要走 CI"是一个道理,端侧只是多了格式这一环。把端侧模型当成"需要严格版本管理与验收的制品",是工程成熟度的体现。
9.7 端侧基座选型矩阵
立项时按"能力需求 vs 设备余量"选基座,给一张速查表:
基座 参数量 量化后体积 适用场景 设备门槛 1B 约1B ~0.6GB Q4 闲聊/分类/简单抽取 低端机也可 3B 约3B ~1.8GB Q4 摘要/改写/轻量问答 中端机流畅 7B 约7B ~3.5GB Q4 复杂推理/代码生成 旗舰机/端侧独显经验法则:先按"最低目标设备"选能跑的最大基座,再用量化压到体积预算内;不要反过来拿 7B 去适配 4GB 内存的低端机,那样怎么做都难受。这也解释了为什么端侧 1B~3B 蒸馏模型(呼应 A27 蒸馏)近来吃香——它们在低端设备上就能 cover 大量日常任务,是"端侧可行性"的真正推手。选型时若拿不准,先用最小可用基座跑通链路,再用更大基座做 A/B,比一上来堆 7B 再想办法压缩更稳。
十补、端侧部署的验收速查清单
把前文要点压成一张可勾选清单:①体积——量化后权重+KV Cache 峰值压进目标设备内存,mmap 兜底;②延迟——TTFT<1.5s、解码>15 tok/s、长对话不崩;③精度——业务测试集上 4bit 掉点<2%,超标层升回 8/16bit;④功耗——连续 5 分钟温升与降频曲线可接受;⑤协同——简单走端、复杂走云,路由规则明确。五条全过才上线,任何一条不达标都先回去调压缩档位或换更小基座,而不是硬上。能背出这张清单,面试基本直接判定你做过真端侧,而非只在笔记本上 ollama run。
十、面试速答 + 高频追问清单
面试速答(背下来):
- 端侧三瓶颈:内存带宽(主)、可用内存、持续算力(受发热限)。量化砍权重体积是最直接手段。
- 端侧几乎必量化:7B INT4≈3.5GB 可塞手机;敏感层留高精度,小模型压到 INT2 易崩。
- 引擎按平台分:llama.cpp(跨平台/量化生态)、MLC LLM/ExecuTorch(真 NPU)、Ollama(桌面易用)。
- mmap 按需分页避免一次性 OOM;KV Cache 量化+对话裁剪省内存。
- NPU 峰值≠持续算力,且算子/精度覆盖有限,量化是必选项不是可选项。
- 端云协同:小模型做隐私兜底+过滤,云端做重活,路由是关键。
- 量化必须在目标引擎做,格式与引擎强绑定,跨引擎精度不可继承。
高频追问:
1. 为什么端侧不学服务端用 vLLM?答:缺统一 NPU 抽象、内存更紧、要离线,引擎按平台分裂。
2. mmap 真能省内存吗?答:只映射不占满物理内存,访问才换入,OS 回收不用的页。
3. 4bit 掉点怎么救?答:敏感层升精度、关键任务走端云协同、必要时 QAT 微调。
4. 端侧 MoE 能用吗?答:方向好但 NPU/内存布局支持不成熟,主力仍是稠密小模型+量化。
5. 怎么定量化档位?答:先锁设备规格与延迟/精度目标,反推基座大小与比特档,而非先训后塞。
6. 为什么量化必须在目标引擎做?答:各引擎数值实现/校准不同,跨引擎精度不可继承。
7. 投机解码在端侧为什么难落地?答:双模型调度 NPU 支持差、占内存,收益常被 overhead 吃光。