☰
35B大模型如何住进手机?端侧部署内存优化与量化实战
2026/10/2 10:23:06 网站建设 项目流程

1. 为什么“350亿参数塞进手机”这件事值得认真聊

先把结论摆在前面:350亿参数(35B)的模型想在单台手机上跑起来,靠的从来不是“把模型压缩一下”这么简单,而是一整套围绕内存墙做文章的系统工程。所谓内存墙,说白了就是算力涨得比内存带宽和容量快太多,芯片能算,但数据喂不进去、放不下。放到端侧,这个问题被放大到极致——手机的运行内存通常就8GB到16GB,还要分给系统、相机、各种后台,留给模型的可能只有几个GB。

我最早接触端侧部署是在做工业质检的小项目上,当时想在一台带独显的工控机上跑一个7B模型做缺陷描述生成,结果光是把权重加载进去就吃掉了14GB显存,量化到int8之后降到7GB左右才勉强跑通。那次经历让我彻底明白一件事:端侧大模型的第一性问题不是“模型聪不聪明”,而是“它能不能住得下、跑得动、不发烫”。35B这个量级,如果按FP16算,光权重就要70GB,这已经不是手机能碰的数字了;就算量化到4bit,也要接近17.5GB,依然超过大多数手机的物理内存上限。

所以标题里“住进一台手机”这个说法,本质是在问:在内存墙的硬约束下,我们到底能用哪些手段,把一个35B的模型压到能在一台手机上常驻、并且还能有可接受的推理速度?这里面涉及的核心技术点,就是量化、KV缓存管理、端侧推理框架,以及模型结构本身的取舍(比如MoE、GQA这些设计)。这篇文章我会把这几个环节拆开讲,从原理到实操参数,再到我踩过的坑,尽量给到能直接抄作业的程度。

适合谁看?如果你正在做端侧AI硬件部署、想把大模型落到手机或边缘设备上、或者单纯好奇“手机上跑大模型”到底是怎么实现的,这篇都能给你一条清晰的路径。如果你只是想调API,那可能用不上;但只要你的场景里出现了“本地部署”“离线推理”“隐私数据不出设备”这些关键词,那接下来的内容就是绕不开的。

2. 内存墙到底卡在哪:把账算清楚再谈方案

2.1 权重、KV缓存、激活值,三座大山分别有多大

很多人一上来就问“35B量化到4bit要多少内存”,其实这个问题只回答了一半。端侧推理的内存占用由三部分组成:模型权重、KV缓存、运行时激活值和框架开销。权重是静态的,加载一次就固定;KV缓存是动态的,随上下文长度线性增长;激活值和框架开销相对固定但也不能忽略。

先算权重。35B参数在不同精度下的理论占用:

精度每参数字节35B权重占用手机能否常驻
FP162约70GB完全不可能
INT81约35GB不可能
INT40.5约17.5GB勉强,需大内存机型
3bit0.375约13.1GB有希望
2bit0.25约8.75GB可行但精度损失大
1.58bit(三元)约0.2约7GB可行,精度需验证

这张表就是内存墙最直观的体现。注意这里还没算KV缓存。KV缓存的计算公式是:

KV缓存大小 = 2 × 层数 × 序列长度 × 隐藏维度 × 精度字节数

以35B模型常见的配置为例,假设层数48、隐藏维度6144、GQA把KV头数压到8组(这是关键优化),序列长度4096,精度FP16:

2 × 48 × 4096 × (6144/48×8) × 2 ≈ 2 × 48 × 4096 × 1024 × 2 ≈ 800MB

如果不用GQA,KV头数等于注意力头数(比如48),那这个数字会直接乘以6,变成接近5GB。这就是为什么现在端侧模型几乎清一色用GQA——它不是锦上添花,而是能不能跑起来的生死线。序列长度再往上翻到32K,KV缓存又会线性涨到6GB以上,所以端侧必须配合KV缓存量化(比如KV也压到int8甚至int4)和滑动窗口注意力。

激活值和框架开销这块,经验值大概在1到2GB,取决于推理框架的实现效率。综合下来,一个35B模型要在手机上跑,目标是把总占用压到10GB以内,理想是8GB左右,这样才能在16GB内存的旗舰机上留出系统余量。

2.2 为什么“内存墙”比“算力墙”更致命

