2、模型量化基础:量化的数学原理、定点数与浮点数、量化误差分析

2.1 为什么需要量化?

做边缘计算的朋友都知道,模型在服务器上跑得飞起,一放到嵌入式设备上就卡成PPT。原因很简单——服务器有强大的GPU和充足的内存,而边缘设备往往只有几MB的SRAM和几百MHz的MCU。

我最早接触量化是在一个智能门锁项目上。客户要求人脸识别延迟低于200ms,但模型有50MB,芯片只有4MB内存。当时我第一反应是「这不可能」,后来才发现量化就是专门解决这类问题的。

量化的核心思想很简单:用更少的比特数来表示模型的参数和中间结果。比如原本用32位浮点数,现在换成8位整数。这样一来,模型体积直接缩小4倍,推理速度也能提升好几倍。

量化带来的三大好处:

  • 模型体积缩小:从FP32到INT8,体积缩小75%
  • 推理速度提升:整数运算比浮点运算快得多,尤其在ARM Cortex-M这类芯片上
  • 功耗降低:更少的数据搬运,更简单的计算单元

2.2 浮点数与定点数——两种世界观

要理解量化,得先搞清楚浮点数和定点数到底有什么区别。说白了,它们就是两种表示实数的方式。

2.2.1 浮点数(FP32)

浮点数用科学计数法来存数字。一个32位浮点数由三部分组成:1位符号位、8位指数位、23位尾数位。

FP32 = (-1)^sign × 2^(exponent - 127) × (1 + mantissa)

举个例子,3.14这个数在FP32里长这样:

符号位: 0 (正数)
指数位: 10000000 (表示2^1)
尾数位: 10010001111010111000011 (表示1.57)
结果: 1 × 2^1 × 1.57 = 3.14

浮点数的好处是动态范围大,能表示从10^-38到10^38这么宽的范围。但代价是计算复杂,硬件实现成本高。

我的一点经验:在边缘设备上,浮点运算往往需要软件模拟,或者依赖FPU硬件。很多低端MCU根本没有FPU,跑浮点运算慢得让人抓狂。

2.2.2 定点数(INT8)

定点数就简单多了。它用固定的小数点位置来表示数字。比如Q7格式,1位符号位,7位小数位:

INT8 = 整数部分 × 2^(-7)

拿Q7格式来说,它能表示的范围是[-1, 0.9921875],精度是2^(-7) ≈ 0.0078。

定点数的好处是计算简单,硬件实现成本低。但问题是动态范围小,精度有限。你想想看,如果模型参数分布在[-10, 10]这个范围,用Q7格式根本装不下。

数据类型 比特数 动态范围 精度 硬件复杂度
FP32 32 ~10^76 ~10^-7
FP16 16 ~10^5 ~10^-3
INT8 8 256 1/256
INT4 4 16 1/16 极低

2.3 量化的数学原理

量化的本质就是做一个映射:把浮点数范围映射到整数范围。最常见的做法是线性量化。

2.3.1 线性量化公式

线性量化的核心是两个参数:缩放因子(scale)和零点(zero_point)。

量化: q = round(r / scale) + zero_point
反量化: r = (q - zero_point) × scale

其中:

  • r 是浮点数
  • q 是量化后的整数
  • scale 是缩放因子,决定每个整数步长代表多少浮点数
  • zero_point 是零点,保证浮点0能精确映射到整数

举个例子,假设我们要把[-1.0, 1.0]这个范围量化到INT8([-128, 127]):

scale = (1.0 - (-1.0)) / (127 - (-128)) = 2.0 / 255 ≈ 0.00784
zero_point = round(-(-1.0) / 0.00784) + (-128) = round(127.55) - 128 = 0

所以浮点数0.5量化后:
q = round(0.5 / 0.00784) + 0 = round(63.78) = 64

关键点:量化不是简单的截断,而是带舍入的映射。这个舍入操作就是量化误差的来源之一。

2.3.2 对称量化 vs 非对称量化

我刚开始做量化时,经常搞混这两种方式。后来发现其实很简单:

  • 对称量化:zero_point = 0,浮点范围关于0对称。适合ReLU这类激活函数后的值(都是正数)
  • 非对称量化:zero_point ≠ 0,可以更灵活地匹配数据分布。适合权重这类可能有正有负的参数

对称量化的好处是实现简单,计算时少一次减法。非对称量化则能更好地利用整数范围,减少量化误差。

2.4 量化误差分析

量化必然带来误差。我见过不少同学一看到精度下降就慌了,其实大可不必。关键是要搞清楚误差从哪里来,有多大影响。

2.4.1 误差来源

量化误差主要有三个来源:

  1. 舍入误差:浮点数映射到整数时,需要四舍五入。这个误差最大不超过半个scale
  2. 截断误差:超出量化范围的值会被截断到边界值。比如INT8范围是[-128, 127],浮点数200会被截断成127
  3. 精度损失:量化后的整数只能表示有限个数值,相邻两个量化值之间的浮点数无法区分

我曾经踩过的坑:有一次量化一个分类模型,精度从98%掉到了85%。排查了半天,发现是某个层的权重分布特别不均匀,大部分值集中在[-0.1, 0.1]之间,但有几个异常值达到了±5。结果量化时scale被拉得很大,导致大部分权重精度严重损失。

解决方案是:先做权重裁剪,把异常值限制在合理范围内,再重新量化。精度恢复到了96%。

2.4.2 量化误差的数学表达

假设原始浮点值为r,量化后的值为q,反量化后的值为r':

量化误差: e = r - r'
绝对误差: |e| ≤ scale / 2
相对误差: |e| / |r| ≤ scale / (2|r|)

从公式可以看出,当r很小时,相对误差会变得很大。这就是为什么量化对小数值特别不友好。

2.4.3 如何评估量化误差

我常用的评估方法有两种:

  • 逐层误差分析:对比量化前后每一层的输出,计算均方误差(MSE)或信噪比(SNR)
  • 端到端精度评估:直接跑验证集,看量化后的模型精度下降了多少

一般来说,如果逐层MSE小于10^-3,端到端精度下降不超过1%,这个量化方案就是可接受的。

我的建议:不要只看最终精度。有时候精度下降1%,但模型体积缩小了4倍,推理速度提升了3倍,这个trade-off是值得的。关键要看你的应用场景对精度有多敏感。

2.5 小结

量化说白了就是一场「精度换效率」的交易。浮点数给了我们无限接近真实值的可能,但代价是计算复杂、存储量大。定点数虽然精度有限,但胜在简单高效。

在实际项目中,我一般会先跑一遍FP32的模型作为baseline,然后尝试INT8量化。如果精度下降在可接受范围内,就直接用INT8。如果不行,再考虑混合精度或者更复杂的量化方案。

下一章我们会聊具体的量化方法——从最简单的MinMax量化到更高级的KL散度量化。到时候我会分享一些实际项目中的调参经验,保证让你少走弯路。