把一份开源权重下载到本地,量化到 4 位,显存占用从两位数降到个位数——很多人第一次做模型压缩,都是被这个瞬间说服的。然后是第二个瞬间:模型开始把两个小数的大小比错,长段落偶尔原地复读。它不是坏了,是精度掉在了某个你还没测到的地方。
压缩改变的不是体积,是误差的形状
量化、剪枝、知识蒸馏、低秩分解,常被并列成四种手段,其实它们动手的位置完全不同。
- 量化把权重和激活的表示变粗,误差是均匀铺开的还是集中在少数通道,取决于怎么定标。
- 剪枝拿走一部分连接或整块结构,动的是网络拓扑。
- 蒸馏换一个小网络去拟合原模型的行为,学的是输出分布而不是参数。
- 低秩分解则假设某些矩阵近似于低秩,赌的是冗余。
四种做法留下的误差分布不一样:有的让少数通道系统性偏移,有的在长尾输入上突然失准。所以“压缩率”这个数字本身说明不了什么,它没有告诉你误差落在哪。
我习惯先问一句:这个模型里,哪些层是不能动的。经验上,对输出影响大的往往是很少的一部分结构,把它们原样保留,其余部分压得更狠,比整网统一压到一个比特数更容易保住效果。代价是模型变得不均匀,部署时多几行分支逻辑,换来的是可控。
压到多少,取决于它跑在什么上面
还有一个常被忽略的变量是硬件。同样是 4 位权重,某些芯片上有对应的算子实现,吞吐确实上来;换一块卡,可能要反量化回更高位宽再算,反而比直接跑 8 位更慢。模型压缩从来不是纯粹的算法问题,它和算子实现、内存带宽、批大小绑在一起。先确定推理落在哪种设备上,再决定压到哪一档,顺序反了就会白忙。
评估也容易走偏。困惑度降了几个点,不等于业务上可用;反过来,困惑度难看但任务指标稳住的模型也存在。更实际的做法是拿自己的数据分布跑一遍,尤其是那些不常见的输入——压缩带来的退化,通常先在长尾上露头。
它不是一个按钮
所以我不太喜欢把模型压缩说成“把大模型变小”的动作。它更像一次有取舍的重新设计:你放弃一部分表达能力,换内存、延迟和成本,前提是清楚放弃的是哪一部分、会不会在你关心的场景里被触发。做不到这一点,压缩就只是把问题推迟到上线之后。