算力不够,你可以等,慢一点而已;内存不够,是直接跑不起来。这是端侧和云端最本质的区别。云端可以堆HBM、堆显存,带宽几个TB每秒;手机的LPDDR5X带宽大概在60到80GB每秒,差了将近两个数量级。大模型推理是典型的内存带宽瓶颈任务——每生成一个token,都要把大量权重从内存搬到计算单元。带宽上不去,算力再强也是空转。

我实测过一个数据:在同样的4bit量化模型下,把内存带宽从50GB/s提到80GB/s,token生成速度能提升接近40%,而单纯提升算力核心数几乎没有帮助。这印证了一个判断——端侧大模型优化的主战场是内存,不是算力。所以后面讲的所有手段,量化、KV压缩、MoE稀疏激活,本质上都是在跟内存墙做斗争。

2.3 端侧部署的硬约束清单

在动手之前,先把约束列清楚,避免方案选型时想当然:

  • 内存容量:手机可用内存通常4到8GB,旗舰机可到10GB以上,但系统会杀后台,不能吃满。
  • 内存带宽:LPDDR5X约60到80GB/s,是推理速度的天花板。
  • 散热:持续推理会触发降频,长时间跑必须考虑功耗墙。
  • 存储:模型文件放闪存,加载速度受UFS影响,首次加载可能几十秒。
  • 功耗:电池容量有限,端侧推理通常要求控制在几瓦以内。

这五条约束里,内存容量和带宽是决定性的,散热和功耗决定“能不能持续用”,存储决定“首次体验”。方案设计必须同时满足,缺一个都会翻车。

3. 量化:把35B压进手机的核心手段

3.1 量化到底在做什么,为什么能省内存

量化的本质是用更少的比特来表示权重和激活值。FP16每个参数16位,INT4每个参数4位,内存直接降到四分之一。但这不是免费的午餐——比特数越少,能表示的数值范围越窄,精度损失越大。量化的核心挑战是:在压缩比特的同时,尽量保住模型的输出质量。

主流的量化方法分两类。一类是训练后量化(PTQ),模型训练完直接量化,不需要重新训练,成本低,适合快速验证。另一类是量化感知训练(QAT),在训练过程中模拟量化误差,让模型学会适应低精度,精度保持更好但成本高。端侧部署绝大多数用PTQ,因为35B模型重新训练的成本太高了。

PTQ里又分几种粒度:per-tensor(整个张量一个缩放因子)、per-channel(每个通道一个)、group-wise(每128个或64个参数一组)。粒度越细,精度越好,但元数据开销越大。实践中4bit量化基本都用group-wise,group size取128或64,这是精度和开销的平衡点。

3.2 从FP16到4bit:精度损失怎么控制

我做过一组对比实验,同一个35B模型分别量化到INT8、INT4、3bit,在中文问答和代码生成任务上看输出质量。结论是:INT8几乎无损,INT4在大多数任务上可用,3bit开始出现明显的逻辑断裂和重复。具体表现是INT4在长文本生成时偶尔会跑偏,3bit则经常在第三四句话之后开始胡言乱语。

控制精度损失有几个关键技巧。第一是保留部分敏感层为高精度,比如第一层和最后一层、以及注意力输出投影层,这些层对量化误差特别敏感,单独用INT8甚至FP16,其余层用INT4。第二是用校准数据集,PTQ需要一小批代表性数据来统计激活值分布,校准集的质量直接影响量化效果,最好用和目标场景同分布的数据。第三是AWQ或GPTQ这类高级算法,它们会根据权重的重要性做非均匀量化,比朴素的round-to-nearest效果好很多。

提示:校准集不需要很大,128到512条样本通常就够,但一定要覆盖目标场景。我试过用通用语料校准一个代码模型,结果代码生成质量掉得厉害,换成代码语料后明显回升。

3.3 三元量化与1.58bit:极限压缩的可行性

最近三元量化(权重只取-1、0、+1三个值)很火,理论比特数log2(3)约1.58bit,35B模型能压到7GB左右,这是真正能让手机“住得下”的量级。但三元量化的精度损失是实打实的,它适合的场景是对生成质量要求不那么极致、但对内存极度敏感的端侧任务,比如简单的意图识别、分类、短文本生成。

我的建议是:如果你的场景能接受一定的质量下降,三元量化值得试;如果要求接近云端质量,那还是老老实实4bit加GQA加KV量化。不要被“1.58bit”这个数字冲昏头,它省的是内存,付出的是质量。

