量化感知训练(QAT)入门

各位同学,今天我们来聊聊量化感知训练。说实话,很多刚入行的朋友会把QAT想得很神秘,觉得是什么黑科技。其实说白了,QAT就是让模型在训练过程中就学会适应低精度推理的一种方法。

我个人习惯把QAT比作「带着镣铐跳舞」。你想想看,模型本来在FP32的世界里自由自在,突然要把它塞进INT8的小盒子里,它肯定不适应。QAT就是提前告诉模型:嘿,你以后要在小盒子里生活,现在先练练。

QAT的核心原理

QAT的原理其实不复杂。我们在训练过程中插入一些特殊的节点——伪量化节点(Fake Quantize Nodes)。这些节点会模拟量化-反量化的过程,让模型的前向传播感受到量化带来的精度损失。

为什么叫「伪量化」?因为它只模拟,不真正做量化。具体来说:

  • 前向传播:输入先被量化到INT8,再反量化回FP32,然后继续计算
  • 反向传播:梯度直接跳过伪量化节点,用直通估计器(STE)来近似梯度

我在项目中遇到过一个问题:刚开始用QAT时,总觉得训练完的模型精度还不如直接PTQ。后来才发现,原来是学习率没调好。嗯,这里要注意,QAT的学习率一般要比正常训练小一个数量级。

核心公式:伪量化操作可以表示为

Q(x) = round(clamp(x / scale + zero_point, qmin, qmax))
DQ(x) = (Q(x) - zero_point) * scale

其中scale和zero_point就是量化参数,会在训练过程中不断更新。

在PyTorch中插入伪量化节点

PyTorch提供了非常方便的QAT工具。我个人推荐使用torch.quantization.QuantStubtorch.quantization.DeQuantStub来标记量化边界。

来看一个简单的例子:

import torch
import torch.nn as nn
import torch.quantization as quant

class QATModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.quant = quant.QuantStub()
        self.conv1 = nn.Conv2d(3, 64, 3)
        self.relu = nn.ReLU()
        self.dequant = quant.DeQuantStub()
    
    def forward(self, x):
        x = self.quant(x)      # 插入伪量化节点
        x = self.conv1(x)
        x = self.relu(x)
        x = self.dequant(x)    # 反量化
        return x

# 准备QAT
model = QATModel()
model.qconfig = quant.get_default_qat_qconfig('fbgemm')
quant.prepare_qat(model, inplace=True)

# 正常训练...
# 训练完成后转换
model.eval()
quant.convert(model, inplace=True)

这里有个坑,我曾经踩过:prepare_qatconvert的顺序不能搞反。先prepare_qat训练,再convert做实际量化。如果你先convert再训练,那伪量化节点就没了,QAT等于白做。

训练技巧与注意事项

QAT的训练技巧,我总结了几条实战经验:

技巧 说明 我的经验
学习率调整 QAT学习率通常为正常训练的1/10到1/100 我习惯用1e-4起步,观察loss曲线再微调
训练轮数 一般10-20个epoch就够,太多反而过拟合 有一次我跑了50个epoch,精度反而下降了
Batch Size 建议和正常训练保持一致 太小会导致量化参数估计不准
权重衰减 可以适当减小,避免量化后权重分布太集中 我一般设为正常训练的0.5倍

避坑指南:我曾经在QAT训练时忘记冻结BN层,结果模型精度直接崩了。QAT训练时,BN层应该保持训练模式(model.train()),但量化参数(scale/zero_point)需要冻结。PyTorch的prepare_qat会自动处理这些,但如果你自己写训练循环,一定要小心。

QAT的适用场景

不是所有模型都适合QAT。我个人觉得,以下场景用QAT效果最好:

  • 小模型:MobileNet、SqueezeNet这类轻量网络,PTQ掉点严重,QAT能挽回不少
  • 低比特量化:INT4甚至INT2量化,必须用QAT
  • 精度要求高:比如人脸识别、医疗影像,掉点0.5%都不能接受

反过来,如果你的模型很大(比如ResNet-152),或者量化位宽够宽(INT8),PTQ可能就够用了。没必要为了QAT而QAT,毕竟训练时间也是成本。

知识体系结构图

下面这张图展示了QAT的完整知识体系,我把它画成了流程图,方便你理解各个环节的关系:

量化感知训练(QAT)知识体系 QAT核心原理 伪量化节点 QuantStub / DeQuantStub 训练技巧 学习率 / 轮数 / BN处理 适用场景 小模型 / 低比特 / 高精度 前向:量化+反量化 反向:STE近似梯度 学习率调小10倍 10-20个epoch BN保持训练模式 MobileNet等轻量网络 INT4/INT2低比特量化 高精度要求场景 QAT = 训练时模拟量化 → 推理时真正量化 精度损失最小化,部署效果最大化

个人小建议:刚开始做QAT时,别急着上复杂模型。拿一个简单的分类网络练手,比如CIFAR-10上的ResNet-18。先跑通流程,再慢慢加难度。我当年就是这么过来的,踩过的坑现在都变成了经验。

好了,关于QAT入门就讲到这里。记住一句话:QAT不是万能的,但没有QAT是万万不能的。尤其是在嵌入式设备上,每一分精度都弥足珍贵。

专注资料整理