小米端侧大模型部署:LLM剪枝与量化实战指南
发布时间:2026/9/30 4:49:21 作者:尧图编辑部 阅读量:1,286

简介这份PDF资料聚焦小米大模型端侧部署的落地探索面向大模型算法工程师、端侧AI开发者及对轻量化部署感兴趣的技术人员系统梳理了端侧AI的重要性、LLM端侧部署挑战与相关技术路径。内容涵盖可靠性、隐私安全、个性化服务与成本效益四大端侧优势并对比云端与端侧在算力、内存、功耗、带宽上的差异深入讲解剪枝、量化、Sheared LLaMA、TransAct等模型压缩与推理加速方案以及模型分片、内存瓶颈优化等部署实践。资源包内含1个PDF文件大小约4.23MB结构完整、图文并茂适合作为端侧大模型技术调研与方案设计的参考材料。目前已有144人学习可帮助读者快速建立端侧部署知识框架理解小米在轻量化、本地化方向上的技术定位与突破思路。1. 端侧大模型部署为什么小米把「轻量化」当成主力突破方向去年底我拿到这份《小米大模型端侧部署落地探索》的 PDF第一反应不是「又一个发布会材料」而是翻到第 6 页看那张云端 VS 端侧的算力对比表——A100 显存带宽接近 1.6TB/s手机 NPU 大约 70G/s差了二十多倍。这个数字基本决定了端侧 LLM 部署的所有技术路线你不可能靠堆硬件解决只能从模型结构、数值精度、内存搬运三个方向抠。端侧 AI 说白了就是在手机、音箱、车机这类终端上直接跑推理不依赖网络回传。它解决的是四件事断网可用、数据不出本地、按用户习惯做个性化、大规模铺开时省云端算力成本。小米做这件事的底气是设备保有量大软件乘硬件的组合能把轻量化和本地部署真正推到量产。这份材料适合两类人看一类是想搞清楚端侧 LLM 到底卡在哪的算法工程师一类是准备把大模型往终端搬、需要一份技术选型参照的部署工程师。下面我按「挑战在哪 → 剪枝怎么做 → 量化怎么落 → 避坑 → 进阶验证」的顺序把这份材料拆成能照着复现的笔记。2. 端侧部署的硬约束算力、内存、带宽三座大山怎么量化2.1 云端和端侧的差距不是「小一点」是数量级材料里那张对比表值得逐行拆。云端服务器用 A100 这类卡算力数百 TFLOPS显存大容量且带宽接近 1.6TB/s功耗设计本来就是给高功耗环境用的散热要求高。端侧这边手机算力相对低内存和存储通常几个 GB 到十几 GBNPU 带宽约 70G/s还是低功耗设计。真正要命的是内存瓶颈。材料里给了一个很具体的账6B 模型FP16 权重就要 12GB 左右而手机内存总共约 16G。也就是说光把权重塞进去系统和其他 App 就没剩多少了KV cache 还没算。这就是为什么端侧部署第一步永远是「把模型变小」而不是「把推理写快」。推理速度这块材料给了一个很接地气的锚点母语读者平均阅读速度 300 到 500 字每分钟约 50 字每秒快速阅读者能到 500 到 700 字每分钟约 100 字每秒。端侧推理如果不做优化20 tokens/s 以内。这个对比的意思是——用户对「生成速度」的容忍度其实参照的是自己的阅读速度你跑到 50 tokens/s 以上体验就接近「读得比生成快」不会觉得卡。所以优化目标不是无限快而是先跨过这条体验线。2.2 推理时延拆成两项优化才有抓手材料把推理时延拆成推理时延 计算时间 数据搬运时间。这个拆法很关键因为它直接对应两类手段减少计算量剪枝、量化减小数据搬运剪枝、量化、投机推理注意剪枝和量化同时出现在两栏里说明它们既省算力又省带宽。而投机推理主要省的是「搬运」这一侧的时间——用小模型快速起草、大模型并行验证把串行的搬运摊薄。这个公式是我看这份材料时觉得最实用的一句话因为它把「玄学调优」变成了两个可以分别测量的量你先 profile 一下到底是算得慢还是搬得慢再决定往哪个方向使劲。2.3 一个可复现的显存与带宽估算脚本在动手剪枝量化之前我习惯先算一遍理论下限避免做完发现还是塞不进去。下面这段脚本按材料给的参数口径估算权重显存、KV cache 和理论搬运时间参数都可以改。# 端侧 LLM 显存与带宽粗算参数按材料口径可调 def estimate(model_params_b, bits, seq_len, batch, n_layers, n_kv_heads, head_dim, mem_gb, bw_gbs): # 权重显存参数量 × 每参数字节数 bytes_per_param bits / 8 weight_gb model_params_b * 1e9 * bytes_per_param / (1024**3) # KV cache2(K和V) × 层数 × batch × 序列长 × kv头数 × 头维度 × 2字节(fp16) kv_gb (2 * n_layers * batch * seq_len * n_kv_heads * head_dim * 2) / (1024**3) total_gb weight_gb kv_gb # 理论搬运时间把权重从内存搬到计算单元按 NPU 带宽估 move_ms weight_gb * 1024 / bw_gbs * 1000 print(f权重显存: {weight_gb:.2f} GB) print(fKV cache: {kv_gb:.2f} GB) print(f合计: {total_gb:.2f} GB / 可用 {mem_gb} GB f- {塞得下 if total_gb mem_gb else 塞不下继续压}) print(f单次权重搬运理论下限: {move_ms:.1f} ms) # 6B 模型FP16序列 2048batch 132 层8 个 KV 头头维度 128 estimate(6, 16, 2048, 1, 32, 8, 128, mem_gb16, bw_gbs70)逻辑说明bits控制量化位宽改成 4 就是 w4 场景n_kv_heads和head_dim决定 KV cache 大小这也是后面 TransAct 那类结构剪枝要动的地方。参数说明mem_gb填设备可用内存bw_gbs填 NPU 带宽。跑一遍你会看到 FP16 的 6B 模型权重就 11GB 出头加上 KV cache 直接顶到 16G 上限结论很明确——不量化不剪枝端侧没戏。这个脚本我一般放在项目最开始跑一次作为「必须压到多少」的硬指标。3. LLM 剪枝结构化剪枝为什么是硬件最友好的那条路3.1 三种剪枝的取舍先看硬件支持材料把剪枝分成非结构化、结构化、半结构化三类并直接给了一句结论结构化剪枝目前硬件支持最友好。这句话背后是血泪经验——非结构化剪枝虽然稀疏率高、理论压缩比好看但产生的是不规则稀疏通用 NPU 和移动 GPU 很难真正加速经常是「模型小了但没变快」。半结构化比如 2:4需要特定硬件支持端侧不一定有。结构化剪枝直接剪掉整个层、整个 head、整个维度剪完还是规整的稠密矩阵硬件能实打实加速。结构化剪枝具体剪什么材料列了剪层、剪 Head、剪维度。剪层是砍掉整个 Transformer block剪 Head 是减少注意力头数剪维度是缩 hidden dim。这三种对 KV cache 的影响完全不同这也是后面选型的分水岭。3.2 Sheared LLaMA 的启发和它的短板材料引了 Sheared LLaMAICLR24它的做法是同时剪深度层数和 hidden dim。剪枝的校准目标是加 mask 训练模型优化「一般损失 mask 稀疏度损失」让模型在剪枝过程中自己学出哪些结构可以丢。材料给了一个很重要的结论剪枝 少量恢复训练能超越相同大小的预训练模型。这句话是剪枝这件事「有意义」的根基——否则你剪完还不如直接训个小模型那剪枝就白做了。但材料对 Sheared LLaMA 的评价很直接损失较大KV cache 压缩不足。原因在于它剪 hidden dim 时对 KV cache 的压缩不够狠。KV cache 大小取决于层数、KV 头数、头维度你光剪 hidden dim 不一定等比缩 KV。端侧长上下文场景下KV cache 经常是压垮内存的最后一根稻草所以这个短板在端侧是致命的。3.3 TransAct保留深度、缩模块内激活维度材料重点讲的是他们自己的 TransActACL2024结构设计有三个特点保留深度和 hidden dim减小 MHA 和 MLP 模块内的激活维度参数量相近时KV cache 显著减小这个思路和 Sheared LLaMA 正好相反不动深度和 hidden dim专挑模块内部的激活维度下手。好处是 KV cache 能实打实降下来因为 MHA 内部的激活维度直接关联 KV 的投影维度。材料还给了剪枝后的计算量和端到端时延w4a16对比说明这套结构剪枝配合 4bit 权重、16bit 激活端到端时延有可测量的下降。3.4 一个结构化剪枝的 mask 校准骨架下面这段代码演示结构化剪枝里「加 mask 稀疏度损失」的校准循环骨架思路对齐材料里 Sheared LLaMA 的校准目标具体层结构按你的模型替换。import torch import torch.nn as nn class MaskedLinear(nn.Module): 带可学习 mask 的线性层mask 趋近 0 的维度可被剪掉 def __init__(self, in_features, out_features): super().__init__() self.weight nn.Parameter(torch.randn(out_features, in_features)) self.bias nn.Parameter(torch.zeros(out_features)) # mask 用 sigmoid 参数化保证在 (0,1) 区间 self.mask_logit nn.Parameter(torch.zeros(out_features, 1)) def forward(self, x): mask torch.sigmoid(self.mask_logit) return x (self.weight * mask).T self.bias def sparsity_loss(model, target0.5): 鼓励 mask 稀疏让平均 mask 值靠近 target total, count 0.0, 0 for m in model.modules(): if isinstance(m, MaskedLinear): mask torch.sigmoid(m.mask_logit) total total mask.mean() count 1 return abs(total / max(count, 1) - target) # 校准循环一般损失 稀疏度损失 def calibrate(model, dataloader, steps1000, lam0.1): opt torch.optim.AdamW(model.parameters(), lr1e-4) for step, batch in enumerate(dataloader): if step steps: break out model(batch[input_ids]) task_loss nn.functional.cross_entropy( out.logits.view(-1, out.logits.size(-1)), batch[labels].view(-1)) loss task_loss lam * sparsity_loss(model) opt.zero_grad() loss.backward() opt.step() if step % 100 0: print(fstep {step} task {task_loss.item():.3f} fsparsity {sparsity_loss(model).item():.3f})逻辑说明MaskedLinear把每个输出维度乘一个可学习 mask训练中 mask 会向 0 或 1 分化趋近 0 的维度就是可以剪掉的。sparsity_loss控制整体稀疏程度lam是稀疏度损失权重。参数说明target是期望的平均 mask 值越小剪得越狠lam太大会伤任务性能我一般从 0.05 到 0.1 起步。跑完校准后把 mask 低于阈值的维度真正物理剪掉再做少量恢复训练——这一步对应材料说的「剪枝 少量恢复训练超越同尺寸预训练模型」。注意恢复训练的数据量不用大但质量要高否则剪完的模型会明显掉点。4. LLM 量化把浮点转定点w4a16 在端侧怎么落地4.1 量化的本质和端侧收益材料对量化的定义很干脆在深度学习领域量化是将浮点数值转化为定点数值的方法。落到端侧收益有两块——模型体积变小权重从 FP16 的 2 字节降到 4bit 的 0.5 字节6B 模型从 12GB 降到约 3GB以及定点运算在 NPU 上通常比浮点快、功耗低。材料里剪枝效果对比用的是 w4a16也就是权重 4bit、激活 16bit。这个组合在端侧很常见因为权重占内存大头压权重收益最大激活保持 16bit 是为了不把精度伤太狠毕竟激活对数值误差更敏感。你如果一上来就 w4a4大概率精度崩掉得不偿失。4.2 量化不是「转完就完」校准集决定成败量化最容易被低估的是校准。权重量化分对称和非对称激活量化要处理离群值outlier这些都需要校准集来统计数值分布。校准集选得不好量化后的模型在特定任务上会突然掉点而且这种掉点在通用评测上看不出来上线才翻车。我一般会这么做校准集从真实业务分布里采样覆盖长短输入、中英文、代码和普通文本不要只用维基百科那种干净语料。校准样本数几百条通常够用太多收益递减。量化完必须做端到端评测不只看 perplexity还要看具体任务的准确率。4.3 一个权重量化的最小实现下面这段代码演示对称权重量化的核心逻辑把 FP16 权重映射到 4bit 整数再反量化方便你理解量化误差从哪来。import torch def quantize_weight_symmetric(w, bits4): 对称权重量化把浮点权重映射到 [-2^(bits-1), 2^(bits-1)-1] qmax 2 ** (bits - 1) - 1 # 每个输出通道单独算 scale避免全局 scale 被大值主导 scale w.abs().amax(dim1, keepdimTrue) / qmax scale scale.clamp(min1e-8) # 防止除零 q torch.round(w / scale).clamp(-qmax - 1, qmax) return q.to(torch.int8), scale def dequantize(q, scale): 反量化回浮点用于验证误差 return q.float() * scale # 模拟一层权重 w torch.randn(4096, 4096) * 0.02 q, scale quantize_weight_symmetric(w, bits4) w_hat dequantize(q, scale) err (w - w_hat).abs().mean().item() print(f4bit 量化平均绝对误差: {err:.6f}) print(f原始显存: {w.numel()*2/1024**2:.1f} MB, f量化后: {q.numel()*1/1024**2:.1f} MB (int8 存储))逻辑说明quantize_weight_symmetric按输出通道算 scale这是 per-channel 量化比 per-tensor 精度好很多代价只是多存一点 scale。clamp防止除零。参数说明bits改成 8 就是 w8改成 4 就是 w4dim1表示按输出通道如果你的权重布局不同要相应调整。跑完看平均绝对误差如果误差大得离谱通常是权重里有极端离群值需要先做离群值处理或者换非对称量化。注意这段只是演示原理真正部署要用成熟量化工具链手写量化容易在算子融合上出问题。4.4 剪枝和量化的叠加顺序材料把剪枝和量化并列但实际落地有顺序问题。我的经验是先剪枝再量化剪枝改变的是模型结构量化改变的是数值表示。你先量化再剪枝剪枝后的结构可能破坏量化时的 scale 统计得重新校准先剪枝再量化量化一次到位。而且剪枝后的模型更小量化校准也更快。这个顺序在材料里没明说但从它把剪枝放在量化前面讲能看出这个倾向。5. 端侧部署避坑五条我踩过的血泪记录5.1 只看参数量不看 KV cache长上下文直接 OOM现象模型权重明明塞得下一跑长上下文就内存溢出。原因KV cache 随序列长度线性增长6B 模型 32 层、8 个 KV 头、头维度 128序列 4096 时 KV cache 能到几个 GB权重之外还要留这份。解决用第 2 章那个估算脚本先算 KV cache长上下文场景优先选 KV cache 友好的结构比如 TransAct 那类缩模块内激活维度的或者上 KV cache 量化。5.2 非结构化剪枝剪完没变快现象稀疏率做到 70%模型文件小了一半但端侧推理速度几乎没变。原因非结构化剪枝产生不规则稀疏通用 NPU 没有对应的稀疏加速单元实际还是按稠密算。解决端侧优先结构化剪枝剪层、剪 Head、剪维度剪完是规整稠密矩阵硬件能真加速。材料那句「结构化剪枝目前硬件支持最友好」就是踩过这个坑才写的。5.3 量化校准集太干净上线掉点现象量化后通用评测 perplexity 几乎没掉但业务任务准确率掉了好几个点。原因校准集用的是干净通用语料没覆盖业务里的特殊分布长数字、代码、专有名词这些恰恰是量化误差最大的地方。解决校准集从真实业务分布采样覆盖长短输入和多语言多模态量化后必须跑端到端业务评测不能只看 perplexity。5.4 忽略数据搬运只优化计算现象算子换成更快的实现计算时间降了但端到端时延没怎么动。原因材料那个公式——推理时延 计算时间 数据搬运时间。端侧带宽只有约 70G/s权重搬运经常是瓶颈你优化计算但搬运没变总时间自然不动。解决先 profile 分清是算得慢还是搬得慢搬得慢就上量化减小搬运量和投机推理摊薄串行搬运别一头扎进算子优化。5.5 恢复训练数据量堆太大反而过拟合现象剪枝后做恢复训练数据越多越久验证集反而变差。原因剪枝后的模型容量变小大量恢复训练容易过拟合到恢复数据上而且材料说的「少量恢复训练」重点在少量。解决恢复训练数据质量优先量控制在能恢复性能即可配合早停验证集掉点就停。别把恢复训练当成重新预训练。6. 进阶验证怎么确认你的端侧部署真的达标前面讲的是怎么压、怎么剪、怎么量化这一章讲怎么验证——因为端侧部署最容易自欺欺人的地方就是「实验室跑通」和「真机达标」之间的差距。我一般会走三层验证缺一层都不敢说落地。第一层是理论下限验证用第 2 章那个估算脚本确认权重加 KV cache 在目标设备可用内存内且单次权重搬运理论下限低于你的时延预算。这一层不过后面都白搭。第二层是数值精度验证剪枝和量化后各跑一遍端到端评测。这里有个具体技巧不要只看平均指标要看分桶指标。把测试集按输入长度、语言、任务类型分桶看哪个桶掉点最狠。量化误差往往集中在长尾分布上平均指标会把它平均掉。我一般会做一张这样的对比表验证项剪枝前剪枝后量化后(w4a16)达标线权重显存12GB8GB3GB 6GBKV cache(4K)4GB2.5GB2.5GB 3GB短输入准确率基准-0.5%-1.2% -2%长输入准确率基准-1.0%-2.5% -3%端到端时延基准-20%-45%跨过 50 tokens/s第三层是真机验证在目标设备上跑真实场景重点看三件事峰值内存有没有触顶、长时间运行的功耗和发热、以及连续多轮对话下 KV cache 会不会累积到 OOM。实验室用单轮短输入测出来的时延和真机多轮长上下文完全是两回事。这里有个我踩过的坑值得单独说真机验证一定要用和量产一致的推理框架和算子库。我见过实验室用 PyTorch 跑得好好的换成端侧推理框架后因为某个算子没做量化融合时延直接翻倍。所以第三层验证的框架必须和最终部署一致否则测了个寂寞。最后给一个我自己的习惯每次端侧部署上线前我都会强制走一遍「估算 → 分桶评测 → 真机多轮」这三层任何一层不过就不发版。这套流程帮我拦下过好几次「实验室达标、真机翻车」的事故。端侧部署没有后悔药压模型的时候多花一天验证比上线后回滚省心得多。希望这份拆解帮到你把这份材料里的剪枝量化思路真正落到自己的端侧项目上。本文还有配套的精品资源点击获取