4、量化技术基础:线性量化与非线性量化、定点数与浮点数转换、量化误差分析

量化,说白了就是用更少的比特去表示一个数值。在模型压缩里,我们经常要把 32 位浮点数(FP32)的权重,变成 8 位甚至 4 位的整数。你想想看,存储一下子少了 4 倍到 8 倍,推理速度也能快不少。

但代价是什么?精度损失。我刚开始做量化的时候,总觉得不就是四舍五入嘛,能差多少?结果模型直接崩了。嗯,这里面的门道,咱们今天一条条捋清楚。

4.1 线性量化 vs 非线性量化

量化分两大类:线性量化和非线性量化。区别在于,数值映射到整数时,步长是不是均匀的。

4.1.1 线性量化

线性量化是最常用的方法。它假设原始浮点数的分布是均匀的,或者至少可以用均匀的步长去近似。公式很简单:

Q = round((r - zero_point) / scale)
r = Q * scale + zero_point

其中 r 是原始浮点数,Q 是量化后的整数,scale 是步长,zero_point 是零点偏移。

我在项目中遇到过一个问题:当权重分布特别不均匀时,比如大部分值集中在 0 附近,但有几个离群点特别大。这时候线性量化的步长会被那几个离群点拉大,导致中间区域的精度严重损失。模型精度直接掉了 5 个点。

核心要点:线性量化适合数据分布比较均匀的场景。如果数据有长尾或离群点,需要先做裁剪(clipping)或者改用非线性量化。

4.1.2 非线性量化

非线性量化,说白了就是步长不固定。常见的有对数量化、浮点数量化(如 FP8、BF16)。

对数量化的公式长这样:

Q = round(log2(|r|))
r = sign(r) * 2^Q

为什么用对数?因为很多神经网络权重的分布其实是对数正态的——小值特别多,大值特别少。用对数步长,小值区域量化得更精细,大值区域粗糙一点,整体误差反而更小。

我记得有一次做语音模型的量化,权重分布跨度特别大,从 1e-6 到 10 都有。线性量化直接崩了,换成对数量化后,精度只掉了 0.3%。

特性 线性量化 非线性量化
步长 均匀 不均匀(如对数、指数)
硬件支持 广泛(CPU/GPU/NPU 都支持) 有限(需要特殊指令集)
适用分布 均匀分布 长尾分布、对数正态分布
实现复杂度 低 中高
精度损失 分布均匀时小 分布不均匀时更优

我的建议:优先尝试线性量化。如果精度损失超过 1%,再考虑非线性量化。不要一上来就搞复杂的,工程上简单就是美。

4.2 定点数与浮点数转换

量化本质上就是浮点数到定点数的转换。但这里有个坑:定点数不是整数,它有一个隐含的小数点位置。

4.2.1 定点数的表示

定点数用 Qm.n 格式表示,其中 m 是整数位宽,n 是小数位宽。比如 Q4.3 表示总共 7 位,其中 4 位整数,3 位小数。

// Q4.3 格式示例
// 二进制: 0101 100
// 整数部分: 0101 = 5
// 小数部分: 100 = 0.5
// 实际值: 5.5

浮点数转定点数的步骤:

  1. 确定位宽和小数点位置(Qm.n)
  2. 计算缩放因子:scale = 2^n
  3. 量化:Q = round(r * scale)
  4. 反量化:r_hat = Q / scale

注意:定点数有溢出风险。如果浮点数太大,乘以 2^n 后可能超出整数位宽。我曾经在 DSP 上吃过这个亏,一个乘法结果直接溢出变成负数,排查了半天。

4.2.2 浮点数转定点数的工程实践

在实际项目中,我习惯用以下流程:

1. 统计权重/激活值的范围 [min, max]
2. 选择位宽(通常 8 位或 16 位)
3. 计算小数位宽 n = floor(log2((max - min) / (2^bitwidth)))
4. 如果 n < 0,说明需要先做缩放或裁剪
5. 生成量化表或直接做 round 操作

你想想看,如果权重范围是 [-1.0, 1.0],用 8 位定点数,小数位宽可以设到 7 位,精度相当高。但如果范围是 [-128, 128],小数位宽只能设 0 位,精度就差了。

4.3 量化误差分析

量化误差是绕不开的话题。我见过太多人量化完模型,精度掉了也不分析原因,直接放弃。其实误差来源就那么几种,对症下药就好。

4.3.1 误差来源

  • 舍入误差:round 操作带来的,不可避免,但可以控制范围
  • 截断误差:超出量化范围的值被截断,这是最大的误差来源
  • 零点偏移误差:zero_point 选择不当导致的系统性偏移
  • 累积误差:多层量化后误差不断放大

4.3.2 误差度量指标

指标 公式 说明
MSE mean((r - r_hat)^2) 均方误差,最常用
MAE mean(|r - r_hat|) 平均误差,对离群点不敏感
SNR 10*log10(var(r)/var(e)) 信噪比,越高越好
MaxErr max(|r - r_hat|) 最大误差,关注最坏情况

经验之谈:我一般先看 MaxErr,如果某个权重的误差特别大,说明裁剪策略有问题。然后看 MSE,如果整体偏高,说明步长太大,需要增加位宽或改用非线性量化。

4.3.3 误差传播分析

量化误差会随着网络层数加深而放大。我做过一个实验:

层数 1: 量化误差 MSE = 0.001
层数 5: 量化误差 MSE = 0.015
层数 10: 量化误差 MSE = 0.12
层数 20: 量化误差 MSE = 0.89  // 模型已经崩了

为什么会这样?因为每一层的输出误差会作为下一层的输入误差,加上该层自身的量化误差,像滚雪球一样越来越大。

解决办法有两个:

  • 逐层校准:每一层单独做量化校准,而不是全局统一 scale
  • 误差补偿:在量化后做一次 fine-tuning,让模型适应量化后的权重分布

避坑指南:我曾经在一个 50 层的 ResNet 上做量化,直接全局量化后精度掉了 8%。后来改成逐层校准,精度只掉了 1.2%。记住,层数越深,校准越要精细。

4.4 知识体系总览

下面这张图把本章的核心逻辑串起来了。你可以看到量化技术从分类到实现再到误差分析的完整链路。

量化技术知识体系 量化技术 线性量化 非线性量化 定点/浮点转换 均匀步长 零点偏移 对数量化 浮点数量化 Qm.n 格式 缩放与溢出 量化误差分析 舍入误差 | 截断误差 | 零点偏移 | 累积误差 工程实践:逐层校准 / 误差补偿 / Fine-tuning 核心原则:量化不是简单的四舍五入,而是精度、速度、存储的三角权衡

从这张图可以看得很清楚:量化技术从线性/非线性量化出发,经过定点/浮点转换,最终都要落到误差分析上。而误差分析的结果,又会指导我们做逐层校准和误差补偿。这是一个闭环。

好了,这一章的内容就到这里。量化看起来简单,但真正做好需要大量的实验和调优。记住我前面说的那些坑,能帮你少走不少弯路。