把一个大模型塞进一台三五年前的手机,最先遇到的往往不是算法问题,而是内存。加载权重的那一刻,可用内存被吃掉大半,系统和别的应用开始争抢,推理还没跑起来,进程已经不太稳。很多人对模型压缩的想象停留在这里:把参数改小一点,文件瘦一点。真做起来才发现,变小只是起点,能不能用才是问题。
压缩不是把文件变小,而是重新分配预算
端侧部署有一组很硬的约束:内存上限、算力上限、功耗上限,还有延迟。它们互相牵制。模型压得太狠,精度掉下去;压得太松,跑不动或者发烫。所以模型压缩更像是在做预算分配——把有限的比特和计算留给真正影响输出的部分,其余的做近似。
这个视角会改变很多判断。比如一个模型在服务器上表现稳定,压缩后准确率只降了一点点,看起来可以接受;但端侧面对的是长尾输入,光线差、口音重、文本里夹着错别字,那“一点点”可能正好落在关键处。压缩带来的退化通常不是均匀分布的,它偏爱常见样本,对边角情况更吝啬。
三条常见路径,各有各的代价
- 剪枝:去掉不重要的连接或结构。结构化剪枝对硬件友好,但粒度粗;非结构化剪枝压缩率高,实际加速却常常兑现不了,除非底层算子配合。
- 量化:把浮点权重和激活换成低位表示。它带来的收益直接,工程上也相对成熟,难点在异常值处理和精度校准,尤其是激活值的动态范围。
- 蒸馏:让小模型去学大模型的输出分布。它不直接减少参数,而是换一个更小的载体,代价是要有可用的教师模型和足够的训练投入。
这三条路径经常被一起用,先蒸馏得到小结构,再量化,再剪掉冗余。叠加不是简单相加,误差会累积。工程上更稳妥的做法是一次只动一个变量,把每次改动后的行为记录下来,否则出问题很难定位。
被忽略的一环:压缩之后怎么判断它还行
我见过不少项目,压缩流程走得很顺,指标也漂亮,上线后却频繁出问题。原因常常不在压缩本身,而在评估方式。用同一套测试集比较前后精度,得到的结论太乐观。端侧的真实输入分布和训练集差得远,设备、传感器、用户习惯都会改变数据的样子。
更实际的做法是把评估拆开:先看整体指标有没有越线,再看关键子集的表现,最后看失败样本长什么样。第三点尤其重要。压缩后的模型往往不是“全面变差”,而是某几类输入突然失效,比如短文本、低亮度图像、带噪声的语音。把这些失效模式找出来,比盯着一个小数点更有意义。
边界在哪
模型压缩有它的适用区间。当任务本身对精度极度敏感,或者输入分布极不稳定时,压缩的空间会很窄,硬压只会把问题推到用户那边。另一种情况是硬件不支持低位运算,压缩省下的存储换不来速度,收益就被抵消了。
所以我会把模型压缩看作一项需要和部署环境一起设计的工作,而不是训练结束后的收尾步骤。它决定了模型最终以什么形态存在于设备上,也决定了用户在什么情况下会碰到它的短板。把这件事想清楚,比追逐更低的比特数更有价值。