首页 / PyTorch 入门教程 / 量化:PTQ 与 QAT

PyTorch 入门教程

量化:PTQ 与 QAT

本教程共 60 篇 · 第 53 篇 · 更新于 2026-08-17 · 约 4 分钟阅读

PyTorch量化PTQQATINT8FP8推理加速

本节目标:搞懂量化在做什么,分清 PTQ 和 QAT 两条路线,会写动态量化的最小代码,知道什么时候该选哪条路。

量化在做什么

模型训练和推理默认用 32 位浮点(FP32)。每个数占 4 字节,算得准,但慢、占内存。量化(Quantization)就是把权重和激活值从 FP32 换成低精度表示,最常见的是 8 位整数(INT8),只占 1 字节。

打个比方。一张照片原图几十 MB,压成 JPEG 可能就几 MB。肉眼看差别不大,文件小了一大截。量化就是这个思路:把连续浮点数映射到有限的整数格子上。

具体公式很简单:

q = round(x / scale) + zero_point

scale 是缩放因子,决定每个整数格子代表多大范围;zero_point 是零点偏移,让整数 0 能对应回原来的浮点 0。反推回来就是 x ≈ (q - zero_point) * scale

Note

映射分两种。对称量化(symmetric)不设零点偏移,适合权重这种正负均匀的分布;非对称量化(asymmetric)带 zero_point,适合激活值这种往往偏正数的分布。

收益很直接:INT8 体积是 FP32 的四分之一,计算量更小,还能利用硬件里的 INT8 加速单元。代价是精度损失,损失多少取决于你怎么量化。

为什么压这么狠还能保住精度?两个原因。一是神经网络的权重数值范围其实很集中,大部分权重落在零点附近的小区间里,量化格子能覆盖住;二是模型本身有冗余,个别数值的轻微失真,后续层能「消化」掉。好比把 24 位色深的照片存成 8 位,肉眼几乎分不出来。

量化粒度上还有一层选择:per-tensor 是整张权重共用一个 scale,per-channel 是每个输出通道各用各的 scale。后者精度更好,是现在的默认倾向,代价是稍微多存一点元数据。

算一笔账:ResNet-18 的权重约 44 MB(FP32),压成 INT8 只剩 11 MB。对部署在手机、边缘设备上的模型来说,这直接决定了能不能塞进去。

PTQ:训练后量化

PTQ(Post-Training Quantization,训练后量化)最省事。模型已经训练好了,你直接拿过来压一遍,不需要碰训练流程。它分两种:

  • 动态量化(dynamic):只量化权重,激活值在前向时临时算。适合线性层、LSTM 这类层,一行代码就能跑。
  • 静态量化(static):权重和激活都量化。需要先拿一小批数据跑一遍模型,统计激活值的分布,这一步叫校准(calibration)。校准集不需要标签,几百张图就够。

动态量化的代码短得惊人:

import torch
import torch.nn as nn
from torch.ao.quantization import quantize_dynamic

