JOVANA
Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

给模型瘦身

一个能登顶排行榜、却让每次请求都贵得离谱的模型,只是研究演示,不是产品。本篇讲清三大杠杆——量化、剪枝、蒸馏——它们让你用小模型的预算跑出接近大模型的质量,也讲清每一个杠杆背后藏着的诚实代价。

模型为什么一开始就太大

走到这一级,你已经会训练模型、把它部署上线、并盯着它的延迟。但你训练出来的那个模型,几乎总是比你应该部署的那个更大。一个拥有几百亿个参数大语言模型,把每个权重存成 16 位或 32 位的数字;再乘上每生成一个词元就要触碰的那几十亿个权重,你就得到了推理成本那道残酷的算术——存权重要的内存、搬权重要的带宽、乘权重要的能耗,而且每来一次请求都得重付一遍。

不过模型之所以胖,是有原因的,而且不是浪费。更大的容量让它更容易被*训练*——多出来的参数给了梯度下降更大的回旋余地去找到一个好解,而推动过去十年的缩放定律也表明:随着参数和数据增加,损失会平滑下降。本篇每一项技术背后的关键洞见就是:你*学会*一件事所需的规模,远大于你*运行*它所需的规模。瘦身,就是在大楼盖好之后把脚手架拆掉的艺术。

量化:每个数字少花几个比特

最便宜、最通用的杠杆是[[quantization-ml|量化]]:用更少的比特来存储和计算每个权重。一个以 32 位浮点训练出来的权重,所携带的精度远超模型实际用得上的,于是你把它四舍五入到一个更小的格式——8 位整数(INT8)已是常规,4 位如今在大语言模型上也很常见。从 16 位降到 4 位,内存占用大约缩小到四分之一,而这往往就是「一块GPU装得下」和「装不下」之间的分水岭。

别把它和你之前学过的混合精度训练搞混。那里,低精度是为了加快运算,同时保留一份全精度副本来维持训练稳定。而*训练后量化*发生在模型冻结之后:你把一段段浮点取值映射到一个小小的整数网格上,为每一组权重存一个缩放因子,再在运行时即时重建出一个近似的浮点数。其中的门道在于如何选这个网格——天真的四舍五入会毁掉准确率,因为少数罕见的离群权重数值极大,会把其他所有权重都挤进同一个格子里。

32-bit float weight   ->   4-bit integer + per-group scale
  0.0731  0.0024 ...        [ 9, 0, 14, 3, ... ]   x  scale=0.0081
  ~4 bytes each            ~0.5 byte each
  full precision          ~8x smaller, tiny rounding error
量化把每个权重四舍五入到一个粗糙的整数网格上,每组共享一个缩放因子,在运行时重建出近似值。

能压到多狠?对多数模型,INT8 几乎无损;4 位会损失一点点质量,但通常划得来;再往下,准确率往往会断崖式下跌。如果连小心翼翼的四舍五入都伤得太重,就用*量化感知训练*:把舍入操作模拟进训练回路里去微调模型,让梯度下降学出能扛住这道挤压的权重。回报是实打实的:量化后的模型需要更少内存、搬更少字节,在对整数友好的硬件上还跑得更快——延迟和吞吐量同时改善。

剪枝:把没用的权重丢掉

[[pruning|剪枝]]从另一个角度对付体积:它不是让每个数字变便宜,而是干脆删掉一些数字。许多训练好的权重都趴在零附近,对输出几乎毫无贡献,于是你把它们置零并跳过。*非结构化*剪枝把单个权重一个个置零——能砍掉很大一部分而准确率损失不大,但一个稀疏散落的矩阵很难让GPU加速,所以你省了内存却没怎么省速度。

在生产中通常更划算的是*结构化*剪枝:你移除的是整个单元——整个神经元、注意力头,或Transformer中的整层。剩下的模型既更小*又*稠密,普通硬件不用任何特殊技巧就能跑得更快。经典做法是迭代式的:剪掉一片,微调让幸存者恢复,度量,再重复。一次性剪得太猛,模型就再也缓不过来;循序渐进地剪,你往往能削掉三分之一的网络而几乎不伤元气。

蒸馏:把大模型懂的教给小模型

[[knowledge-distillation|知识蒸馏]]是最雄心勃勃的杠杆。你不去压缩已训练好的模型,而是从头训练一个全新的、更小的*学生*去模仿一个大的*教师*。诀窍在于学生模仿的是什么:不只是教师的最终答案(硬标签),而是它在所有选项上的完整概率分布——也就是*软标签*。当教师说「80% 是猫、15% 是狗、5% 是狐狸」时,这个分布就告诉学生:猫和狗比猫和狐狸更像。这些细微的梯度,每个样本所携带的信号远多于一个独热答案,所以小学生从中学得更快也更好。

这正是许多你真正负担得起去部署的小而快模型背后的路径——当教师是基础模型、学生是一个紧凑可部署的模型时,同样的思路就叫模型蒸馏。不过要诚实地认清上限:蒸馏迁移的是教师*在你给它看的数据上的行为*,而不是什么智能的精华。学生会继承教师的盲点和偏见,在远离蒸馏数据的任务上,它就是会力不从心。一个蒸馏出来的学生,是某一项能力的忠实回声,而非一颗微缩的通用心智。

组合起来:在实践中实现廉价推理

这些杠杆可以叠加。一条常见的生产流水线,会先把一个能干的教师蒸馏成一个更小的学生,再对学生做结构化剪枝,然后把结果量化到 4 位或 8 位——在体积和成本上拿到三重相乘的收益。把这一切统称起来就是模型压缩,而这门手艺是经验性的:每一步都需要自己那一轮度量,因为损失会累乘,有时还会以出人意料的方式相互作用。

  1. 先定好预算和底线:你必须达到的延迟、内存和成本上限,以及你不能跌破的准确率下限(看最坏情况切片,而不只是平均值)。
  2. 先试量化——它是最便宜、最可逆的收益,单靠 INT8 往往就能把你压到预算之内,且损失微乎其微。
  3. 若还不够,就做结构化剪枝、每剪一刀后微调一次,或者从手头的模型蒸馏出一个更小的学生。
  4. 通过一个可移植的运行时来编译并部署最终模型,让它在你的目标硬件上(边缘或服务器)跑得快。
  5. 端到端重新度量,并在上线后持续盯着——随着数据漂移,压缩后的模型可能以与母模型不同的方式退化。

两个实用的搭档让闭环成型。一种可移植的服务格式,比如 ONNX Runtime,让你把压缩后的模型导出一次,就能在 CPU、GPU 和各类加速器上运行而无需重写——而足够小的模型还打开了边缘部署的大门,直接在手机或传感器上运行,完全不用往返云端。这一切都不是「设好就不管」:要把你压缩后的模型置于和其他模型一样的监控之下,因为你在测试中躲过的那道悬崖,在真实世界里依然可能出现。