模型剪枝不是“一剪没”,我靠“笨办法”保住了99%的性能

前几个月帮一个做教育AI的朋友救场,他们的7B模型部署在边缘设备上,本来想剪枝压缩到3B大小,结果找了个自动剪枝工具一键操作之后,模型直接“傻了”,连小学算术题都能算错,生成的课文解析全是逻辑混乱的内容,离上线只剩不到两周,几个人急得嘴上全是泡。

我刚拿到他们剪完的模型时,第一感受就是——太粗暴了。他们直接按照30%的比例,把所有权重里绝对值最小的参数全剪掉了,根本不管这些参数在什么层。我随便抽了十几层看,发现注意力层里好几个负责捕捉长距离语义的重要头,直接被剪没了,难怪模型连上下文都记不住。那天我跟他们说,别信什么“一键全自动剪枝”,好的剪枝根本没有捷径,就得一层一层慢慢试,看起来是笨办法,实则是最快的路。

接下来的一周,我们把模型拆成了三个部分分开处理:首先是嵌入层和输出层,这两层直接完全不剪,本来参数量就不大,还对最终输出的语义影响极大,剪了得不偿失;然后是中间的注意力层,我们不按全局比例剪,而是给每个注意力头单独做重要性打分,把那些在测试集上贡献度低于1%的头才删掉,最后12个注意力头里我们只剪掉了2个最没用的;最后才是占参数量最多的前馈网络层,这里我们也没敢直接大比例剪,而是一层一层试,每次剪完就跑一遍他们准备的2000多道教育场景测试题,精度掉超过0.5%就立刻回滚。

那段时间我们办公室的服务器24小时没关,每个人轮着守夜,每次剪完一层就盯着测试结果看,有时候熬到凌晨两三点,发现某一层剪完精度掉了1%,就立刻退回去重新调整剪枝比例。本来大家都以为这么做肯定要花一个月,结果没想到一周就做完了,最后剪出来的模型参数量刚好压到了3.2B,在他们的边缘设备上跑起来速度翻了一倍,而在教育测试集上的精度,只比原来的全量模型低了0.8%,几乎感知不到差别。

中间还有个特别有意思的小插曲,我们本来以为前馈网络层可以剪得狠一点,结果有一层我们剪了20%的参数之后,模型突然就不会写作文了,写出来的内容全是短句拼接,完全没有逻辑。后来我们把删掉的那部分参数恢复回去一点点,立刻就好了,原来那部分看起来绝对值很小的权重,刚好是负责控制句子连贯性的,之前的自动剪枝工具根本识别不到这种场景化的重要性。

经过这件事我特别想吐槽一下网上很多鼓吹“一键剪枝”的教程,好像点一下按钮就能得到一个又小又快的完美模型,现实里根本不可能。不同的业务场景,对模型不同能力的要求天差地别:做客服对话的模型,可能对闲聊相关的权重敏感度低,你可以多剪一点;做数学推理的模型,但凡和计算步骤相关的层,你动一点点参数,结果就可能全错。你不结合自己的业务数据去做适配,光靠通用工具瞎剪,最后只能得到一个“残废模型”。

我后来还跟他们说,剪枝完别着急上线,一定要做一轮小范围的微调,不用全参数训练,就用自己的业务数据集做个几十步的LoRA微调,花不了多少算力,就能把剪枝时损失的那一点点精度完全补回来。他们照着做了之后,最后上线的模型,在用户实际使用的时候,几乎没人能分辨出它是剪过枝的压缩版本。

其实做技术很多时候真的不能太图省事,你想靠一个通用工具解决所有问题,最后往往要花更多的时间去填坑。剪枝这件事,看起来笨的逐层测试,反而是最稳妥、效果最好的路径,慢一点,反而走得更快。