第四章:量化技术——INT8/INT4量化、GPTQ、AWQ、量化感知训练、量化后的精度损失评估

量化,说白了就是给模型“减肥”。

一个70B的模型,FP16精度下光参数就要140GB显存。你想想看,普通显卡根本跑不动。我最早接触量化是在两年前,当时要给一个金融风控模型做端侧部署,模型太大,手机根本装不下。后来发现量化这招,直接把模型砍到原来的四分之一,效果还凑合。

嗯,今天我们就聊聊量化的那些事。

4.1 量化是什么?

量化就是把模型里的浮点数(比如FP32、FP16)变成整数(INT8、INT4)。

举个例子:一个权重是3.1415926,量化成INT8后可能变成125(假设缩放因子是0.025)。推理时再反量化回来,虽然精度有损失,但模型体积和推理速度都大幅提升。

我个人习惯把量化分为两类:

  • 训练后量化(PTQ):模型训练完再做量化,简单粗暴。
  • 量化感知训练(QAT):训练时就模拟量化误差,精度更高。

核心公式:

量化值 = round(浮点值 / 缩放因子) + 零点偏移

反量化:浮点值 = (量化值 - 零点偏移) × 缩放因子

4.2 INT8与INT4量化

INT8量化是目前最成熟的方案。每个参数从16位降到8位,模型体积直接减半。推理速度能快2-4倍。

INT4就更狠了,体积再砍一半。但精度损失也更大。我在项目中遇到过,用INT4量化一个BERT模型,下游任务F1值掉了3个点。后来改用混合精度——关键层用INT8,非关键层用INT4,才把损失控制在1%以内。

量化精度 模型体积缩减 推理加速比 精度损失(典型)
FP16→INT8 50% 2-4x 0.5%-1%
FP16→INT4 75% 4-8x 1%-3%
混合INT8/INT4 60%-70% 3-6x 0.5%-1.5%

注意:INT4量化对激活值敏感。如果激活值分布不均匀,量化误差会放大。我曾经踩过这个坑,模型输出全是乱码,排查了半天才发现是激活值范围没校准好。

4.3 GPTQ:基于最优脑损伤的量化

GPTQ是目前大模型量化的主流方法之一。它的思路很有意思——不是简单地把每个权重单独量化,而是考虑权重之间的相互影响。

说白了,就是找到那些“砍掉后对输出影响最小”的权重,优先量化它们。这有点像“最优脑损伤”的思想,每次剪掉一个权重,然后调整其他权重来补偿误差。

# GPTQ量化伪代码
def gptq_quantize(weights, calibration_data):
    # 1. 计算Hessian矩阵(二阶梯度)
    H = compute_hessian(weights, calibration_data)
    
    # 2. 逐列量化,并补偿误差
    for col in range(weights.shape[1]):
        # 找到当前列的最优量化值
        q_val = quantize(weights[:, col])
        
        # 计算量化误差
        error = weights[:, col] - dequantize(q_val)
        
        # 将误差分配到其他未量化的列
        weights[:, col+1:] -= error * H_inv[col, col+1:]
    
    return quantized_weights

我建议你在使用GPTQ时,校准数据要选好。最好用和下游任务分布一致的数据。我之前用通用语料做校准,结果在代码生成任务上精度掉了2%,换成代码语料后只掉了0.3%。

4.4 AWQ:激活值感知量化

AWQ是另一种主流方法。它和GPTQ的区别在于:GPTQ关注权重,AWQ关注激活值。

为什么关注激活值?因为激活值中有些通道特别“重要”——它们的值很大,对输出影响也大。AWQ的做法是:对重要通道保留更高精度,对不重要通道可以多砍一点。

我的经验:AWQ在视觉模型上表现更好,GPTQ在语言模型上更稳。但这不是绝对的,建议你两个都试试。我一般先用AWQ快速评估,再用GPTQ做最终优化。

4.5 量化感知训练(QAT)

QAT是在训练过程中模拟量化操作。模型会“学会”适应量化带来的误差。

具体做法是:在前向传播时插入伪量化节点(fake quantize),把权重和激活值先量化再反量化。这样反向传播时,梯度就能感知到量化误差。

# QAT中的伪量化操作
class FakeQuantize(torch.autograd.Function):
    @staticmethod
    def forward(ctx, x, scale, zero_point):
        # 量化
        q_x = torch.round(x / scale + zero_point)
        # 反量化(模拟量化误差)
        return (q_x - zero_point) * scale
    
    @staticmethod
    def backward(ctx, grad_output):
        # 直通估计器(STE):梯度直接通过
        return grad_output, None, None

嗯,这里要注意:QAT的训练时间会变长,因为每次前向都要做量化反量化。我建议你只在最后几个epoch开启QAT,或者只对关键层做QAT。

避坑指南:我曾经在QAT中用了太高的学习率,结果模型直接不收敛了。QAT的学习率一般要比正常训练小一个数量级,建议从1e-5开始试。

4.6 精度损失评估

量化完怎么知道效果好不好?不能光看模型体积小了,还得看精度。

我一般从三个维度评估:

  1. 任务指标:比如分类任务的准确率、生成任务的BLEU/ROUGE。这是最直接的。
  2. 输出分布:对比量化前后模型输出的概率分布。KL散度越小越好。
  3. 异常检测:看量化后有没有出现NaN、Inf等异常值。如果有,说明量化参数没设对。
评估维度 指标 可接受范围
任务指标 准确率/ROUGE等 下降不超过1%
输出分布 KL散度 < 0.01
异常检测 NaN/Inf比例 0%

我的建议:别只看一个指标。有时候任务指标没掉,但输出分布已经变了。这种模型上线后可能会出问题。我习惯把三个维度都跑一遍,确保万无一失。

4.7 知识体系图

下面这张图总结了量化的核心脉络:

量化技术知识体系 模型量化 训练后量化 (PTQ) 量化感知训练 (QAT) INT8/INT4 GPTQ AWQ 伪量化节点 精度损失评估 任务指标(准确率等) 输出分布(KL散度) 异常检测(NaN等) 量化技术核心脉络:从方法选择到效果评估

量化这条路,说白了就是“用精度换效率”。但怎么换、换多少、换完怎么检查,每一步都有讲究。我建议你从INT8开始试,如果精度损失在可接受范围内,再考虑INT4。别一上来就追求极致压缩,容易翻车。

最后说一句:量化不是银弹。如果你的模型本身精度就不高,量化后可能会更差。先确保原始模型够好,再考虑量化。


公众号:蓝海资料掘金营,微信deep3321