PTQ基础入门:Post-Training Quantization概念、Calibration数据集的选择、静态量化与动态量化的区别
大家好,我是你们的老朋友。今天咱们来聊聊模型量化里最基础、也最实用的一块——PTQ,也就是训练后量化。
说实话,我刚开始接触量化的时候,觉得这东西挺玄乎的。明明模型跑得好好的,为什么要动它?后来在项目里被逼着给模型瘦身,才真正体会到量化的价值。PTQ作为入门第一课,咱们得把概念吃透。
什么是PTQ?说白了就是给模型「减负」
PTQ,全称Post-Training Quantization,中文叫训练后量化。意思很直白——模型训练完了,我再动手脚。
你想想看,一个训练好的模型,参数都是FP32的浮点数。每个数占4个字节。如果我把它们变成INT8,每个数只占1个字节。模型体积直接缩到四分之一。推理速度呢?在支持INT8的硬件上,能快2-4倍。
我有个项目,模型原本50MB,部署到手机端死活装不下。做了PTQ之后,变成12MB,跑起来还快了不少。嗯,这就是量化的魅力。
核心思想:用更少的比特数来表示模型的权重和激活值,在尽量不损失精度的情况下,换取速度和体积的优化。
Calibration数据集——选对了事半功倍
做PTQ的时候,有个关键步骤叫Calibration(校准)。说白了,就是拿一小批数据跑一遍模型,看看每一层的激活值分布是什么样的。然后根据这个分布,决定怎么把浮点数映射到整数。
这里有个坑——数据集选不好,量化效果直接崩。
我个人习惯的做法是:
- 数据要有代表性:不能拿猫的图片去校准一个做人脸识别的模型。我见过有人图省事,随便拿了几张图做校准,结果模型量化后精度掉了5个点。
- 数据量不用太大:一般100到500张就够了。太多了反而浪费时间,因为校准过程本身也要跑一遍推理。
- 覆盖各种场景:如果模型要处理白天和夜晚的图片,校准集里两种都得有。
我的小技巧:从训练集里随机抽200张,再混20张没见过的验证集图片。这样既保证了代表性,又能防止过拟合到校准集上。
校准的时候,常用的方法有几种:
| 方法 | 原理 | 适用场景 |
|---|---|---|
| MinMax | 直接取激活值的最小最大值 | 分布均匀的情况 |
| Percentile | 去掉极端值,取某个百分位 | 有离群点时特别好用 |
| KL散度 | 让量化前后的分布差异最小 | 大多数场景,我默认用这个 |
我曾经在一个目标检测项目里,用MinMax做校准,结果因为有几个激活值特别大,导致整个量化范围被拉宽,精度掉得一塌糊涂。换成KL散度之后,问题就解决了。
静态量化 vs 动态量化——到底选哪个?
这两个概念,初学者特别容易搞混。我简单说说。
动态量化:权重在离线的时候就量化好了,但激活值是在推理的时候实时计算的。每次跑一个batch,都要算一下当前激活值的范围,然后做量化。
静态量化:权重和激活值的量化参数都在校准阶段算好了。推理的时候直接查表或者做整数运算,不需要再算范围。
你可能会问:「那动态量化不是更灵活吗?」
没错,动态量化确实更灵活,因为它能适应不同的输入。但代价是——每次推理都要多一步计算激活值范围的操作,这会拖慢速度。
我给大家列个对比:
| 对比项 | 静态量化 | 动态量化 |
|---|---|---|
| 权重量化 | 离线完成 | 离线完成 |
| 激活量化 | 离线校准,固定参数 | 在线计算,每次不同 |
| 推理速度 | 快(省去了计算步骤) | 中等(多了计算开销) |
| 精度 | 略低(固定范围可能不准) | 略高(自适应输入) |
| 部署难度 | 简单(参数固定) | 稍复杂(需要运行时支持) |
注意:不是所有硬件都支持动态量化。有些芯片只支持静态量化,部署前一定要查清楚目标平台的文档。
我个人在实际项目中的选择策略是这样的:
- 如果模型要部署在手机或嵌入式设备上,我优先用静态量化。因为速度快,而且参数固定,不容易出幺蛾子。
- 如果模型是跑在服务器上,对延迟要求没那么苛刻,但精度要求高,我会试试动态量化。
- 如果模型里有大量不同尺度的输入(比如多分辨率检测),动态量化往往表现更好。
举个代码例子,用PyTorch做动态量化其实很简单:
import torch
import torch.quantization as quant
# 加载一个预训练模型
model = torch.load('my_model.pth')
model.eval()
# 动态量化:只量化权重
quantized_model = quant.quantize_dynamic(
model,
{torch.nn.Linear, torch.nn.LSTM}, # 指定要量化的层类型
dtype=torch.qint8
)
# 跑推理
output = quantized_model(input_data)
静态量化稍微复杂一点,需要先做校准:
# 准备校准数据
calibration_data = load_calibration_dataset()
# 配置量化
model.qconfig = quant.get_default_qconfig('fbgemm')
quant.prepare(model, inplace=True)
# 校准:跑一遍数据
for data in calibration_data:
model(data)
# 转换模型
quant.convert(model, inplace=True)
# 现在model已经是静态量化后的了
output = model(input_data)
你看,代码量都不大。但背后的原理,咱们得心里有数。
一句话总结:动态量化适合精度敏感、输入变化大的场景;静态量化适合追求极致速度和部署简单的场景。
好了,PTQ的基础概念就聊到这儿。记住,量化不是银弹,但它确实是目前模型部署中最实用的优化手段之一。下一节咱们会深入讲量化参数的计算方法,到时候再细聊。