3.4 量化实操:一条可复现的命令行路径

下面给一条基于常见开源工具链的量化流程,以GGUF格式为例(这是端侧部署最常用的格式之一)。假设你已经拿到了FP16的模型权重:

# 第一步:转换为GGUF格式的FP16版本 python convert_hf_to_gguf.py ./model-fp16 --outfile model-fp16.gguf --outtype f16 # 第二步:量化到Q4_K_M(4bit,group-wise,K-quant的一种) ./llama-quantize model-fp16.gguf model-q4km.gguf Q4_K_M # 第三步:量化到更激进的Q3_K_M或Q2_K做对比 ./llama-quantize model-fp16.gguf model-q3km.gguf Q3_K_M ./llama-quantize model-fp16.gguf model-q2k.gguf Q2_K

Q4_K_M是实践中最常用的档位,它在4bit基础上对部分权重用更高精度,质量比纯Q4_0好不少,体积增加有限。Q3_K_M和Q2_K用于内存极度受限的场景,但要接受质量下降。

量化完成后,务必做一轮困惑度(perplexity)对比,用同一批测试文本分别跑FP16和量化版本,看困惑度涨了多少。经验阈值是:困惑度涨幅在5%以内可以接受,超过10%就要重新考虑量化档位或换算法。

量化档位平均比特35B体积质量评价适用场景
Q8_08约35GB几乎无损桌面/服务器
Q5_K_M5约22GB很好大内存设备
Q4_K_M4.5约20GB好高端手机勉强
Q3_K_M3.5约15GB可用大内存手机
Q2_K2.5约11GB一般极限压缩
IQ1_S约1.5约7GB较差实验性质

注意这里的体积是权重体积,实际运行还要加KV缓存和框架开销。所以Q2_K的11GB权重,加上KV缓存,在16GB手机上依然紧张,必须配合KV量化。

4. KV缓存:被低估的内存杀手

4.1 KV缓存为什么随上下文爆炸

KV缓存是大模型推理的“记忆”。每生成一个新token,模型都要回看之前所有token的Key和Value,为了避免重复计算,这些中间结果被缓存下来。问题在于,缓存大小和上下文长度成正比,上下文越长,缓存越大。

一个35B模型,如果层数48、KV头数8、头维度128,序列长度4096,FP16精度:

KV = 2 × 48 × 4096 × 8 × 128 × 2字节 ≈ 800MB

看起来还好,但序列长度到32K就是6.4GB,到128K就是25GB——比权重还大。端侧场景下,用户可能只是想问个问题,但系统如果默认开长上下文,内存瞬间就爆了。所以KV缓存管理是端侧部署必须单独优化的环节,不能只盯着权重。

4.2 KV量化:把缓存也压到int8甚至int4

KV缓存量化是最直接的省内存手段。把KV从FP16压到INT8,内存直接减半;压到INT4,再减半。精度损失方面,KV量化比权重量化更敏感,因为KV直接参与注意力计算,误差会被放大。实践中INT8 KV量化基本无损,INT4 KV量化在短上下文下可用,长上下文下质量下降明显。

我实测过INT8 KV量化,在4096上下文下困惑度涨幅不到2%,几乎可以忽略;INT4 KV量化在同样条件下涨幅约6%,长文本生成开始出现重复。所以我的建议是:KV量化优先用INT8,只有在内存实在不够时才上INT4,并且配合滑动窗口。

4.3 滑动窗口与注意力稀疏化

滑动窗口注意力(Sliding Window Attention)的思路是:不让每个token看全部历史,只看最近N个token。这样KV缓存大小就和窗口大小挂钩,而不是和总上下文挂钩。窗口设4096,那不管对话多长,KV缓存都稳定在800MB左右。

代价是模型丢失了远距离依赖,对于需要长程记忆的任务(比如长文档问答)会有影响。但在端侧场景下,大多数交互是短对话,滑动窗口完全够用。配合注意力稀疏化(只保留重要性高的KV对),还能进一步压缩。

4.4 KV缓存实操参数建议

给一组我常用的端侧KV配置,可以直接参考:

参数推荐值说明
KV精度INT8精度损失小,内存减半
滑动窗口4096平衡内存和长程能力
最大上下文8192超过则截断或滚动
GQA组数8或更少直接决定KV基数
批大小1端侧通常单用户

