1. 量化误差的根源:为什么QAT中会有量化误差?从信息论角度理解量化噪声的数学本质

做量化感知训练(QAT)这些年,我见过太多同学一上来就调参、改代码,结果模型精度死活上不去。其实说白了,你不理解量化误差从哪来的,后面所有技巧都是瞎蒙。今天咱们就从根上把这事掰扯清楚。

1.1 量化到底在干什么?

先问个问题:一个32位浮点数,硬塞进8位整数里,会发生什么?

嗯,信息丢失。这是必然的。

量化本质上是一个映射函数

Q(x) = round(x / scale) + zero_point

其中 scale 是缩放因子,zero_point 是零点偏移。这个公式看着简单,但坑全藏在 round() 里。

核心观点:量化误差 = 信息论中的有损压缩误差。你丢掉的是浮点数中那些"细粒度"的表达能力。

1.2 从信息论看量化噪声

我记得刚入行时,导师跟我说过一句话:"量化就是给连续信号拍了一张低分辨率照片。" 这个比喻我一直记着。

从信息论角度,量化过程可以拆解为三步:

  1. 采样:把连续值域离散化成有限个区间
  2. 舍入:每个区间内的值被映射到同一个整数
  3. 反量化:整数再映射回浮点数,但信息已经丢了

量化噪声的数学本质,就是舍入误差。假设输入分布是均匀的,量化误差近似服从均匀分布:

误差 e = Q(x) - x
e ~ Uniform(-0.5 * scale, 0.5 * scale)

它的方差是:

Var(e) = scale² / 12

这个 scale²/12 就是量化噪声的功率。你想想看,scale 越大,噪声功率越大。这就是为什么量化精度跟数据范围直接挂钩

个人经验:我在项目中遇到过,有人把激活值范围设得特别大,结果量化后精度直接崩了。后来一查,scale 太大,噪声淹没了信号。所以做 QAT 第一步,先把数据范围校准好。

1.3 量化误差的两种类型

实际工程中,量化误差分两类,你得区分对待:

误差类型 产生原因 影响范围 QAT中如何处理
舍入误差 round() 操作导致的精度丢失 每个值都有,但小值影响更大 通过直通估计器(STE)近似梯度
截断误差 超出量化范围的值被截断 离群值(outliers) 动态调整量化范围或使用per-tensor量化

为什么会这样?因为舍入误差是均匀分布的,而截断误差是有偏的。截断误差一旦出现,模型输出就会系统性偏移,这比随机噪声更难处理。

避坑指南:我曾经在部署一个检测模型时,发现某个通道的激活值偶尔会飙到 100 以上,而大部分值都在 [-5, 5] 之间。结果量化范围一设,那些离群值全被截断了,模型直接输出 NaN。后来我改用 per-channel 量化,才把这个问题解决。

1.4 量化误差在神经网络中的传播

单层的量化误差可能不大,但别忘了,神经网络是深度堆叠的。误差会逐层累积、放大。

我画了一张图,帮你理解这个传播过程:

量化误差在神经网络中的传播路径 输入 x 量化 Q(x) 误差 e₁ 层1输出 量化 Q(·) 误差 e₂ 误差累积效应 第1层:输出 = f(W · Q(x) + e₁) 第2层:输出 = f(W · Q(第1层输出) + e₂) ... 第L层:输出 = f(W · Q(第L-1层输出) + eₗ) 关键问题: • 每层引入新的量化误差 eᵢ • 误差逐层累积,非线性放大 • 深层网络误差更大,更难收敛 • QAT 通过训练让网络"适应"误差

从这张图你能看到,每一层量化都会注入新的误差。这些误差不是简单相加,而是通过非线性激活函数(ReLU、Sigmoid等)被扭曲放大。这就是为什么深层网络量化起来比浅层网络难得多。

1.5 QAT 为什么能缓解量化误差?

你可能会问:既然量化误差不可避免,那 QAT 到底在干嘛?

说白了,QAT 的核心思想就一句话:让模型学会跟量化误差共存

具体来说:

  • 前向传播:使用量化后的权重和激活值进行计算(模拟部署环境)
  • 反向传播:使用直通估计器(STE)近似量化函数的梯度
  • 参数更新:在浮点精度下更新权重,但下次前向时再次量化

这个过程让模型逐渐"适应"了量化噪声。我习惯把 QAT 比作在噪声中训练——就像你在嘈杂的环境里练听力,时间长了自然能听清对方说什么。

数学本质:QAT 等价于在损失函数中引入了一个隐式的正则化项,这个正则化项惩罚那些对量化敏感的参数。所以 QAT 训练出来的模型,参数分布更鲁棒,对量化噪声不敏感。

1.6 量化误差的数学度量

最后,咱们用几个指标来量化误差大小。这些指标在 QAT 训练中经常用到:

指标 公式 含义
MSE E[(Q(x) - x)²] 量化误差的均方误差,越小越好
SNR 10·log₁₀(Var(x) / Var(e)) 信噪比,越大表示量化损失越小
KL散度 D_KL(P_float || P_quant) 量化前后分布的差异,用于校准

我个人习惯在 QAT 训练过程中,每 100 步打印一次 SNR。如果 SNR 低于 20dB,说明量化噪声太大了,需要调整量化范围或者增加位宽。

实用技巧:你可以用下面这段代码快速计算量化误差的 SNR:

def compute_snr(x, x_quant):
    signal_power = torch.var(x)
    noise_power = torch.var(x - x_quant)
    snr = 10 * torch.log10(signal_power / noise_power)
    return snr.item()

把这个函数加到你的训练循环里,能帮你实时监控量化质量。

小结

量化误差的根源,说白了就是信息丢失。从信息论角度看,量化是一个有损压缩过程,舍入和截断都会引入噪声。这些噪声在深度网络中逐层累积,最终影响模型精度。

QAT 的作用不是消除误差,而是让模型学会在噪声中生存。理解了这一点,后面那些具体的训练技巧(比如 STE 改进、范围校准、蒸馏等)你才能真正用对地方。


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