4、PTQ实战:使用PyTorch FX进行PTQ、使用TensorRT进行PTQ、使用ONNX Runtime进行PTQ

好,咱们直接进入正题。PTQ(训练后量化)是实际落地中最常用的手段。为什么?因为它快,不需要重新训练模型。你想想看,一个几百兆的大模型,跑一遍PTQ可能就几分钟,而QAT得折腾好几天。

我个人习惯是:先上PTQ,看看精度损失能不能接受。如果不行,再考虑QAT。今天咱们就聊聊三个主流框架的PTQ实战:PyTorch FX、TensorRT、ONNX Runtime。

核心要点:PTQ的本质是用少量校准数据,统计出每层激活值和权重的分布,然后找到最优的量化参数(scale和zero_point)。不同框架的实现细节不同,但底层逻辑是一样的。

4.1 用PyTorch FX做PTQ

PyTorch FX是PyTorch官方推出的量化工具。它通过符号跟踪(symbolic trace)把模型转换成一张计算图,然后在这张图上插入量化/反量化节点。

我记得第一次用FX时,最大的坑是模型必须符合FX的trace规则。如果你的模型里有动态控制流(比如if-else),FX会直接报错。嗯,这里要注意。

下面是一个完整的PTQ流程:

import torch
import torch.quantization as quant
from torch.quantization import get_default_qconfig
from torch.quantization.quantize_fx import prepare_fx, convert_fx

# 1. 准备模型
model = MyModel().eval()

# 2. 设置量化配置
qconfig = get_default_qconfig('fbgemm')  # x86用fbgemm,arm用qnnpack
model.qconfig = qconfig

# 3. 准备量化(插入观察器)
example_inputs = (torch.randn(1, 3, 224, 224),)
model_prepared = prepare_fx(model, qconfig, example_inputs)

# 4. 校准(喂入校准数据)
for batch in calib_dataloader:
    model_prepared(batch)

# 5. 转换为量化模型
model_quantized = convert_fx(model_prepared)

# 6. 推理
with torch.no_grad():
    output = model_quantized(test_input)

我的经验:校准数据一般用500-1000张就够了。我曾经试过用10000张,结果精度反而下降了。因为数据太多,统计出来的分布过于平滑,丢失了细节。

FX的优点是跟PyTorch生态无缝集成。缺点也很明显——对模型结构有要求。如果你的模型里有自定义算子,FX可能处理不了。

4.2 用TensorRT做PTQ

TensorRT是NVIDIA的推理优化库。它的PTQ走的是另一条路——用INT8校准器(calibrator)来统计激活值分布。

说白了,TensorRT的PTQ分两步:第一步,跑一遍校准数据,收集每层的激活值直方图;第二步,根据直方图选择最优的量化阈值。

我项目中遇到过最头疼的问题:TensorRT的INT8量化对某些层特别敏感,稍微量化一下精度就崩了。后来发现,可以手动指定哪些层跳过量化。

import tensorrt as trt

# 创建builder和config
builder = trt.Builder(TRT_LOGGER)
network = builder.create_network()
config = builder.create_builder_config()

# 设置INT8模式
config.set_flag(trt.BuilderFlag.INT8)

# 自定义校准器
class MyCalibrator(trt.IInt8EntropyCalibrator2):
    def __init__(self, calib_loader):
        trt.IInt8EntropyCalibrator2.__init__(self)
        self.calib_loader = calib_loader
        self.buffer = None
        
    def get_batch(self, names):
        try:
            data = next(self.calib_loader)
            self.buffer = np.ascontiguousarray(data)
            return [self.buffer]
        except StopIteration:
            return None

# 设置校准器
calibrator = MyCalibrator(calib_loader)
config.int8_calibrator = calibrator

# 构建引擎
engine = builder.build_serialized_network(network, config)

避坑指南:我曾经因为校准数据太少(只用了100张),导致TensorRT选错了量化阈值,模型输出全是噪声。后来我把校准数据增加到1000张,问题就解决了。校准数据一定要覆盖真实场景的分布。

TensorRT的INT8量化效果通常很好,尤其是对卷积层。但要注意,TensorRT只支持部分算子。如果你的模型里有不支持的算子,它会回退到FP32,影响加速效果。

4.3 用ONNX Runtime做PTQ

ONNX Runtime(ORT)是微软开源的推理引擎。它的PTQ支持两种模式:静态量化和动态量化。

静态量化需要校准数据,跟FX类似。动态量化不需要校准数据,但精度通常差一些。我个人习惯用静态量化,除非模型太大、校准数据不好找。

ORT的PTQ接口很简洁:

import onnx
from onnxruntime.quantization import quantize_static, QuantType
from onnxruntime.quantization import CalibrationMethod

# 加载ONNX模型
model_fp32 = 'model.onnx'
model_int8 = 'model_int8.onnx'

# 定义校准数据读取器
class MyDataReader:
    def __init__(self, dataloader):
        self.dataloader = dataloader
        self.iter = iter(dataloader)
    
    def get_next(self):
        try:
            data = next(self.iter)
            return {'input': data.numpy()}
        except StopIteration:
            return None

# 执行静态量化
quantize_static(
    model_input=model_fp32,
    model_output=model_int8,
    calibration_data_reader=MyDataReader(calib_loader),
    quant_format=QuantType.QInt8,
    per_channel=True,
    calibration_method=CalibrationMethod.MinMax
)

小技巧:ORT的per_channel量化对精度提升很明显。我对比过,per_channel比per_tensor的精度高0.5-1%。代价是推理速度稍微慢一点,但通常可以接受。

ORT的优点是跨平台支持好,Windows、Linux、ARM都能跑。缺点是对某些复杂模型的支持不如TensorRT成熟。

4.4 三个框架的对比

你可能会问:到底该选哪个?我个人的建议是:

框架 适用场景 精度 速度 易用性
PyTorch FX PyTorch生态、快速验证 中等 中等 高(需模型兼容)
TensorRT NVIDIA GPU、生产部署 最高 中(需转ONNX)
ONNX Runtime 跨平台、多后端 中等

说白了,如果你在NVIDIA GPU上部署,TensorRT是首选。如果你需要跨平台,ORT更合适。如果你只是快速验证PTQ效果,FX最方便。

4.5 PTQ精度调优的通用技巧

不管用哪个框架,PTQ精度调优的思路是相通的。我总结了几条经验:

  • 校准数据要够多、够多样——500-1000张,覆盖各种场景
  • 逐层分析精度损失——找出哪些层量化后损失最大,单独处理
  • 尝试不同的校准方法——MinMax、Entropy、Percentile,效果差别很大
  • 考虑per_channel量化——对权重用per_channel,精度提升明显
  • 敏感层跳过量化——如果某层量化后精度崩了,就让它跑FP32

注意:PTQ不是万能的。如果你的模型精度损失超过2%,建议考虑QAT。我曾经在一个检测模型上,PTQ损失了3%的mAP,换成QAT后只损失了0.5%。

好了,PTQ实战就聊到这里。三个框架各有千秋,关键是根据你的场景选对工具。记住:PTQ是量化的第一步,也是性价比最高的一步。

PTQ实战框架对比与流程 FP32预训练模型 校准数据(500-1000张) PyTorch FX 符号跟踪 + 观察器 TensorRT INT8校准器 + 直方图 ONNX Runtime 静态/动态量化 INT8量化模型 精度调优:校准数据 → 逐层分析 → 敏感层跳过

专注资料整理