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计算是模拟的,速度反而更慢。
我个人习惯,在部署前先做一轮“量化可行性分析”:
- 用校准集跑一遍PTQ,看精度损失是否在可接受范围内。
- 如果精度损失大,尝试逐通道量化或混合精度(敏感层保留FP16,其他层用INT8)。
- 如果还不行,上QAT。
- 最后,在目标硬件上实测延迟,别只看理论值。
总结一下:量化是延迟优化的“核武器”。FP16几乎无痛,INT8需要花点心思,INT4则要谨慎。我建议从FP16开始,如果延迟还不达标,再考虑INT8。别一上来就上INT4——我曾经在一个语音模型上试过,精度掉了8个点,最后不得不回退到INT8。
嗯,量化这块内容就这些。下一章我们会聊到“算子融合与图优化”,这是另一个能显著降低延迟的技巧。到时候我会分享一个我在TensorRT上踩过的坑——保证让你印象深刻。