3、模型量化技术:FP32到FP16/INT8的转换原理

聊到推理延迟优化,量化是绕不开的一关。说白了,就是把模型从高精度“压缩”到低精度。我刚开始接触这块时,总觉得这像是一种“有损压缩”,心里有点抵触。但后来发现,只要方法得当,精度损失完全可以控制在可接受范围内,而延迟收益却是实打实的。

3.1 转换原理:从FP32到FP16/INT8

先说说基本原理。FP32是32位浮点数,FP16是16位,INT8是8位整数。你想想看,数据量直接砍半甚至砍到四分之一。这意味着什么?内存带宽压力小了,计算单元吞吐量高了。

FP32到FP16的转换相对简单。FP16的指数位从8位减到5位,尾数位从23位减到10位。嗯,这里要注意:FP16的动态范围比FP32小很多。我在项目中遇到过,有些模型的权重分布很广,直接转FP16会导致溢出或下溢。这时候就需要做“损失缩放”(Loss Scaling)——先把梯度放大,再在反向传播时缩小回来。

FP32到INT8的转换就复杂多了。INT8只有256个离散值,而FP32有约42亿个。怎么映射?核心是找到合适的“缩放因子”(Scale)和“零点”(Zero Point)。公式很简单:

// 量化公式
q = round(r / scale) + zero_point

// 反量化公式
r = (q - zero_point) * scale

其中r是原始浮点数,q是量化后的整数。scale和zero_point怎么选?我习惯用“最大最小值法”——统计整个张量的数值范围,然后映射到INT8的[-128, 127]或[0, 255]区间。

关键点:量化不是简单的截断。它需要校准数据集来统计激活值的分布。校准数据集的质量直接影响量化效果。我见过有人随便拿100张图做校准,结果模型精度掉了5个点——这就是典型的“校准集不具代表性”。

3.2 量化感知训练(QAT)与后训练量化(PTQ)

这两种方法,说白了就是“训练时考虑量化”和“训练后直接量化”。

后训练量化(PTQ)

PTQ是最简单粗暴的方式。模型训练完,直接拿校准集跑一遍,统计出每层的scale和zero_point,然后量化。优点是快,不需要重新训练。缺点是精度损失可能较大,尤其是对敏感层。

我记得有一次,一个BERT模型做PTQ,精度掉了2个点。排查后发现是LayerNorm层的输出分布太宽,INT8根本装不下。后来我改用“逐通道量化”(Per-Channel Quantization),给每个通道单独算scale,精度损失降到了0.3个点。

我的建议:如果模型对精度不敏感(比如分类任务),PTQ完全够用。但如果是检测、分割这类任务,或者模型本身就很“脆弱”,建议上QAT。

量化感知训练(QAT)

QAT的思路是在训练过程中模拟量化误差。具体做法是:在前向传播时插入“伪量化节点”(Fake Quantize Node),把权重和激活值先量化再反量化。这样模型在训练时就能“感知”到量化带来的误差,并主动调整参数来补偿。

# 伪量化操作示例(PyTorch风格)
class FakeQuantize(torch.nn.Module):
    def __init__(self, scale, zero_point):
        super().__init__()
        self.scale = scale
        self.zero_point = zero_point
    
    def forward(self, x):
        # 量化
        q = torch.round(x / self.scale) + self.zero_point
        # 反量化
        x_q = (q - self.zero_point) * self.scale
        return x_q

QAT的优点是精度损失极小,通常能控制在0.1-0.5个点以内。但代价是训练时间变长,而且需要修改训练代码。我曾经在一个YOLOv5项目里用QAT,训练时间增加了30%,但推理延迟从12ms降到了4ms,精度只掉了0.2个mAP——这笔账怎么算都划算。

避坑指南:QAT训练时,学习率要调小。我曾经用默认学习率跑QAT,结果模型直接发散。后来把学习率降到原来的1/10,才稳定下来。另外,QAT的batch size不能太小,否则统计出来的scale不稳定。

3.3 量化对延迟的影响

量化对延迟的影响,可以从三个维度看:

量化类型 理论加速比 实际加速比(我的经验) 精度损失
FP32 → FP16 2x 1.5x - 1.8x 几乎无损失
FP32 → INT8 4x 2.5x - 3.5x 0.5% - 2%
FP32 → INT4 8x 4x - 6x 1% - 5%

为什么实际加速比达不到理论值?原因有三:

  • 内存带宽瓶颈:有些模型是内存密集型,计算单元在等数据。量化后数据量小了,但内存带宽可能还是瓶颈。
  • 量化/反量化开销:每次计算前后都要做量化/反量化操作,这部分有额外开销。
  • 硬件支持:不是所有硬件都原生支持INT8计算。比如某些老款GPU,INT8计算是模拟的,速度反而更慢。

我个人习惯,在部署前先做一轮“量化可行性分析”:

  1. 用校准集跑一遍PTQ,看精度损失是否在可接受范围内。
  2. 如果精度损失大,尝试逐通道量化或混合精度(敏感层保留FP16,其他层用INT8)。
  3. 如果还不行,上QAT。
  4. 最后,在目标硬件上实测延迟,别只看理论值。

总结一下:量化是延迟优化的“核武器”。FP16几乎无痛,INT8需要花点心思,INT4则要谨慎。我建议从FP16开始,如果延迟还不达标,再考虑INT8。别一上来就上INT4——我曾经在一个语音模型上试过,精度掉了8个点,最后不得不回退到INT8。

嗯,量化这块内容就这些。下一章我们会聊到“算子融合与图优化”,这是另一个能显著降低延迟的技巧。到时候我会分享一个我在TensorRT上踩过的坑——保证让你印象深刻。