把一个FP32的视觉模型量化成INT8,部署到边缘设备上,推理速度翻了好几倍,结果精度直接掉了三四个点,这是很多算法工程师都踩过的坑。排查下来,量化算法没选错,量化框架也没问题,最后往往发现问题出在一份随手凑的校准数据上。校准数据决定了激活值统计分布的准确性,而激活分布直接决定了量化参数(scale和zero point)的计算结果。换句话说,校准数据质量不行,再好的量化算法也救不回来。这篇文章就来系统聊聊校准数据该怎么选、怎么用,以及如何诊断和修复量化掉点问题。

为什么校准数据对量化精度影响这么大
先从原理上理解这件事。训练后量化(PTQ)并不会重新训练模型,而是在前向推理过程中统计每一层输出激活值的分布,然后根据这个分布计算量化参数。如果激活值的实际分布范围被低估,大量数值会被截断(clip)到量化边界之外,造成严重的信息损失;如果范围被高估,量化步长变大,数值分辨率下降,小信号会被淹没在量化噪声里。
校准数据的作用就是提供一个“足够有代表性”的激活值分布样本。以ResNet这类分类网络为例,如果你用100张全是猫狗的图片做校准,但实际业务场景里有大量工业缺陷图像,那么卷积层统计出的激活范围完全可能偏离真实分布。尤其是检测、分割这类任务,backbone浅层特征对输入内容极其敏感,分布偏差会被逐层放大。
一个简单的验证方法:用测试集中随机抽取的图片做校准,再用测试集评估量化模型,通常掉点在0.5%以内;换成不相关的数据集校准,掉点可能达到2%以上。这个对比能直观说明校准数据的重要性。
校准集应该怎么选:规模、分布与预处理
第一是规模。经验值通常在100到1000张之间,对于分类任务,128到512张基本够用。校准的本质是估计分布,样本太少方差大,样本多了收益递减。如果某个层激活值分布有长尾特征,可以适当增加样本量,让极端激活值有机会被观测到。
第二是分布匹配。校准数据必须来自与推理时相同的数据分布,这是最重要的一条原则。具体做法是从实际业务数据中采样,尽量覆盖各类别、各种光照、各种目标尺寸。如果业务数据不方便拿,至少要保证图像风格、分辨率、噪声水平接近。跨数据集校准是精度掉点的高发原因。
第三是预处理一致性,这是最容易翻车的地方。训练时的resize方式、归一化均值方差、色彩空间转换顺序,校准时必须完全一致。下面是一段典型的校准流程伪代码:
import cv2
import numpy as np
def preprocess(img_path):
img = cv2.imread(img_path)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 注意:与训练保持一致的 resize 方式,训练用 letterbox 就不能直接 resize
img = cv2.resize(img, (640, 640))
img = (img.astype(np.float32) / 255.0 - mean) / std # mean/std 必须与训练一致
img = img.transpose(2, 0, 1) # HWC 转 CHW
return img[None] # 增加 batch 维度
calib_images = load_calib_list("calib.txt")
for batch in batches(calib_images, size=32):
model.forward([preprocess(p) for p in batch]) # 只跑前向,框架自动统计激活分布
quant_model = model.quantize()
这段代码里每一行注释都是常见错误点:有人训练用了letterbox填充,校准时直接暴力resize;有人训练是BGR输入,校准时转成了RGB;还有人忘了除以255。任何一处不一致都会让统计出的激活分布失真,而这些错误往往不会报错,只会在最终的精度评估时才暴露出来。
校准方法的选择:MinMax、KL散度与Percentile
拿到校准数据后,量化框架会提供多种校准算法,它们的差异在于如何从激活分布推导截断范围。
MinMax校准直接取观测到的最大最小值作为量化边界,优点是不丢任何观测值,缺点是对离群点极其敏感。一张高对比度图片产生的极端激活值,会把整个scale撑大,导致常规数值的分辨率下降。适合激活分布均匀、无明显离群点的模型。
KL散度校准(也叫熵校准,TensorRT采用的方案)寻找一个截断阈值,使截断后量化分布与原始分布的KL散度最小。它允许丢弃少量离群点,换取整体量化误差的降低,对大多数视觉模型效果稳定。Percentile校准则直接按百分位截断,比如取99.9%分位数作为上界,思路简单,效果介于两者之间。
实际操作建议:先用KL散度跑基线,如果掉点明显,逐一层的权重直方图,观察哪些层有明显长尾。对离群点严重的层(通常靠近输出端)单独用Percentile调整,对分布集中的层保持MinMax。多数量化框架都支持per-layer的校准策略配置。
量化掉点的诊断与进阶优化
如果认真做了校准还是掉点,就需要系统地诊断了。第一步做逐层敏感度分析:把模型一层层还原成FP32,看哪一层还原后精度恢复最多,那层就是敏感层。对敏感层可以采用混合精度策略,保留FP16或INT16,其余层用INT8,通常能把掉点压到0.3%以内。
第二步考虑难例补充。用FP32模型在校准集和验证集上分别推理,挑出预测差异大的样本类别,往校准集里定向补充这类样本。比如检测模型对小目标漏检严重,就多加小目标图片;对夜间场景误检,就补充夜拍数据。这种针对性校准往往比盲目扩大校准集有效得多。
最后,如果PTQ的上限确实达不到要求,就转向量化感知训练(QAT)。但实践中绝大多数视觉模型,只要校准数据分布正确、预处理一致、校准方法选对,INT8掉点都能控制在0.5%以内。与其急着上QAT,不如先回头检查一遍校准集,多数时候问题就藏在那几百张图里。