PTQ基础入门:Post-Training Quantization概念、Calibration数据集的选择、静态量化与动态量化的区别

大家好,我是你们的老朋友。今天咱们来聊聊模型量化里最基础、也最实用的一块——PTQ,也就是训练后量化。

说实话,我刚开始接触量化的时候,觉得这东西挺玄乎的。明明模型跑得好好的,为什么要动它?后来在项目里被逼着给模型瘦身,才真正体会到量化的价值。PTQ作为入门第一课,咱们得把概念吃透。

PTQ 训练后量化 核心概念 FP32 → INT8 精度损失 vs 速度提升 Calibration 数据集 代表性数据 数据量:100-500张 量化方式 静态量化 权重+激活预计算 动态量化 权重固定,激活在线 选择策略:精度优先用静态,部署灵活用动态

什么是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的基础概念就聊到这儿。记住,量化不是银弹,但它确实是目前模型部署中最实用的优化手段之一。下一节咱们会深入讲量化参数的计算方法,到时候再细聊。


专注资料整理