模型压缩十年实战:量化、剪枝、蒸馏与低秩分解的选型指南

发布时间:2026/10/18 13:39:15
模型压缩十年实战:量化、剪枝、蒸馏与低秩分解的选型指南
2015年夏天我第一次接触“模型压缩”这个概念时手头是一台内存只有512MB的终端设备。当时深度学习的参数规模还远不如今天夸张但已经足够让一个像样的视觉模型“装不进去”。我花了很多个晚上手工砍通道、试低比特权重最终把模型压到能跑代价是精度掉了将近三个点。那个项目给我留下的最大教训是压缩不是“碰运气”而是一套需要体系化决策的工程方法。从2015到2025模型压缩走过了整整十年。它从少数论文里的技巧变成了部署大模型、端侧模型、边缘设备时绕不开的“标配工序”。这篇内容不是教科书式的综述更像是我带着这十年的实际经历把主线拆开量化、剪枝、蒸馏、低秩分解它们为什么存在、怎么选型、怎么排布以及我在项目里踩过的那些坑。如果你正要为一个容量或时延受限的场景做模型瘦身这套判断框架应该能帮你少走不少弯路。1. 十年演进的主线从手工瘦身到自动化改造1.1 第一波规则方法当道2015—2017这阶段我习惯叫它“规则方法当道”。深度学习刚过了爆发期大家发现模型越大效果越好但硬件和网络场景跟不上。于是出现了很多“硬核”压缩手段二值化、三值化、哈希参数共享、低秩近似、剪掉小权重。思路很直接用数学或启发式规则把“显然是冗余的东西”干掉。为什么2015到2017年集中出现规则方法因为那时的部署平台远没有今天智能算子融合、自动调优都还停留在编译器层面。模型容量就是硬约束把大网络改造成小网络是唯一的出路。但我得说这类方法最大的问题是精度不可控稍一用力就崩。我当年做二值化实验时网络几乎只能保住六成精度后来才知道问题不在“量化这个主意”而在于没有做训练时感知量化纯粹靠事后截断信息丢得太多。这个阶段的另一个特点是很多人都把目光放在“减少参数量”上却忽略了真实运行时的内存搬运。一个在纸面上算起来很省的二值网络如果底层算子不支持位运算反而可能比普通FP32更慢。所以那几年的经验总结下来就一句话压缩方案必须绑定部署硬件一起看脱离了目标平台的压缩都是自嗨。1.2 第二波结构化改造与蒸馏成熟2018—20202018年以后硬件平台和编译生态开始支持更多算子非结构化剪枝虽然减少计算量但在通用矩阵乘法上收益有限。于是“结构化剪枝”成为主流因为它改变了张量的真实形状能直接利用底层高效算子。同一时期知识蒸馏大规模出现在各类任务里从图像分类扩展到检测、分割、语音。为什么蒸馏会在那个节点火起来因为“大模型作为教师”这个设定缓解了压缩后模型的收敛问题。你直接拿一个小网络从零开始训练往往很难逼近大模型的天花板但如果你让大模型把已经学会的规律“翻译”成软标签喂给学生小模型的训练起点和信号质量都会高很多。这相当于有人给你画好了重点你只需要背熟而不是自己从头划重点。当时很多团队把“压缩”和“自动化”绑在一起用结构搜索或强化学习寻找每一层的剪枝比例。听起来很性感但实际落地成本很高中小团队往往还是要用手工经验加规则。另一个重要变化是压缩评估从只看“指标”走向看“算子融合后的真实时延”。如果只盯着FLOPs不看数据搬运和算子调度剪完可能比原来更慢。我见过好几个项目都栽在这上面剪枝后理论计算量降了一半实测时延纹丝不动最后发现瓶颈在内存访问而不是计算。1.3 第三波大模型与全链路压缩2021—2025到了2021年以后大语言模型把压缩的“尺寸”拉到几百亿参数。传统手工规则方法又回来了但形式更复杂分组量化、KV Cache量化、稀疏化与蒸馏共存量化位宽甚至从INT8一路压到4bit、3bit。为什么旧方法会被再次启用因为在大模型场景里冗余量的绝对规模比小模型大得多一个简单的4bit量化就能省掉几十GB显存而且大模型推理的瓶颈往往在内存带宽而不是计算本身。所以压缩的核心目标发生了变化从“塞进终端”变成“让同一块GPU服务更多人”。这个阶段也把“全链路压缩”这个词推到了前台。你不仅要在权重上做量化剪枝还要管推理中间状态、Key-Value缓存、激活值、甚至是投机采样的草稿模型。十年时间压缩的对象从“网络参数”扩展到了“整个推理过程中的一切存储”。但往回看核心原理并没有变还是量化、剪枝、蒸馏、结构改造这四门手艺只是在新规模下重新排列组合。2. 四大核心方法的原理拆解以及为什么它们有效2.1 量化用“精度预算”换体积与速度校准才是关键量化本质上是把连续浮点映射到离散定点。你可以想象成把一张16位深度的照片压成8位如果色调渐变很平滑肉眼几乎看不出区别但如果有一小块区域亮度跨度很大就会看到明显的“色带”。模型的权重和激活也一样大部分数值都集中在一个相对狭窄的区间少数极端值则会把整个分布拉坏。量化的基本公式是 r s × (q − z)其中 r 是原始浮点值q 是量化后的整数s 是缩放因子z 是零点。s 决定精度步长z 保证零点在整数域里也有精确表示。为什么INT8是过去十年最常见的位宽因为在多数硬件上INT8都有原生加速指令而且动态范围足够覆盖权重和激活的常见分布。那为什么还会出现4bit量化因为大模型权重分布很宽有异常值INT8省下的显存不够“解渴”4bit配合分组量化反而能在精度和容量之间找到新平衡。校准范围是量化最容易被忽略的坑。MinMax校准最简单但一个极端权重就能让整个缩放因子变大导致普通数值的精度被压缩。更稳的做法是用KL散度或百分位保留分布形状而不是死盯最大值。权重一般按输出通道做per-channel量化因为不同通道的范围可能差出数量级激活则更难办因为它是动态变化的必须在部署前用一组校准集去统计激活分布并通过量化感知训练把误差吸收进模型本身。我自己的经验是不要一上来就全模型INT8先只量化权重保持激活FP16观察掉点来源再逐步收紧。2.2 剪枝删除冗余更要躲开“悬崖点”剪枝分两类。非结构化剪枝是把小的权重直接置零网络变得稀疏但通用硬件不擅长稀疏矩阵收益要看部署平台是否做了稀疏加速。结构化剪枝则是整行、整列或整个通道去掉直接缩小矩阵收益明确也是多数端侧场景的首选。按绝对值删权重听起来很自然但实际并不总对。权重的绝对值只代表单个连接的大小不代表信息重要度。更稳的做法是看“去掉某层通道后损失的变化”这就要做敏感度分析。方法不复杂对每一层依次扫剪枝比例10%、20%、30%画出精度曲线。你会发现有的层剪到60%还稳有的层剪10%就崩。这就像拆墙承重墙和隔断墙看着都是墙但动它们的结果完全不同。剪枝还有一个反直觉的现象不是越小的权重越可删。某些层虽然权重大但它和后续层组合起来才是重要结构。我见过有人按全局最小权重删了30%正确率直接跳崖改成逐层敏感度后剪40%精度反而还在线。这就是“全局幅度掩码”的经典陷阱。另外剪枝完成后必须恢复训练相当于外科手术后的康复期不康复直接上场大概率出事。恢复训练时学习率要低通常从原学习率的十分之一起步并且用接近原训练分布的增强策略重新读数据。2.3 蒸馏让大模型当老师输出不只是答案蒸馏最核心的点是教师模型输出的概率分布带有“软知识”。比如一张图是狗硬标签只告诉你“狗”但教师模型可能认为它更像狼而不是猫这层相似性对压缩模型而言是宝贵监督信号。公式是 p_i exp(z_i / T) / Σ exp(z_j / T)其中 T 是温度。温度越高分布越平滑类别间的关系越明显温度太低则接近独热编码蒸馏就失去了意义。有人会问既然数据还在为什么不直接拿原始训练集重新训练一个小模型因为容量小、数据有限时小模型很难学到类别之间的连续性。教师模型已经把这些信息编码在logits里蒸馏等于把大模型的判断抽象成更丰富的标签而不是只给一个正确答案。但要注意教师模型的预测误差会被学生放大如果某个特定类别教师本身判断很弱学生很容易学歪。这种情况可以降低蒸馏损失的权重或者用多教师平均来抹平单点噪声。常见的蒸馏损失是 L α × KL(学生, 教师) (1 − α) × CE(学生, 硬标签)。温度T默认从4左右起步太大噪声多太小几乎就是硬标签α取0.7左右兼顾教师软知识与真实标签。如果学生和教师的结构差异很大光靠输出层对齐常常不够可以加中间层特征蒸馏让学生的特征图去对齐教师某个层的内容。代价是实现复杂度更高还要考虑特征维度不一致的问题。2.4 矩阵分解与结构重参数化容易被忽略的隐性加速矩阵分解的思路是把一个大矩阵 W 变成两个小矩阵的乘积比如 U 和 V。如果 W 是 m×nU 是 m×kV 是 k×n当 k 远小于 m 和 n 时参数就少了。在早期CNN和全连接层上效果很好。但为什么现在用得少了因为现代编译器已经把卷积等效成矩阵乘法并且做了算子融合你硬拆出来的小矩阵可能会增加算子数量和内存搬运反而让推理更慢。低秩的适用范围逐渐转向超大矩阵比如一些超大模型的Embedding层和投影层。结构重参数化则是另一个方向训练时用复杂结构比如多分支、高分辨率卷积以获取更好的表达推理时把它们折叠成一个单卷积层。这看起来没有减少原始FLOPs但减少了运行时算子调度和数据搬运实测时延收益非常明显。我想特别提一句这类方法常常被当成“没有压缩”而不被重视实际在工程落地里它经常能与量化和剪枝叠加使用效果是乘法而不是加法。3. 实操流程从“想压缩”到“敢部署”3.1 先回答这三个问题再做任何压缩第一个问题你要压给谁是手机终端、边缘盒子还是云端GPU不同平台的算子和内存层次天差地别。第二个问题你能容忍多少精度损失如果是产品预训练特征0.5%也许就要命如果只是辅助任务2%也可能能接受。第三个问题你的瓶颈是容量还是时延这决定你要先上哪个工具。我习惯用一个简单的判断矩阵来启动项目场景类型首选方案次选方案原因存储容量受限量化、低秩分解结构化剪枝直接减小包体和模型体积时延敏感通道剪枝、重参数化蒸馏后量化改变张量形状后算子更快吞吐量成本优先蒸馏、量化稀疏化降低每请求计算量和带宽大模型显存受限分组量化、KV Cache优化蒸馏小型化重点是省显存、提并发把这三个问题想清楚能避免“做了半天压缩结果发现瓶颈不在模型”这种尴尬。我见过一个项目团队花了三周做量化最后定位到问题在预处理数据加载模型本身快得很。压缩这事方向比努力重要。3.2 一个模拟项目图像分类模型从138MB到62MB为了把流程讲透我拿一个实际做过的项目套路来拆解数字做了脱敏处理。原始模型是一个带残差结构的图像分类网络FP32权重完整包体约138MB部署在一台只支持部分算子融合的边缘设备上推理时延约45ms。目标是包体压到64MB以内时延降到20ms左右。第一步是做性能画像。用profiler逐层看时延和参数量发现前三组残差块时延占比接近一半但部分通道对精度贡献很小。于是决定先剪枝。第二步做敏感度分析对每个残差块分别扫10%、20%、30%、40%的剪枝率画出一条“剪枝率-精度”曲线。结果很典型浅层剪到20%就明显掉点深层可以剪到40%。第三步执行结构化通道剪枝把冗余通道去掉参数量从35M降到21M包体约84MB精度从92.1%掉到90.2%。第四步做INT8量化。校准集选了一千张分布比较合理的样本权重用per-channel激活用per-tensor。量化后包体约23MB加上一些非量化层和结构头整包约28MB已经远低于目标。精度掉到89.9%。第五步用原始模型当教师做蒸馏恢复温度T设为6蒸馏损失权重α设为0.7训练三个完整epoch精度回到91.4%时延降到19ms。最后把分类头保留FP16防止最后一层精度波动。最终交付时包体62MB时延19ms精度损失约0.7个百分点。这个项目里的具体数值会随任务变化但“先画像、再敏感度、再剪枝、再量化、再蒸馏”的流程是可复制的。我后来几乎每次接到压缩需求都会照着这个框架走一遍效率和成功率都提高了不少。3.3 “先剪后量”的顺序为什么更稳我在实际项目里基本遵循“先做结构变化再做数值压缩”的顺序。原因是剪枝会改变权重和激活的数值分布如果你先量化剪枝后原来的量化范围就失效了需要重新标定反过来先剪后量只需要在最终结构上做一次校准工作量更少。但这也不是铁律。如果目标平台要求整个计算图以固定INT8格式训练那就必须走量化感知训练这时候往往需要联合剪枝一边剪一边重新估计量化范围。还有一个细节是很多编译器的量化校准发生在计算图固定之后如果图结构频繁变化就需要反复校准浪费大量时间。所以除非有明确理由不然先剪后量是更省事的路径。我刚才那个模拟项目也是这个顺序先剪掉通道再量化最后蒸馏恢复。有些团队喜欢在剪枝前先蒸馏让原始模型先变成更好的教师也可以但收益通常比较有限。先剪后量、后蒸馏是一个成本收益很平衡的配方。3.4 再训练与蒸馏的默认配方说到配方我整理了一份常用的默认参数给团队新人当起点使用参数默认值说明学习率原学习率的0.1倍避免大改动破坏已有表达优化器与原训练保持一致尽量不换优化器batch size不变或减半影响恢复稳定性蒸馏温度 T4~8分类任务常见区间蒸馏权重 α0.5~0.9太高会过度相信教师训练epoch原训练的1/10~1/5压缩模型不需要从头练太久数据增强与原训练一致分布漂移会让蒸馏失效剪枝后的再训练要“先小步走再渐入佳境”。直接恢复到原学习率会震荡最好加一小段warmup比如前五百步线性从零升到目标学习率。蒸馏时数据分布要尽量均衡别只拿教师预测得很好的样本送进去不然学生会变成一个“复读机”在困难样本上毫无泛化能力。4. 踩坑记录问题现象、排查思路与填坑办法这十年里我踩过的坑比成功经验多得多其中四个问题出现频率极高我把排查思路和解决办法整理成速查表方便你直接照着做。4.1 现象一量化后精度崩了但模型明明没改坏很多人量化后第一反应是“搞错流程”其实第一步应该检查校准范围。MinMax校准被极端值污染是头号嫌疑改用百分位或KL散度校准往往能救回来。第二步检查权重是不是真的per-channel很多框架默认per-tensor而你的模型通道间范围差异可能非常大。第三步逐层排查找出“毒层”——也就是那些对量化噪声特别敏感的网络层可以把这些层单独保留FP16。我印象很深的一次量化事故是某个归一化算子在INT8下行为异常导致后面几十层全被带偏。后来我们把该层排除在量化范围外精度立刻恢复。所以别迷信全模型量化混合精度在工程里反而是常态。4.2 现象二剪枝后恢复训练始终不收敛这个现象通常和剪枝比例过大有关。剪枝相当于给网络做了一次大手术如果局部结构损坏太严重恢复训练就像让一个失去双腿的人立刻去跑马拉松不现实。解决办法是回退剪枝比例尤其是浅层多保留一些通道。第二把学习率降得更低甚至比原学习率的0.1倍还小。第三采用渐进式恢复先用较小学习率让模型“习惯”稀疏结构再逐渐升到目标值。还有一个我见过很多次的原因剪枝时只按局部幅度排序没有做全局通道选择。这种情况下某些重要信息通道可能被整条端掉再怎么训练也补不回来。解决方法是改用基于全局重要度的选择比如利用归一化层的缩放因子排序再用敏感度曲线做二次校验。4.3 现象三蒸馏loss稳定下降线上指标却纹丝不动这是最磨人的情况因为指标看起来一切正常损失函数也确实在降。第一个嫌疑是温度太高soft label被抹得太平学生学到的全是“废话”。我之前调试过一个任务温度设为20时学生输出几乎变成均匀分布指标当然不动。降回T4之后就正常了。第二个原因是学生容量太小教师的知识超出学生带宽这时候光调温度没用要改用中间层特征对齐或者用Hint Loss把学生的特征图往教师对应层上面拉。还有一种情况是教师在易错样本上犯了系统性错误学生把这些错误也学会了。处理方法包括降低α、用多个教师平均、或者在蒸馏损失中对高频错误类别做降权。4.4 现象四低秩分解后速度不升反降低秩分解听起来很美好理论计算量降低了但真实硬件不一定买账。原因通常是分解后算子数量变多每次调用都有固定开销或者中间结果的内存布局发生变化数据搬运不连续。排查办法很简单用profiler对比算子级别耗时看是计算变快了还是内存变慢了。如果确实如此就不要在卷积层硬套低秩分解转而把它用在超大Embedding、大尺寸投影矩阵上。这些地方总参数量大矩阵维度更规整编译器更容易优化出整齐的访存模式。如果一定要保留低秩结构可以尝试加大k的秩减少分解层数或者把低秩和量化叠加用更高的压缩率抵消额外算子开销。如果让我把这十年的经验浓缩成两句话我会说别总想着一次到位先做最小可行压缩再用部署环境验证另外所有精度损失都可以用蒸馏往回找补一点点但前提是结构骨架别剪坏了。每当我接到新的部署任务时我都会先复制一份原始模型把它锁在“保险箱”里所有实验都基于复制品做等确认效果之后再回归验收。这大概是我十年里最值钱的一个习惯。