
把一个模型训到指标好看只是走完了上半场。真正让人头疼的是下半场参数量 7B 的模型要放进只有 8G 显存的边缘盒子推理延迟要求 30 毫秒但单次前向就要 200 毫秒服务端一个月的算力账单比标注数据还贵。模型压缩解决的正是这个落差——它不追求把模型做得更聪明而是尽量在精度不掉太多或者干脆不掉的前提下把显存占用、计算量和带宽成本压下来。剪枝、量化、蒸馏这三条路线我反复用过很多轮也踩过不少坑剪完的模型理论上稀疏度 80%实测速度一点没变量化后离线评估一片绿上线三天收到一堆badcase蒸馏训了二十轮学生模型反而比从头训还差。这篇就把这三把刀各自的物理边界、实操流程和我真正会去检查的那几个指标完整摊开讲一遍。面向的是已经能把模型训起来、准备往部署端推的读者也适合想搞清楚为什么压缩完反而更慢这类反直觉问题的同行。1. 三把刀切的是不同的肉剪枝、量化、蒸馏的定位差异很多人把这三件事当成一个菜单觉得挑一个最顺手的用就行。实际它们优化的维度根本不是同一个东西剪枝动的是参数的存在性量化动的是参数的表示精度蒸馏动的是知识的载体。搞清楚这一点后面所有的选型和顺序问题都会自然清晰。1.1 用删、压、教三个动作建立直觉剪枝可以类比成给一棵树修枝。模型里大量权重接近于零或者某些通道的输出对最终结果几乎没有贡献把这些结构整体摘掉剩下的部分重新长一遍微调恢复就能得到一个小一圈的模型。注意这里摘掉必须是结构性的如果只是把权重置零但张量形状不变那叫稀疏不叫变小GPU 上该算的乘法一个都省不掉。量化是把一本精装书换成口袋书。原来每个权重用 32 位浮点存现在用 8 位整数存模型体积直接降到四分之一而且现代推理硬件对 INT8 的乘加吞吐通常是 FP32 的两到四倍。代价是每个数都被四舍五入到最近的格点误差会累积。量化的魅力在于它几乎不改变网络结构工程改造量小见效快。蒸馏则是师傅带徒弟。小模型学生自己去学任务标签只能看到这张图是猫这种硬信息而大模型老师输出的是一整条概率分布——80% 是猫15% 是猞猁4% 是狗这条分布里藏着类别之间的相似性关系信息量远大于一个 one-hot 标签。学生模仿这条分布等于免费拿到了老师的暗知识。注意这三种手段并不是互斥的实际项目里最常见的是先蒸馏出一个结构更小的学生再对它剪枝最后量化部署。但顺序一变效果可能差出一大截第 5 节会专门算这笔账。1.2 三个维度的指标对照我把这三种手段最常被问到的几个维度拉平放在一起这张表我基本每次做方案汇报都会拿出来用维度剪枝量化蒸馏直接收益计算量 FLOPs 下降、部分场景显存下降模型体积降至 1/4 或 1/2、带宽压力骤降、算力吞吐提升可用更小 backbone参数量与延迟同时下降精度损失风险中到高结构化剪枝更明显低到中INT8 通常 1 个点以内低学生容量足够时甚至反超工程改造量大要改模型结构、要重训小到中工具链成熟主要在校准中要维护双模型、改训练流程是否需要重训结构化剪枝基本必须PTQ 不需要QAT 需要本身就是一个训练过程加速是否自动兑现否取决于是否结构化是取决于硬件是否支持 INT8是student 更小就是更快典型压缩比通道数砍 30%~50%4xFP32→INT8学生参数约为老师的 1/10~1/3这张表里最容易被忽略的是加速是否自动兑现这一行。我见过不止一个团队做完剪枝稀疏度报表写着 75%结果上线延迟纹丝不动——因为他们做的是非结构化剪枝硬件根本吃不到这个稀疏性。1.3 怎么选从瓶颈在哪倒推手段选型不要从我想用什么技术出发要从现在卡在哪倒推。下面是我自己在用的判断路径用文字描述比画图更清楚瓶颈是显存放不下、模型文件太大、内存带宽打满优先量化。这是投入产出比最高的一刀往往一天就能出结果精度掉点在 1% 以内都算正常损耗。瓶颈是算力不够、FLOPs 太高、延迟要求苛刻且模型本身有大量冗余通道优先剪枝但一定选结构化通道级或层级的剪枝。瓶颈是我没法改部署链路只能用更小的模型架构但又不想重新收集标注数据、不想从头训选蒸馏。它不依赖新数据只依赖老师能在无标注数据上给出软标签。三个瓶颈同时存在组合使用顺序见第 5 节。这种情况下我的默认起手式是先蒸馏定型再剪枝瘦身最后量化交付。再补一条很多人不愿意承认的经验如果模型本来就没多少冗余比如已经是 MobileNet 级别的轻量 backbone或者任务本身对细微特征极其敏感像细粒度分类、小目标检测剪枝的性价比会急剧下降。这时候硬剪往往花两周时间换来 5% 的速度提升和 3 个点的精度下降不如老老实实量化加蒸馏。2. 剪枝从能删到删完真能变快之间的鸿沟剪枝是三个里面最容易做出漂亮报表、也最容易自欺欺人的一个。原因很简单稀疏度和实际加速之间隔着一整个硬件栈。2.1 非结构化剪枝为什么常常白干非结构化剪枝的做法是把权重张量里绝对值最小的那批元素置零得到一个稀疏矩阵。数学上你的参数量确实少了 70%存储如果用稀疏格式CSR/CSC也确实能省。但主流 GPU 的矩阵乘法单元是为稠密计算设计的它一个 warp 里的线程要同时取连续地址的数据你给它一个零散分布的非零元素它取数据的时间比算乘法的时间还长。所以会出现这种荒诞结果稀疏度 70%理论 FLOPs 降 70%实测单张图推理时间从 24ms 变成 26ms因为多了一层索引映射的开销。这就是我标题里说的鸿沟。要吃到非结构化稀疏的收益需要硬件或推理引擎原生支持稀疏张量核。部分推理框架提供了 2:4 结构化稀疏每 4 个连续权重里保留 2 个这种模式下硬件能拿到接近 2 倍的加速而且对精度的影响在可接受范围。但前提是你必须严格按 2:4 的 pattern 去剪模型质量和稀疏模式强绑定灵活度很低。我的建议很直接如果你的部署目标是通用 GPU 或移动端 NPU默认放弃非结构化剪枝直接做通道级结构化剪枝。除非你明确知道目标硬件支持稀疏加速否则那些稀疏度报表只是给自己看的。2.2 结构化剪枝通道打分与实操流程结构化剪枝的最小单位是卷积通道或者线性层的输出神经元。把一个通道删掉输出特征图的对应通道就没了下一层的输入通道也要跟着删这就带来一个连锁约束卷积层和紧随其后的 BN 层必须同步剪残差连接两侧的通道必须保持对齐。这些约束是结构化剪枝麻烦的根源。最常见的打分方式是 BN 层的缩放因子 γ。BN 在推理时等价于一个逐通道的仿射变换如果某个通道的 γ 接近 0说明这个通道的输出被压得非常扁对后续贡献很小。这套思路的好处是γ 本来就在训练里被梯度更新你只要在 loss 里给它加一个 L1 正则让 γ 在训练中主动趋向稀疏然后再按 γ 大小排序剪枝就相当于让网络自己告诉你哪些通道可以丢。下面是我常用的一个最小可跑的骨架基于 PyTorch 的 BN 参数做通道排序import torch import torch.nn as nn def collect_bn_gammas(model, threshold1e-3): 收集所有 BN 层的缩放因子返回 {层名: (gamma张量, 保留索引)} plan {} for name, m in model.named_modules(): if isinstance(m, nn.BatchNorm2d): gamma m.weight.detach().abs() keep_idx torch.where(gamma threshold)[0] plan[name] (gamma, keep_idx) return plan def sparsity_regularizer(model, coef1e-4): 训练时加在总loss上逼BN的gamma向0收缩 reg 0.0 for m in model.modules(): if isinstance(m, nn.BatchNorm2d): reg reg m.weight.abs().sum() return coef * reg # 训练循环里 # loss criterion(out, target) sparsity_regularizer(model, 1e-4)用正则化训完之后按照全局的 γ 分位数确定剪枝阈值而不是每层各剪各的比例——全局剪枝能让重要层保留更多通道比逐层均匀剪精度更好。实践里 30%~50% 的通道剪枝率是安全区超过 60% 就需要相当长的恢复训练才能救回来。真正改结构的时候建议别自己手写通道索引搬运容易在残差和 concat 处翻车。可以用 torch-pruning、NNI 这类支持依赖图自动传播的工具你只需要告诉它这一层的第 3、7、11 个通道不要了它会帮你把下游所有受影响的层一起改掉。这个细节非常重要我最早手写过一次残差分支没对齐训了一晚上 loss 一直在 NaN 附近震荡。2.3 恢复训练学习率和轮次的取值逻辑剪枝必然掉点掉多少取决于剪枝率。通道砍掉 40%ImageNet 上 top-1 掉 3~8 个点很常见。恢复训练的目标不是让它学新东西而是让剩下的通道重新分工把被删通道的职责接过来。关键参数的经验值学习率用原始训练峰值学习率的 1/10 到 1/100。因为模型已经在一个不错的局部极小值附近学习率太大会把它炸出这个盆地。我一般从 1e-4 起步配合 cosine 衰减。迭代轮次原始训练总轮数的 10%~30%。比如原模型训了 100 epoch恢复训练跑 15~30 epoch 通常够。剪枝率越高需要的轮次越多。冻结策略前 1~2 个 epoch 可以只训 BN 层和最后的分类头等统计量重新稳定了再放开全部参数。这一步能显著降低早期震荡。还有一个反直觉的点恢复训练时不要再加稀疏正则。正则的目的是寻找冗余现在冗余已经剪掉了继续加只会让剩下的通道也被压扁反而拖慢收敛。2.4 剪枝实操里最容易翻车的四个点我把这些年踩过的坑整理成一张排查表基本上遇到问题按这个顺序看就行现象大概率原因处理方式剪完不重训直接评估精度断崖正常现象剪枝必然要恢复训练直接进恢复训练流程不要慌稀疏度报表好看但延迟不变做了非结构化剪枝硬件吃不到换通道级结构化剪枝恢复训练 loss 震荡 / NaN残差或 concat 处通道没对齐学习率过大用依赖图工具重剪学习率降一个量级某一类精度掉得特别狠该类依赖通道被批量剪掉改成分组剪枝按类敏感度保留通道或调低整体剪枝率除此之外还有两个纸质文档里不会写的事。第一剪枝前先做一次逐层敏感度分析每次只剪一层、剪 20%看验证集掉多少点把最不敏感的那些层列出来优先剪。这个分析大概花两个小时但能让你的剪枝率上限从 40% 提到 55% 左右非常值。第二分类头和 neck 部分尽量别动。很多团队的默认操作是全网均匀剪枝结果分类头被剪得太狠最后一层的表达能力不够怎么训都救不回来。我一般的做法是 backbone 剪 40%neck 剪 20%head 完全不剪——head 参数量本来就小剪它省不下什么算力却最容易伤精度。3. 量化把 FP32 压成 INT8误差究竟从哪儿来量化是三条路里工程性价比最高的一条但它也是最容易离线评估全绿、线上翻车的一条。理解误差来源比会调工具重要得多。3.1 从浮点到定点scale 与 zero point 到底在算什么一个浮点张量要映射到 8 位整数本质是一个仿射变换q round(x / scale) zero_point x_approx (q - zero_point) * scale其中 scale 是一个整数格点代表多大的实数跨度zero_point 是偏移量作用是让实数 0 能精确地被某个整数表示。为什么必须保证 0 的精确表示因为 padding 用的就是 0如果 0 被量化成 3那所有 pad 区域都会注入一个偏置噪声这个坑很多人不知道。scale 的确定方式是 range / 255对称量化或 range / 255 的非对称版本。问题就出在这个 range 上它由校准数据里观测到的最大最小值决定。如果你的校准集恰好包含一个极端异常值比如某张图特别亮max 会被拉得很高scale 随之变大其余 99% 的正常数值就都被挤在很少的几个格点上分辨率严重不足。这就是所谓的离群值主导量化误差。对激活值来说这个问题尤其严重。权重的分布相对温和、接近对称而激活值经常有长尾——某些通道在特定输入下会突然爆一个很大的值。所以权重通常能安全地用对称量化、per-channel逐输出通道各自一个 scale而激活值只能 per-tensor抗离群值能力差很多。一个实用的判断方法拿校准集跑一遍统计每个激活张量的 max/99.9 分位数之比。如果这个比值超过 5说明存在明显离群通道直接 min-max 校准大概率掉点应该改用 KL 散度校准或者百分位截断。3.2 PTQ 与 QAT什么时候必须上 QATPTQ训练后量化的流程是拿一个训练好的 FP32 模型喂几百张校准图统计各层激活的分布范围然后把权重量化、把推理图改写成 INT8 算子。整个过程不需要标签、不需要反向传播半小时能搞定。它的问题在于量化误差是事后引入的模型完全没有机会去适应。QAT量化感知训练在训练时插入伪量化节点前向时把权重和激活真的截断、取整、再反量化回浮点模拟量化误差反向时因为取整不可导用 STE直通估计器把梯度原样传过去。这样模型在训练过程中就体验过量化误差会主动调整权重分布去规避它。选择依据我给一个简单粗暴的标准模型是 ResNet / MobileNet 这类结构规整的 CNN任务是分类直接 PTQ掉点在 0.5% 以内就收工。模型是 Transformer、检测、分割、或者任何含 LayerNorm / Softmax 敏感算子的结构先试 PTQ掉了超过 1.5 个点就转 QAT。精度要求极其苛刻比如指标必须在 0.3 个点内波动一开始就上 QAT别浪费时间试 PTQ。QAT 的成本大概是 PTQ 的 20 倍工时但它通常能把掉落从 2~3 个点收回到 0.3 个点以内。这笔投入在什么情况下划算我的判断是如果模型的精度直接对应线上收入或安全底线那必须 QAT如果是内部工具类模型PTQ 掉 1.5 个点能接受就接受不要过度优化。3.3 校准集怎么选这一步决定了量化的天花板校准集的规模不是越大越好。我的经验是 200~500 张有代表性的样本足够关键是覆盖度而不是数量。具体做法从验证集里按类别分层抽样保证每个类都有样本别随机抽出一个只有三个类的子集。一定要包含难例和边缘场景——低光照、模糊、遮挡、罕见角度。校准阶段的 max 值由最难的那张图决定如果校准集里全是干净样本线上遇到脏数据就会溢出到饱和区误差直接失控。校准集的预处理必须和线上完全一致。我遇到过一次校准脚本忘了做归一化输入动态范围差了 255 倍量化后模型输出全是常数排查了一整天才发现。校准算法上min-max 最简单但最怕离群值KL 散度校准会找一个截断阈值让截断前后的分布 KL 距离最小对激活长尾更友好百分位校准比如取 99.99 分位介于两者之间实现简单我日常用得最多。经验上KL 和百分位在 Transformer 类模型上比 min-max 稳定 0.5~1 个点。3.4 掉点排查的完整顺序量化掉点时不要乱试按这个链路往下走基本能在半天内定位第一步确认是不是可量化算子之外的部分出问题。把模型拆成量化主干 浮点头尾两段先只量化 backbone如果 backbone 单独评估掉点很小那问题在 head 或者后面的敏感算子。第二步逐层定位。用工具把每一层的输出和 FP32 版本做余弦相似度对比掉得最狠的那几层就是重灾区。常见的重灾区是第一个卷积层输入动态范围大、最后一层输出直接决定 logits、以及 LayerNorm 之后的激活。第三步针对重灾区做特殊处理。有三招把这几层保留为 FP16/FP32混合精度量化这是最快见效的把这几层的量化粒度从 per-tensor 改成 per-channel或者把这几层排除在校准统计里单独用更精细的百分位阈值。第四步如果还不行转 QAT。但转 QAT 之前先确认一下是不是模型本身已经处于精度极限量化只是压垮骆驼的最后一根稻草。这种情况下更好的方案是换一个稍微宽一点的模型再量化而不是硬压。特别提醒一个线上翻车的典型场景离线评估用的是标准的 resize center crop线上是任意长宽比的原始图。不同长宽比会导致量化后的激活分布偏移我见过一次 PTQ 离线掉 0.4 个点线上实际掉了 3.1 个点原因就是线上输入分布和校准集对不上。解决办法很简单校准集直接采线上真实流量样本。4. 蒸馏学生要模仿的不是答案是老师的犹豫蒸馏看起来最简单——不就是让小模型学大模型的输出吗但真正做起来很多细节决定了它是提点 2 个点还是掉点 1 个点。4.1 温度系数与软标签的作用机理老师的原始输出经过 Softmax 之后如果 logit 差距很大分布会非常尖锐0.99 对 0.01。这条分布里类别之间的相对关系信息几乎被抹平了。温度系数 T 的作用就是在做 Softmax 之前先把 logit 除以 T把分布摊平soft_target softmax(logits / T)T 越大分布越平暗知识暴露得越充分但 T 太大分布趋近均匀噪声也会被放大。经验区间是 T 在 2 到 5 之间任务类别数越多比如 1000 类T 可以取大一些类别少比如 10 类T 取 2 左右。这个值值得花半天扫一下不同数据集上的最优值差异不小。损失函数通常是两项加权import torch.nn.functional as F def distill_loss(student_logits, teacher_logits, labels, T3.0, alpha0.7): # 软标签损失学生分布逼近老师分布梯度要乘 T^2 做尺度补偿 soft F.kl_div( F.log_softmax(student_logits / T, dim-1), F.softmax(teacher_logits / T, dim-1), reductionbatchmean ) * (T ** 2) # 硬标签损失防止学生盲目模仿老师的错误 hard F.cross_entropy(student_logits, labels) return alpha * soft (1 - alpha) * hard这里有两个必须说清楚的点。第一T² 那个补偿不能漏。因为除以 T 之后梯度量级会缩小 T² 倍不乘回来会导致软标签项在总损失里被稀释蒸馏等于没做。第二alpha 不要设成 1。纯软标签在老师犯错时会把错误也传给学生硬标签是最后一道保险。我一般 alpha 取 0.6 到 0.8老师越强、越可信alpha 越接近 1。还有一条老师模型在训练时一定要开 dropout、要加数据增强让它的软标签带一点不确定性。如果老师是全知全能的完美状态输出过于确定学生学不到东西。反过来老师如果太弱比如只是比学生大一点蒸馏的收益也会很小。一般师生参数量比在 5:1 到 20:1 之间比较合适。4.2 中间层对齐只学输出会丢掉过程信息只对 logits 做蒸馏学生只能学到最终判断而不知道老师是怎么一步步提取特征的。中间层蒸馏就是把学生的某几层特征图和老师的对应层做对齐loss_feat MSE(proj(student_feat_i), teacher_feat_j)这里有个绕不开的问题学生和老师的通道数不一样形状对不上所以需要一个投影层1x1 卷积或者线性层把学生的特征升维到老师的维度。这个投影层会增加一点参数量但它只在训练时存在推理时可以直接扔掉。这一点很多新手不知道白白纠结半天。选哪几层对齐也有讲究。我的经验是选 backbone 的中间偏后的位置太靠前的层学到的是边缘、纹理这些低级特征师生的表示差异本来就大强行对齐反而有害太靠后的层已经接近任务特定表示对齐的收益变小。一般每个 stage 的最后一层取一个全网络 3~4 个对齐点就够。对 Transformer 来说常见的做法是对齐注意力矩阵而不是特征图——因为学生和老师的头数、维度都可能不同但注意力矩阵的语义是一致的都是 token 之间的关联强度。也有对 QKV 的 value 矩阵做对齐的变体效果视任务而定我在文本分类上试过对齐注意力矩阵比对齐特征图稳定一些。4.3 蒸馏不 work 的几个真实原因我遇到过学生训不过从头训的情况总结下来无非几类容量差距太大。学生太小比如老师 300M学生只有 5M学不动老师的分布。这时候应该先做中间规模过渡用大→中→小的两段蒸馏。alpha 和 T 没调。这是最常见的原因默认值直接套用十个任务里能有三四个效果很差。至少扫一遍 T ∈ {2,3,4,5}alpha ∈ {0.5,0.7,0.9}。学生的数据增强比老师弱。蒸馏时学生看到的数据分布必须不小于老师训练时的分布否则学生会在没见过的样本上崩溃。这一点常被忽略——很多人觉得学生小、训练快就少用了几种增强。老师模型过拟合。老师在自己的训练集上 99%泛化到学生看到的数据上其实没那么强。解决办法是让老师在训练时用更强的正则或者用 k-fold 的多个老师做集成取平均软标签。还有一条软经验蒸馏的收益和任务难度强相关。在类别少、区分度高的任务上比如二分类的缺陷检测蒸馏提点非常有限因为标签本身信息量就够而在细粒度分类、多标签、检索这类任务上类别间的相似关系信息量大蒸馏往往能补回学生一半以上的精度差距。5. 组合拳三招叠加时的顺序与验收标准单个手段做到位之后真正的问题是组合起来该按什么顺序收益会不会互相抵消5.1 先剪后蒸还是先蒸后剪收益叠加的规律我实际跑过三种顺序结论写在下面顺序做法优点缺点剪 → 蒸先把大模型剪小再用原大模型当老师蒸剪后模型剪枝的精度损失能被蒸馏补回来一部分剪完的模型和老师的结构差异大蒸馏对齐困难蒸 → 剪先蒸出小结构学生再对学生剪枝学生结构规整剪枝和量化都更友好剪枝的二次损失没有老师兜底蒸 → 剪 → 微调前两步之后加一轮轻量微调三种损失都能收回来精度最稳总工时最长我现在的默认流程是第三种实测下来精度损失最小。具体地说如果目标是把一个 300M 的模型压到 20M 左右我会这样分配蒸馏负责把 300M 降到 60M这一步能保住 95% 以上的精度剪枝负责 60M → 28M掉 2 个点左右恢复训练把损失收回到 1 个点以内最后量化 28M 的 INT8 版本再掉 0.5 个点。整体端到端精度损失控制在 1.5 个点延迟下降 8 倍左右。反过来如果先剪后蒸我遇到的主要问题是结构不匹配。剪枝之后模型有很多不规整的通道数和老师的层对不上做中间层蒸馏时需要写一堆映射逻辑工程复杂度急剧上升。除非你的场景里没有可用的原版大模型只能蒸自己否则我不推荐这个顺序。5.2 一条完整的流水线与每一环的验收线把整个流程固化下来大概是这样的基线固定。先在目标硬件上把 FP32 原模型的延迟、吞吐、显存、精度全部测一遍把测试脚本和固定验证集版本都锁住。这一步不做后面所有的加速比都是空的。蒸馏定型。选出目标结构的学生模型用 T 和 alpha 扫参的方式训一轮直到学生的精度和老师的差距稳定在 5% 以内。这时候如果差距还很大先别往下走回去检查学生容量或者老师的软标签质量。敏感度分析。在学生的结构上逐层剪 20%记录精度变化画出敏感度排序。这一步花两小时能省下后面反复试错的两天。结构化剪枝。按敏感度排序从低敏感层开始剪目标是让整体 FLOPs 下降 40% 左右。剪完立刻用小学习率恢复训练 15 个 epoch。量化。先 PTQ测精度和延迟掉点超过阈值就转 QAT 或对敏感层做混合精度。端到端验收。在真实硬件、真实输入尺寸、真实 batch size 下测三项延迟 P50/P99、峰值显存、完整验证集精度。每一环的验收线我列一下方便对照环节精度让步上限关键验收指标蒸馏与老师差距 ≤ 3%同等任务学生参数量、实际推理延迟剪枝 恢复训练≤ 2%实际 FLOPs、延迟不是稀疏度PTQ≤ 1%硬件 INT8 算子覆盖率QAT≤ 0.3%训练成本、算子兼容性注意硬件 INT8 算子覆盖率这一项。即使你把模型量化成了 INT8如果某些算子比如某些自定义的激活函数、非常规的 reshape在目标硬件上没有 INT8 实现推理引擎会自动 fallback 回 FP32这些层就成了整条流水线的瓶颈。我见过量化后体积降了 4 倍、延迟只降了 1.2 倍的情况原因就是有 3 个算子在 fallback占了 60% 的时间。解决办法是把这些算子替换成等价的、有 INT8 实现的算子或者干脆把它们用 FP16 单独跑。5.3 上线前的回归检查清单压缩模型最容易出的问题不是精度平均掉多少而是某一类样本突然崩了。所以上线前必须做的不是看总指标而是做切片对比按类别切片逐类别对比压缩前后的召回率找出下降超过 5 个点的类重点看。按难度切片把验证集按置信度分桶看低置信度区间的表现变化。压缩模型的损失通常集中在低置信度区间。按场景切片如果有多个数据来源不同设备、不同光照、不同分辨率必须逐场景看。长尾样本专项专门挑出每个类的少量困难样本人工过一遍这是我最后一道保险。还有一个反向的检查对比压缩模型和原模型的分歧样本。把两者预测不一致的样本捞出来看 200 张你会非常直观地知道压缩损失了哪种能力。有一次我发现压缩模型在小目标且低对比度的场景下错得特别多回头看敏感度分析果然是对应的高分辨率特征层被剪得太狠了。这种问题是切片指标看不出来的只有人工看样本才能发现。还有一件事值得单独说压缩之后一定要重测置信度校准。量化会让模型的输出分布整体偏移原本阈值 0.5 的判定可能不再适用。如果你下游有基于置信度的阈值逻辑比如低于阈值转人工压缩后必须重新扫一遍阈值否则会出现精度没掉但业务指标掉了的诡异情况。我个人在实际操作中的体会是压缩这件事的难点从来不在算法本身而在算得准。你得先有能力把 FP32 基线的延迟测准、把验证集的切片拆对、把硬件的算子支持摸清楚然后剪枝、量化、蒸馏这些手段才有意义。我见过太多团队花两周调出一个理论上快 3 倍的模型上线后因为一个 fallback 算子被打回原形。所以真正值得先投入的不是调参而是把那套基准测试和切片回归的脚本搭起来——这套东西搭好之后后面每一次压缩实验的迭代速度都会快一个数量级。另外一个小技巧做剪枝敏感度分析的时候别只用完整验证集用 200 张的分层子集就够了速度快 20 倍结论几乎一致这一步能把你试错的轮次从个位数提到十几次。