class DemoNet(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(64, 128)
        self.fc2 = nn.Linear(128, 10)

    def forward(self, x):
        return self.fc2(torch.relu(self.fc1(x)))

model = DemoNet().eval()
q_model = quantize_dynamic(model, {nn.Linear}, dtype=torch.qint8)
print(q_model)

静态量化走 FX 图路径,四个步骤:准备、插入观察器、校准、转换。FX 先把模型抓成计算图,再在图上插入观察器(observer)——校准阶段它只负责记录每层激活的数值范围,不改变任何行为。校准集不需要标签,但要能代表真实数据的分布,几百张到一千张样本足够。

from torch.ao.quantization import QConfigMapping
from torch.ao.quantization.quantize_fx import prepare_fx, convert_fx

qconfig = QConfigMapping().set_global(
    torch.ao.quantization.get_default_qconfig("x86")
)
prepared = prepare_fx(model, qconfig, example_inputs)
for x in calib_loader:          # 校准:统计激活范围
    prepared(x)
q_model = convert_fx(prepared)  # 真正换成 INT8 算子
Tip

先跑一段评估,量化后再跑同一段,对比精度。掉点能接受就用 PTQ,这是成本最低的路线。模型越大,PTQ 掉点往往越明显,大语言模型尤其敏感。

动态量化为什么「动态」?因为激活值不预先量化,每次前向时按当前 batch 的范围现算。省了校准这一步,代价是激活量化开销留在运行时。适合 LSTM、Transformer 的线性层这类算子,官方在服务器 CPU 推理里效果不错。

静态量化的激活是固定范围,推理时少一步计算,速度更快,但前提是校准集选得好。校准集选偏了,激活范围估计不准,量化误差会放大。这属于典型的「多花五分钟准备,换来长期稳定」的投入。

QAT:量化感知训练

QAT(Quantization-Aware Training,量化感知训练)思路反过来:把量化误差在训练时就暴露给模型,让它自己适应。

做法是在训练图里插入「伪量化」节点(fake quant)。前向时这些节点假装做了量化再反量化,数值上模拟 INT8 的误差,但梯度照样能反向传播。模型学着在误差存在的情况下把损失压下去。训练完再把伪节点换成真 INT8 算子,精度通常比 PTQ 好不少。

from torch.ao.quantization import QConfigMapping, get_default_qat_qconfig
from torch.ao.quantization.quantize_fx import prepare_qat_fx, convert_fx

qconfig = QConfigMapping().set_global(get_default_qat_qconfig("x86"))
model.train()
model = prepare_qat_fx(model, qconfig, example_inputs)
# 用正常训练循环再训几个 epoch
train(model)
model.eval()
q_model = convert_fx(model)

代价是你要重新训一遍,时间成本高。什么时候值得?PTQ 掉点太多、任务对精度敏感,或者模型要长期部署、值得为它花一次训练钱。

QAT 有几个实操细节。第一,训练时模型保持 train() 模式,伪量化节点的范围参数会跟着反向传播更新;最后转 INT8 前切到 eval()。第二,模型里带 BatchNorm 的话,QAT 会把 BN 先融合进卷积再量化,这个融合在准备阶段就做掉了,你不用管。第三,训练轮数不用多,通常几个 epoch 就能把精度拉回来,因为权重已经接近收敛,只是让模型「习惯」量化误差。

还有一种折中叫「PTQ 后微调」:先 PTQ 压完,再放开权重用低学习率微调几步。效果介于两者之间,成本也介于两者之间,值得一试。

生态工具怎么选

PyTorch 原生的 torch.ao.quantization 覆盖面最广,上面例子都基于它。新出的 TorchAO(pip install torchao)主打 LLM 的仅权重量化(weight-only quantization),INT8、INT4 都支持,和 torch.compile、FSDP2 配合得最好,HuggingFace 模型基本开箱即用。

Intel Neural Compressor(pip install neural-compressor-pt)适合 Intel 硬件。它复用了 PyTorch 的 prepare/convert 写法,多了个 autotune 自动调优:你给一个可容忍的精度损失阈值,它自动遍历量化配置,找到精度和速度的最优解。

from neural_compressor.torch.quantization import RTNConfig, TuningConfig, autotune

tune_config = TuningConfig(
    config_set=RTNConfig(use_sym=[False, True], group_size=[32, 128]),
    tolerable_loss=0.2,   # 精度损失容忍度
    max_trials=10,
)
q_model = autotune(model, tune_config=tune_config, eval_fn=eval_fn)

FP8 是低精度方向的新宠,比 INT8 精度好,比 FP16 快。NVIDIA H100 和 Intel Gaudi 都开始支持,PyTorch 2.x 里配合新硬件已经能用,但生态还在早期,初学者了解概念即可。

Note

本节的量化 API 以 PyTorch 2.13 为准。旧的 torch.quantization 入口已并入 torch.ao.quantization,写新代码请用后者。

一句话总结

先试 PTQ 动态量化,一分钟见效;不够再上静态量化加校准;还不行就 QAT 重训。部署到特定硬件时,看看厂商工具(Intel Neural Compressor、TensorRT 等)能不能白捡更多加速。量化这条路,收益是实打实的四倍体积压缩和倍级推理加速,值得在模型上线前花半天试试。