把一个动辄数十亿参数的大模型塞进手机、车机或边缘盒子里,最先撞上的不是算力墙,而是内存墙。理解端侧推理,本质上是理解"内存放不下、带宽喂不饱"这两件事如何主导一切工程决策。
直觉:为什么端侧第一约束是内存而不是算力
先算一笔账。一个 7B(70 亿参数)模型,如果用 FP16 存权重,光权重就是:
这还没算激活值和 KV Cache。而主流旗舰手机的可用 RAM 也就 8~16GB,且要和操作系统、其他 App 抢。所以端侧的第一道关卡是:权重根本放不进去。
第二个关键事实:自回归生成(decode 阶段)是**访存密集(memory-bound)**而非计算密集。每生成一个 token,都要把全部权重从内存读进计算单元过一遍,但实际做的矩阵-向量乘法计算量很小。于是单 token 的延迟近似为:
t_{\text{token}} \approx \frac{\text{模型字节数}}{\text{内存带宽}}7B 模型 FP16 在带宽 ~50GB/s 的移动平台上,理论下限就是 s/token,也就是每秒三四个字——慢到不可用。这条公式解释了端侧优化的两条主线:把模型变小(量化),以及把重复访存的中间结果缓存住(KV Cache)。
机制一:量化——用更少的比特表示权重
量化的核心是把高精度浮点权重映射到低比特整数。最常见的是对称/非对称的均匀量化:
1 | # 对一组权重做 per-channel 量化到 int4/int8 |
关键工程点:
- 粒度(granularity):per-tensor 一个 scale 最省,但精度差;per-channel、乃至 group-wise(每 64/128 个权重一组共享 scale)是精度与开销的平衡点。group size 越小越准,但 scale 的存储开销越大。
- 离群值(outliers)是量化的头号敌人。Transformer 的某些 channel 激活值会异常大,一个离群值就会把整组的 scale 拉大,导致其余权重量化分辨率坍塌。这正是 GPTQ、AWQ 这类方法存在的原因:GPTQ 用二阶(Hessian)信息逐列补偿量化误差;AWQ 则观察到"重要权重由激活幅度决定",对重要通道做保护性缩放。它们都属于训练后量化(PTQ),不需要重训。
把 7B 从 FP16 降到 4-bit,权重从 14GB 压到约 3.5GB,再叠加 group scale 的少量开销,落在 4GB 上下——这才进入端侧可行区间。代价是约 1% 量级以内的质量损失(视方法和任务而定)。
一个常见误区:量化不是简单"砍精度",端侧推理通常仍用更高精度做累加。典型做法是 W4A16(权重 int4、激活 fp16)或 W8A8。纯整数推理(INT8 全链路)能吃满 NPU 的整数算力,但激活量化更难,质量更敏感。
机制二:KV Cache——拿内存换计算
Transformer 每层注意力需要历史所有 token 的 Key、Value。若不缓存,生成第 个 token 要重算前 个的 K/V,总计算量是 的重复劳动。KV Cache 把每个 token 算过的 K/V 存下来,使 decode 阶段每步只算当前 token,复杂度降到 。
但缓存本身要吃内存。其大小为:
\text{KV bytes} = 2 \times L \times n_{\text{kv\_heads}} \times d_{\text{head}} \times S \times b其中 是层数, 是序列长度, 是每元素字节数,因子 2 是 K 和 V。注意它随上下文长度线性增长——长上下文场景下,KV Cache 可能比权重还大。这就是为什么端侧支持"长文档对话"特别难。
工程上的三板斧:
- GQA(Grouped-Query Attention):多个 Query head 共享一组 KV head,直接把上式的 n_{\text{kv\_heads}} 砍掉数倍,KV Cache 等比缩小。现代开源模型几乎都默认 GQA。
- KV Cache 量化:把缓存里的 K/V 也压到 int8/int4。V 通常比 K 更耐压。
- 滑动窗口 / 驱逐策略:只保留最近 个 token 加少量"注意力汇聚"token,把内存从 压成 ,代价是远距离信息丢失。
工程权衡:prefill 与 decode 是两种负载
端侧推理要分清两个阶段,它们的瓶颈完全相反:
1 | Prefill(处理 prompt) : 一次性并行处理 N 个 token → 计算密集,吃 TOPS |
- 优化首 token 延迟(TTFT)要看 prefill 算力;优化生成速度(tokens/s)要看带宽与模型大小。
- 这也解释了为何端侧偏爱 NPU:NPU 对低比特整数(INT4/INT8)的算力和能效远高于 CPU/GPU,正好匹配量化模型。但 NPU 对动态 shape、稀疏路由(如 MoE)支持往往受限,模型结构选型要迁就硬件。
再补两个常见踩坑:
- 省了权重内存,激活和 KV 仍可能爆。batch=1 时激活不大,但长上下文下 KV Cache 是隐形杀手,估算内存预算必须把它算进去。
- 量化掉点不均匀。困惑度(PPL)几乎不变,不代表代码生成、数学推理这类长链路任务不崩。端侧验收一定要用贴近真实任务的评测,而非只看 PPL。
小结
端侧大模型是一道"在内存墙下做约束优化"的题。量化压住权重的静态体积,GQA 与 KV Cache 管理压住随上下文增长的动态体积,prefill/decode 的负载差异决定了你该堆算力还是堆带宽。把这三件事的字节数和带宽数算清楚,端侧能不能跑、跑多快,基本就有答案了。