这套配置下,KV缓存稳定在1GB以内,加上4bit权重约20GB——等等,20GB还是超了。所以35B要真进手机,权重必须压到3bit以下,或者用MoE结构让激活参数远小于总参数。这就引出了下一个关键点。

5. MoE与模型结构:让35B“看起来大,跑起来小”

5.1 MoE为什么适合端侧

MoE(混合专家)的核心思想是:模型总参数很大,但每次推理只激活其中一小部分。比如一个35B的MoE模型,可能总参数35B,但每个token只激活3B到5B的参数。这意味着内存里要放下全部35B权重,但计算量和激活值只按激活参数算。

这对端侧是把双刃剑。好处是计算量小、速度快、发热低;坏处是权重还是得全放内存里,内存墙没解决。所以MoE适合的是“内存够大但算力有限”的设备,比如16GB内存的手机,权重压到3bit约13GB,勉强能放下,然后靠稀疏激活把速度拉起来。

5.2 激活参数与总参数的取舍

选MoE模型时,关键看两个数字:总参数决定内存占用,激活参数决定计算量和速度。端侧理想的比例是激活参数占总参数的10%到20%。比如35B总参数、4B激活参数,那计算量就按4B算,速度接近4B稠密模型,但知识容量接近35B。

我对比过稠密35B和MoE 35B(激活4B)在手机上的表现:稠密模型根本加载不进去,MoE模型在3bit量化下能加载,生成速度约8到12 token每秒,可以接受。代价是首次加载慢,因为要读13GB的权重文件。

5.3 结构层面的其他省内存设计

除了MoE和GQA,还有几个结构设计对端侧友好:

  • 深窄结构 vs 浅宽结构:层数多、隐藏维度小的模型,KV缓存更小,但推理延迟可能更高。端侧通常偏好适中深度。
  • 共享注意力层:部分层共享KV,直接减少缓存。
  • 分组查询注意力的极端形式MQA:所有头共享一组KV,缓存最小,但质量损失比GQA大。
  • 线性注意力/状态空间模型:用固定大小的状态替代KV缓存,理论上内存不随上下文增长,是端侧的潜力方向,但目前生态还不成熟。

这些结构选择在模型选型阶段就决定了,部署阶段改不了。所以端侧部署的第一步其实是选对模型,选一个结构上就为端侧设计的模型,比事后拼命量化有效得多。

6. 端侧推理框架与实操落地

6.1 框架选型:llama.cpp、MLC、ONNX Runtime怎么选

端侧推理框架的选择直接决定能不能跑起来。几个主流选项:

框架优势劣势适用
llama.cppGGUF生态成熟,量化档位全移动端集成需自己封装快速验证、桌面
MLC-LLM编译优化好,移动端支持强编译流程复杂手机原生部署
ONNX Runtime跨平台,INT8量化成熟大模型支持一般传统模型、边缘
ExecuTorch与PyTorch生态衔接较新,生态在建PyTorch用户

我的经验是:验证阶段用llama.cpp最快,命令行就能跑,量化工具齐全;真要上手机用MLC-LLM或ExecuTorch,因为它们对移动端GPU/NPU的调度更好。ONNX Runtime适合已经有一套ONNX流水线的团队,但35B这个量级在ONNX上部署会比较折腾。

6.2 手机端部署的完整流程

以MLC-LLM为例,走一遍完整流程:

# 1. 环境准备,安装MLC-LLM pip install mlc-llm-nightly mlc-ai-nightly # 2. 编译模型为移动端可用的库 mlc_llm compile ./model-config --device iphone --output ./model-lib # 3. 量化(MLC支持q4f16等档位) mlc_llm convert_weight ./model-fp16 --quantization q4f16_1 --output ./model-q4 # 4. 打包为手机App可加载的格式 mlc_llm package ./model-q4 --output ./model-package

然后在手机端App里加载这个package,配置推理参数。关键参数包括:最大上下文、KV精度、线程数、是否用GPU。线程数不要拉满,手机CPU核心少,拉满反而因为调度开销变慢,一般用4到6个线程。

6.3 性能调优:速度、内存、发热的三角平衡

端侧调优是个三角平衡:要速度就得吃内存和功耗,要省内存就得降精度,要控发热就得限速。我的调优顺序是:

  1. 先定内存上限,根据设备可用内存反推能接受的量化档位和上下文长度。
  2. 再测速度,在内存约束下找最快的配置,通常是调线程数和是否启用GPU加速。
  3. 最后压发热,如果持续推理导致降频,就限制最大生成长度或加冷却间隔。

