大模型部署是个体力活。模型越来越大,显存越来越不够用,推理速度越来越拉胯,线上并发一上来就告警。我这两年做过不少大模型的"瘦身"项目,从知识蒸馏到量化,再到剪枝和低秩分解,基本把这几条主路线都踩了一遍。这篇就把压缩加速的完整思路、实操步骤和踩过的坑一次性讲透,给正在做模型部署、推理优化或者端侧落地的同学一个可以直接上手的参考。
严格来说,大模型"瘦身"不是单一技术,而是一整套组合拳:知识蒸馏负责把大模型的能力迁移给更小的学生模型,量化负责把权重精度从 FP16 压到 INT8 甚至 INT4,剪枝负责把冗余参数直接删掉,低秩分解负责把权重矩阵拆小。每条路线解决的问题不一样,副作用也不一样,组合起来用才是真正的工程答案。
1. 压缩加速的整体设计思路与选型
1.1 为什么大模型非"瘦身"不可
先说账。一个 7B 量级的模型,FP16 权重就要占 14GB 显存,这还没算推理时的激活值和 KV Cache。换句话说,单张 16GB 的卡,光装下权重就已经快爆了,根本别想跑长上下文。更别提 13B、几十B 的模型,直接与单卡部署无缘。
部署层面有两个硬指标绕不开:显存占用和推理时延。显存不够模型根本起不来,时延超标则直接决定线上能不能扛住并发。而这两件事,恰恰是模型压缩最直接能改善的。我做过的实际案例里,一个原本需要两张卡才能部署的模型,做完 INT8 量化后单卡就能跑,时延还降了将近一半。这就是"瘦身"最朴素的价值:帮你省卡、省电、省带宽,同时把增量用户接进来。
还有一个常被忽略的角度:带宽。分布式推理时,模型参数要从显存搬到算力单元,搬运量跟参数体量成正比。参数越小,计算密度越高,通信开销越低。对端侧场景更是如此,手机、边缘设备的内存和带宽都很有限,模型不压缩几乎没法落地。
1.2 四条主路线的对比与组合逻辑
模型压缩的主流路线,我整理成了一张表,方便大家先建立全局认知:
| 技术路线 | 核心思路 | 体积收益 | 速度收益 | 主要代价 | 适合场景 |
|---|---|---|---|---|---|
| 知识蒸馏 | 大模型当教师,小模型当学生,迁移能力 | 可训练一个更小的模型 | 间接生效 | 需要重新训练,成本高 | 需要换小模型架构时 |
| 量化 | 降低权重/激活精度,如 INT8、INT4 | 明显,最大可到 4 倍 | 明显,访存减少 | 精度可能下降,需校准 | 部署阶段最常用 |
| 剪枝 | 删除冗余参数、头、层 | 结构化剪枝有效 | 结构化剪枝有效 | 可能掉点,需微调 | 与量化组合使用 |
| 低秩分解 | 把大矩阵拆成两个小矩阵 | 中等 | 有理论收益但依赖算子 | 拆解后精度恢复麻烦 | 特定结构,如 MLP 层 |
关键的工程经验是:这些路线不是"选一个就行",而是有优先级的组合。我的习惯是——先量化,因为性价比最高,改动最小;如果量化后精度不行,再加混合精度保护;如果模型还是太大,再做蒸馏换小模型;最后才考虑结构化剪枝,因为剪枝对推理引擎算子支持要求高,容易白忙活。
还有一点必须提前想清楚:任何压缩操作都会改变模型行为,所以"评测基线"要先建立。动手压缩之前,先把原模型在代表性数据集上的指标跑出来,F1、困惑度、生成质量评分都可以。后面每一步压缩,都用同一套评测去看变化,否则你根本分不清到底是哪一步把模型搞坏的。这是我见过最多的反面教材:一堆人上来就量化,量化完模型乱说话,然后懵在原地。
2. 知识蒸馏:让学生模型继承教师模型的"经验"
2.1 蒸馏的本质:软标签为什么比硬标签更强
知识蒸馏的核心逻辑说起来很简单:用一个已经训练好的大模型当"教师",让一个小模型当"学生",学生去学教师的输出。很多人以为蒸馏就是拿大模型标注数据来微调小模型,这么理解只对了三分之一。真正的关键是软标签。
举个例子,如果让教师模型做文本分类,面对一条"这家餐厅服务态度很好",硬标签可能只给出"正向"一个答案。但教师模型内部的概率分布是这样的:正向 0.85,中性 0.12,负向 0.03。这个分布就比"正向"两个字信息量大多了——它告诉学生,其实"正向"和"中性"之间也有点模糊。这种类间关系、相似度信息,正是学生模型能从小参数里学到大模型"经验"的核心。
如果用生活化的比喻:硬标签像是只告诉你答案,软标签像是告诉你"这个选项为什么选它,那个选项为什么差点被选中"。学生从解答过程里学到的,比从答案里学到的多得多。这也是蒸馏相比直接用真实标签微调,效果普遍更优的原因。
2.2 温度与损失函数的实战调节
蒸馏要真正落地,除了软标签,还要处理两个关键参数:温度和损失权重。
温度 T 的作用是控制概率分布的"平滑程度"。T 越大,分布越平滑,类间差异被放大,软标签携带的"暗知识"越丰富;T 太小,分布趋近硬标签,蒸馏效果就退化了。我常用的做法是 T=4 起手,训练到中途调回 2,最后阶段用 1 接近真实分布。这不是玄学,T 太高时梯度里噪声也大,学生容易学过头;T 始终太低又回到硬标签问题。
损失函数的设计也很固定,核心是一个加权组合:
L = α * CE(y_pred, y_true) + (1 - α) * KL(soft_pred, soft_target)前半部分是学生跟真实标签的交叉熵,保证学生不偏差;后半部分是学生输出分布和教师软标签的 KL 散度,保证学生模仿教师。α 通常取 0.3 到 0.5 之间,需要小规模实验调。我习惯先固定 T=4 扫一组 α,再固定 α 扫 T,两个变量分开调,避免同时改导致定位不了问题。
还有一点容易被忽视:蒸馏的温度和推理温度是两回事。推理时一般把温度恢复成默认值(如 1),否则生成内容会过于平滑、缺乏确定性。我见过有同学在蒸馏模型上线时忘了改回推理温度,结果生成质量肉眼可见地变差,排查了半天才发现是这问题。
2.3 蒸馏落地的关键细节与避坑
蒸馏看着简单,实操里坑很多。第一个是教师太强、学生太弱。如果教师是几十B 的模型,学生只有一两百M,学生根本学不动。这时候光用输出层蒸馏是不够的,得用中间层特征蒸馏的思路——让学生的中间层表示去逼近教师的对应层表示。常见做法是把两边的隐层向量投影到同一维度,再加一个 L2 距离损失。这个"中间层对齐"往往比单纯输出蒸馏更稳,尤其对生成模型。
第二个坑是数据分布匹配。教师模型擅长什么领域,你蒸馏的数据最好也是那个领域。我之前做过一次通用模型的蒸馏,手头没有领域数据,就随便拼了些通用语料,结果学生模型在目标场景上的表现明显低于预期。后来换成目标场景数据,效果立刻上来了。蒸馏数据质量比数量更重要,几百条高质量数据能顶几千条凑数的。
第三个经验是蒸馏后模型往往不一定掉点,有时反而更稳。因为教师模型在输出分布中编码了信息,相当于给训练加了正则,学生在小数据上收敛更稳,泛化也更好。所以别一上来就恐惧蒸馏带来的性能损失,用评测说话。
3. 量化:用更低的精度换体积和速度
3.1 量化到底在做什么
量化可能是压缩里性价比最高的一步。基本原理一句话:模型权重本来就是浮点数,每个参数用 FP16(16 位浮点)存,如果换成 INT8(8 位整数)存,理论上存储直接减半;换到 INT4,存储变成四分之一。
那精度损失怎么控制?量化的本质是把浮点数值范围映射到整数范围。比如一个 FP16 的权重范围是 [-2.5, 2.5],我们把它线性映射到 INT8 的 [-128, 127],每个数值乘一个缩放系数。这个映射公式就两个参数:scale(缩放比)和 zero_point(零点偏移)。
对称量化和非对称量化的区别也常说。对称量化把映射中心定在零,公式简单,硬件友好;非对称量化允许负数和正数范围不同,通常精度稍好一点。实际部署中,权重一般用对称量化,激活值必要时用非对称量化。因为权重分布通常以零为中心、比较对称,而激活分布偏斜明显,非对称能多"榨"出一点精度。
这里必须说明:很多人以为量化就是"直接把 float 强转 int",这是错的。如果不做校准直接转,大模型基本会直接崩掉或者质量暴跌。正确做法是先拿一批代表性数据跑一遍模型,观察权重和激活值的真实分布范围,再根据这个范围算 scale 和 zero_point,这个过程叫校准。
3.2 PTQ 与 QAT 的实操流程
量化主要分两条路:训练后量化(PTQ)和量化感知训练(QAT)。
PTQ 的流程很直接,我一般这么走:
- 在目标数据集上抽 100 到 500 条代表性样本作为校准集。
- 用校准集跑一遍原始模型,记录每一层激活值的分布。
- 根据分布计算每层的 scale 和 zero_point。
- 把权重和激活转换成低精度表示,得到量化模型。
- 用完整评测集对比量化前后的指标。
这里面最大的变量是校准集。选不好,分布统计失真,量化误差会放大。我习惯从真实线上请求里采样,而不是从训练集里抽——线上分布才是模型实际要面对的分布。
QAT 则是在训练或微调阶段,给模型插入"伪量化节点",让模型在前向计算时模拟量化误差,反向传播时权重照常更新。这样模型在训练时就"适应"了量化带来的噪声,最后转成真正的低精度模型时掉点要小得多。代价是要有训练数据和算力,流程也更长。
实操经验:模型越大,PTQ 越稳;模型越小,PTQ 越容易崩。原因是超大模型参数冗余度高,每一层少量精度损失被整体容量吞掉了。小模型每一层都"物尽其用",一点量化误差都可能被放大成明显的质量劣化。所以小模型更倾向于用 QAT 或者混合精度方案。
3.3 精度修复的成熟经验
量化后如果精度不达标,别急着上 QAT。先把问题定位准,通常有三个手段:
第一,敏感层保护。不是所有层都一样怕量化。我实测下来,embedding 层、LayerNorm 层、以及模型最后的分类头或输出层,往往是最敏感的。这些地方的量化和不改,其他层照常量化,这种混合精度方案经常能用几乎不增加多少体积的代价,换回大部分精度损失。具体做法是逐层量化并对比层输出的余弦相似度,哪层相似度低就把它留成高精度。
第二,处理离群值。大模型权重和激活里经常有少量绝对值特别大的"离群值",它们会把量化范围撑得很大,导致大多数数值的量化精度下降。我见过几种处理方法:按通道分组而不是整层统一做 scale(把不同通道分开量化,离群通道单独处理);或者先用某些方式把权重按通道做平滑缩放,再统一量化。这些都是成熟思路,本质都是"别让少数极端值毁掉全局精度"。
第三,KV Cache 量化。推理时显存消耗的大头经常不是权重,而是 KV Cache,尤其长上下文场景。对 KV Cache 做 INT8 甚至更低精度量化,能显著提升并发能力。这个往往和权重量化是独立的,可以单独开关,我用下来对显存的改善非常可观。
4. 剪枝、低秩分解与 KV Cache 优化
4.1 结构化剪枝与非结构化剪枝
剪枝的想法也很直白:大模型很多参数是冗余的,删掉不影响能力,只影响体量。但"怎么删"决定了这个方案能不能落地。
非结构化剪枝是细粒度地删掉单个权重,把一个矩阵里的很多元素置零,得到稀疏矩阵。理论上压缩率高,但实际很鸡肋——稀疏矩阵要享受加速得靠专门支持稀疏计算的硬件和算子,通用推理引擎里几乎拿不到收益。我做过的测试里,做个 50% 稀疏的模型,通用引擎上速度不仅没快,反而更慢,因为计算变成了不规则访问。
结构化剪枝就务实得多:删的是结构性单元,比如删掉某个 Attention Head、删掉 FFN 里的某些神经元,甚至直接删掉若干层。删除对象是独立的计算单元,所以删完后模型结构依然整洁,现有的算子都能直接跑,加速效果实打实。代价是删完大概率掉点,需要拿少量数据做微调做恢复。我看到不少开源方案里确实有删层 + 微调的实践,操作路径是通的。
实操上我建议:剪枝从Attention Head 数和 FFN 中间维度下手最安全,这两个部位冗余普遍最多。别一上来就删层,删层的风险大得多,尤其是深层之间的依赖关系复杂。
4.2 低秩分解与权重共享
低秩分解的思路是把一个大权重矩阵 W 近似拆成两个小矩阵 A 和 B 相乘,只要拆出来的 A、B 维度远小于原矩阵,就能省大量参数和计算。典型如把 4096×4096 的矩阵拆成 4096×512 和 512×4096,参数从 1600 万降到 400 万,理论省 75%。
但低秩分解有一个工程坑:W 的低秩近似不是免费的。分解本身有误差,而且并非所有层都适合拆。我实测下来,Transformer 里的 FFN 层适合拆,因为它是稠密大矩阵;而注意力里的 Q/K/V 投影层直接拆容易掉点,它们的权重往往对下游影响更敏感。另一个现实问题是,很多推理引擎并没有为分解后的算子做过专门优化,导致理论省了计算量,实际速度没变化。所以低秩分解在落地时优先级不高,一般排在量化和剪枝之后。
权重共享则更多用在减小嵌入层。很多模型词表非常大,嵌入矩阵占了参数总量的不小比例。可以把嵌入层映射到更低的维度,或者让嵌入层和输出层共享权重。这类操作配合蒸馏落地小模型时很常用,单独做也要注意词表相关的任务效果。
4.3 KV Cache 优化:推理显存的隐形大头
聊加速容易忽略一个事实:生成阶段每出一个 token,都要把历史 token 的 Key 和 Value 缓存下来,供后续注意力计算用。上下文越长,KV Cache 越大,到最后比权重还占显存。所以 KV Cache 优化是大模型推理加速绕不开的一环。
目前主流的优化方向有几个。一个是GQA / MQA这类注意力结构改造,让多个注意力头共享同一组 KV,从源头减少缓存量。这个通常在模型训练阶段就做了,推理阶段改不了。推理阶段能做的,主要是前面说过的KV Cache 量化,以及滑动窗口或关键 token 裁剪——只保留最近几千 token 的 KV,更早的丢掉或做摘要压缩。
KV Cache 优化的实际收益非常直观:原来能并发的请求数,做完 KV Cache 量化后常常能翻倍。这也是为什么我反复强调,压缩项目别只盯着权重,要看整个推理链路哪里在吃显存、哪里在吃带宽。
5. 常见问题与排查技巧实录
5.1 蒸馏效果不佳怎么排查
蒸馏做完效果不如预期,先别怀疑蒸馏理论,按这个顺序排查:
- 教师模型输出分布是不是过"尖"。如果教师对目标任务的置信度都很高,软标签几乎等于硬标签,信息量不够。先把温度调高到 8 甚至 10 试试。
- 损失权重 α 是不是失衡。KL 项权重太低,学生根本没在学教师;太高,学生又被教师带偏,真实标签信息丢失。
- 学生模型容量是否太勉强。中间层特征对齐试过没有?如果学生比教师小两个数量级,输出层蒸馏大概率不够。
- 数据分布是否和目标任务匹配。这条是最大隐性问题,我踩过最多次。
5.2 量化后模型"崩了"怎么定位
量化后模型输出质量严重下降,别慌,逐层排查是标准流程:
- 拿一组输入,跑原始模型和量化模型的每一层,对比每一层输出的余弦相似度。
- 找出相似度最低的那几层,这几层就是罪魁祸首。
- 把这几个层单独改成高精度,其他层保持量化,重新评估。
- 如果改完恢复明显,说明定位准确,直接定稿混合精度方案。
- 如果全层相似度都在 0.9 以下,大概率是整体 scale 算得有问题,检查校准集覆盖性和分布统计。
这个逐层对比的思路是通用的,我在多个模型上都用过,基本半小时就能定位出敏感层。
这里还有一条重要心得:量化后不只看指标均值,要看极端 case。我遇到过均值指标几乎没掉,但个别输入下模型输出完全跑偏的情况。所以评测集里一定要混合一些长文本、多轮对话、特殊格式输入,这些 case 更考验量化模型的稳定性。
5.3 一个可复用的压缩工作流
最后把我实际跑下来比较顺的工作流整理出来:
- 建立评测基线:原模型跑满代表性评测集。
- 先做 PTQ 量化(INT8),看精度和速度变化;如果达标就收工。
- 不达标就逐层对比定位敏感层,上混合精度;还不达标再考虑 QAT 微调。
- 体积仍不够,就上蒸馏,直接训练一个目标大小的学生模型,蒸馏数据用目标场景数据。
- 如果还嫌大,最后上结构化剪枝 + 少量微调恢复。
- 每一步改完,都回到第 1 步同一套评测上对比,所有结论用数据说话。
我个人的体会是,压缩最难的不是某一项技术有多深,而是组合顺序和数据意识。方案定得好,往往量化和混合精度两步就能解决问题,根本不用折腾蒸馏和剪枝。方案定得糙,一步选错陷进精度修复的泥潭里,项目周期会被拖得很长。另外,强烈建议在项目一开始就把部署目标写清楚:到底要压到多少 G、要跑到多少毫秒时延。目标越具体,选型和节奏越好把握,不会被中间的各种诱惑带偏。