☰
模型压缩十年实战:量化、剪枝、蒸馏与低秩分解的选型指南
2026/10/18 13:39:11 网站建设 项目流程

2015年夏天,我第一次接触“模型压缩”这个概念时,手头是一台内存只有512MB的终端设备。当时深度学习的参数规模还远不如今天夸张,但已经足够让一个像样的视觉模型“装不进去”。我花了很多个晚上手工砍通道、试低比特权重,最终把模型压到能跑,代价是精度掉了将近三个点。那个项目给我留下的最大教训是:压缩不是“碰运气”,而是一套需要体系化决策的工程方法。

从2015到2025,模型压缩走过了整整十年。它从少数论文里的技巧,变成了部署大模型、端侧模型、边缘设备时绕不开的“标配工序”。这篇内容不是教科书式的综述,更像是我带着这十年的实际经历,把主线拆开:量化、剪枝、蒸馏、低秩分解,它们为什么存在、怎么选型、怎么排布,以及我在项目里踩过的那些坑。如果你正要为一个容量或时延受限的场景做模型瘦身,这套判断框架应该能帮你少走不少弯路。

1. 十年演进的主线:从手工瘦身到自动化改造

1.1 第一波:规则方法当道(2015—2017)

这阶段我习惯叫它“规则方法当道”。深度学习刚过了爆发期,大家发现模型越大效果越好,但硬件和网络场景跟不上。于是出现了很多“硬核”压缩手段:二值化、三值化、哈希参数共享、低秩近似、剪掉小权重。思路很直接,用数学或启发式规则把“显然是冗余的东西”干掉。

为什么2015到2017年集中出现规则方法?因为那时的部署平台远没有今天智能,算子融合、自动调优都还停留在编译器层面。模型容量就是硬约束,把大网络改造成小网络是唯一的出路。但我得说,这类方法最大的问题是精度不可控,稍一用力就崩。我当年做二值化实验时,网络几乎只能保住六成精度,后来才知道问题不在“量化这个主意”,而在于没有做训练时感知量化,纯粹靠事后截断,信息丢得太多。

这个阶段的另一个特点是,很多人都把目光放在“减少参数量”上,却忽略了真实运行时的内存搬运。一个在纸面上算起来很省的二值网络,如果底层算子不支持位运算,反而可能比普通FP32更慢。所以那几年的经验总结下来就一句话:压缩方案必须绑定部署硬件一起看,脱离了目标平台的压缩都是自嗨。

1.2 第二波:结构化改造与蒸馏成熟(2018—2020)

2018年以后,硬件平台和编译生态开始支持更多算子,非结构化剪枝虽然减少计算量,但在通用矩阵乘法上收益有限。于是“结构化剪枝”成为主流,因为它改变了张量的真实形状,能直接利用底层高效算子。同一时期,知识蒸馏大规模出现在各类任务里,从图像分类扩展到检测、分割、语音。

为什么蒸馏会在那个节点火起来?因为“大模型作为教师”这个设定,缓解了压缩后模型的收敛问题。你直接拿一个小网络从零开始训练,往往很难逼近大模型的天花板;但如果你让大模型把已经学会的规律“翻译”成软标签喂给学生,小模型的训练起点和信号质量都会高很多。这相当于有人给你画好了重点,你只需要背熟而不是自己从头划重点。

当时很多团队把“压缩”和“自动化”绑在一起,用结构搜索或强化学习寻找每一层的剪枝比例。听起来很性感,但实际落地成本很高,中小团队往往还是要用手工经验加规则。另一个重要变化是,压缩评估从只看“指标”走向看“算子融合后的真实时延”。如果只盯着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×n,U 是 m×k,V 是 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时,学生输出几乎变成均匀分布,指标当然不动。降回T=4之后就正常了。第二个原因是学生容量太小,教师的知识超出学生带宽,这时候光调温度没用,要改用中间层特征对齐,或者用Hint Loss把学生的特征图往教师对应层上面拉。

还有一种情况是教师在易错样本上犯了系统性错误,学生把这些错误也学会了。处理方法包括降低α、用多个教师平均、或者在蒸馏损失中对高频错误类别做降权。

4.4 现象四:低秩分解后速度不升反降

低秩分解听起来很美好,理论计算量降低了,但真实硬件不一定买账。原因通常是分解后算子数量变多,每次调用都有固定开销;或者中间结果的内存布局发生变化,数据搬运不连续。排查办法很简单,用profiler对比算子级别耗时,看是计算变快了还是内存变慢了。如果确实如此,就不要在卷积层硬套低秩分解,转而把它用在超大Embedding、大尺寸投影矩阵上。这些地方总参数量大,矩阵维度更规整,编译器更容易优化出整齐的访存模式。

如果一定要保留低秩结构,可以尝试加大k的秩,减少分解层数;或者把低秩和量化叠加,用更高的压缩率抵消额外算子开销。

如果让我把这十年的经验浓缩成两句话,我会说:别总想着一次到位,先做最小可行压缩,再用部署环境验证;另外,所有精度损失都可以用蒸馏往回找补一点点,但前提是结构骨架别剪坏了。每当我接到新的部署任务时,我都会先复制一份原始模型,把它锁在“保险箱”里,所有实验都基于复制品做,等确认效果之后再回归验收。这大概是我十年里最值钱的一个习惯。

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

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

立即咨询