实测数据供参考:某旗舰手机16GB内存,跑3bit量化的35B MoE模型(激活4B),4线程CPU推理,生成速度约6到10 token每秒,持续推理5分钟后因发热降到4到6 token每秒。这个速度做对话够用,做批量生成就吃力。

注意:手机端推理一定要做内存水位监控,系统在内存紧张时会杀进程,模型加载到一半被杀会导致崩溃。建议在加载前检查可用内存,留出至少2GB余量。

7. 常见问题与排查实录

7.1 加载失败、OOM、速度慢的排查路径

端侧部署最常遇到的三个问题,排查思路如下:

加载失败:先看模型文件是否完整(校验哈希),再看格式是否匹配框架版本。GGUF格式有版本差异,老框架读不了新格式。我遇到过一次加载失败,折腾半天发现是量化时用的工具版本和推理框架版本不匹配,统一版本后解决。

OOM(内存不足):用系统工具监控加载过程中的内存峰值。常见原因是KV缓存按最大上下文预分配了,实际用不到那么长。解决办法是把最大上下文调小,或者启用KV缓存的动态分配。另一个原因是框架的默认批大小太大,端侧要显式设为1。

速度慢:先确认是不是走了CPU而非GPU/NPU。然后看线程数是否合理,太多太少都慢。再看是不是触发了内存交换(swap),一旦开始swap,速度会断崖式下跌。最后看量化档位,2bit虽然省内存,但反量化开销可能让速度变慢,不一定比4bit快。

7.2 量化后质量下降的定位方法

质量下降要定位到具体环节。我的方法是分层排查:

  1. 先用同一批prompt对比FP16和量化版本的输出,确认下降幅度。
  2. 如果下降明显,换更细的量化粒度(group size从128降到64)或换算法(从RTN换到AWQ)。
  3. 如果还不行,把敏感层(首层、末层、注意力输出)单独提精度。
  4. 最后考虑换量化档位,从Q4_K_M升到Q5_K_M。

这里有个容易忽略的点:校准集和测试集要分开。我见过有人用测试集当校准集,结果量化后测试指标虚高,实际使用一塌糊涂。

7.3 端侧部署避坑速查表

问题现象可能原因解决方向
加载到一半崩溃内存不足被杀降量化档位、减上下文
生成重复循环量化精度过低升档位、KV用INT8
速度突然变慢触发swap或降频减内存占用、加冷却
输出逻辑断裂3bit以下量化换4bit、敏感层提精度
首次加载极慢闪存读取慢模型分片、预加载
发热严重持续满负荷限速、限长度、间歇推理

7.4 几个我踩过的坑

第一个坑是盲目追求低比特。一开始我总想把模型压到2bit,觉得越省内存越好,结果质量崩得没法用,返工重做4bit,浪费了两天。后来明白,量化档位要服务于场景,不是越低越好。

第二个坑是忽略KV缓存。有次权重压到4bit只有20GB,觉得16GB手机肯定不行,差点放弃。后来发现把上下文从32K降到4K、KV用INT8,总占用降到14GB以内,居然能跑了。KV缓存的优化空间比想象中大。

第三个坑是没做内存水位监控。模型跑起来看着正常,但系统后台一更新就把它杀了,用户体验极差。后来加了内存监控和优雅降级,内存紧张时自动缩短上下文,稳定多了。

8. 端侧大模型的现实边界与我的实践体会

把35B模型塞进手机,技术上可行,但有明确的边界。内存是硬约束,量化是主要手段,KV缓存是隐藏成本,MoE是结构红利。这四句话基本概括了端侧部署的全部逻辑。你不可能既要35B的完整能力,又要手机的内存和功耗,必须做取舍。

我个人的实践体会是:端侧部署的成功率,八成取决于模型选型,两成取决于调优。选一个结构上为端侧设计的模型(GQA、MoE、适中深度),比拿一个云端大模型硬压要省力得多。调优阶段,先把内存算清楚,再谈速度,最后才考虑发热,顺序不能乱。

最后分享一个实用技巧:端侧部署一定要做降级预案。内存不够时自动切更小的量化档位,上下文超长时自动滚动截断,发热过高时自动限速。用户不会关心你用了什么技术,他们只关心“能不能用、卡不卡、烫不烫”。把这些边界处理好了,端侧大模型才真正算落地。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询