1. 量化基础

大家好,欢迎来到这门课。我是你们这趟量化之旅的向导。

咱们开门见山。模型量化,说白了,就是给神经网络模型「减负」。

你想想看,一个训练好的大模型,参数动辄几十亿上百亿。每个参数都是32位浮点数(FP32)。这玩意儿跑起来,显存占得满满当当,推理速度还慢。我刚开始接触这行的时候,也觉得FP32精度高,挺好。直到有一次,我要把一个视觉模型部署到一台只有4GB显存的边缘设备上。模型死活塞不进去,急得我直挠头。后来用了量化,模型体积直接缩到原来的四分之一,推理速度还翻了一倍。嗯,从那以后,我就再也不敢小看量化了。

1.1 什么是模型量化?

量化,就是把模型里那些高精度的参数(比如FP32),用更低精度的数据类型(比如INT8)来表示。

你可以把它想象成一张照片。FP32是超高清原图,细节丰富,但文件巨大。INT8就是一张压缩过的JPEG图,看着差不多,但文件小得多,加载也快得多。

核心思想:用更少的比特数,去近似表示原来的数值。目标是让模型「变轻、变快」,同时尽量不损失太多精度。

常见的量化目标类型有这些:

数据类型 比特数 表示范围 精度 常见用途
FP32 32 ~±3.4e38 高 训练、高精度推理
FP16 16 ~±6.5e4 中 混合精度训练、推理加速
INT8 8 [-128, 127] 或 [0, 255] 低 边缘端推理、移动端部署
INT4 4 [-8, 7] 或 [0, 15] 极低 极端压缩场景

我的习惯:在项目初期,我会先用INT8量化试试水。如果精度损失在可接受范围内(比如1%以内),那就直接用INT8。如果不行,再考虑FP16或者混合精度方案。别一上来就上INT4,那玩意儿坑多,后面章节我会细讲。

1.2 为什么需要量化?

原因很直接:现实世界不等人。

你训练出一个SOTA模型,精度99.5%,结果部署到手机上,一帧要跑5秒钟。用户早把App卸载了。量化就是为了解决这个「最后一公里」的问题。

具体来说,量化能带来三大好处:

  1. 模型体积变小:从FP32到INT8,体积直接缩到1/4。一个500MB的模型,量化后只有125MB。下载快,加载也快。
  2. 推理速度变快:INT8的计算在硬件上比FP32快得多。特别是现在的GPU、NPU、DSP,都对INT8做了专门优化。我在项目中实测过,量化后的模型在NVIDIA Jetson上推理速度能提升2-3倍。
  3. 功耗降低:计算快了,内存访问少了,功耗自然就降下来了。这对移动设备和IoT设备来说,是命门。

注意:量化不是免费的午餐。它一定会带来精度损失。我们的目标不是「零损失」,而是「在可接受的损失范围内,换取最大的速度提升」。我曾经见过一个团队,为了追求极致压缩,把模型量化到INT4,结果精度掉了8个点,模型直接废了。这就是典型的「捡了芝麻,丢了西瓜」。

1.3 量化的数学原理

好了,聊完了「是什么」和「为什么」,咱们来点硬核的——「怎么做」。

量化的数学原理,核心就一句话:找到一个映射,把高精度数值空间映射到低精度整数空间。

最常见的映射方式叫均匀量化。公式很简单:

r = S * (q - Z)

其中:

  • r 是原始的浮点数(FP32)
  • q 是量化后的整数(INT8)
  • S 是缩放因子(Scale),一个浮点数
  • Z 是零点偏移(Zero Point),一个整数

反过来,从浮点数到整数的量化过程是:

q = round(r / S) + Z

你可能会问:「这个S和Z怎么来的?」

答案是:通过统计原始数据的范围来确定的。

假设我们有一组FP32的权重,最小值是 r_min,最大值是 r_max。我们要把它们映射到INT8的范围 [q_min, q_max](比如 [-128, 127])。那么:

S = (r_max - r_min) / (q_max - q_min)
Z = round(q_min - r_min / S)

嗯,这里要注意:Z 必须是一个整数,而且要在 [q_min, q_max] 范围内。如果算出来超出范围,就截断。

关键点:量化过程一定会引入误差。这个误差叫「量化误差」,它等于 r - S * (q - Z)。我们的目标就是通过选择合适的 S 和 Z,让这个误差尽可能小。

为了让你更直观地理解这个过程,我画了一张图:

均匀量化映射示意图 FP32 空间 r_min r_max 连续值,范围大 量化 (Quantize) INT8 空间 -128 -64 0 64 127 离散值,范围小 反量化 (Dequantize) 量化误差 = 反量化后的值 - 原始值 S 越小,误差越小,但表示范围也越小

这张图很清楚地展示了:FP32空间里的一段连续范围,被「压缩」映射到了INT8空间里的一系列离散整数点上。每个整数点代表了一个小段浮点数范围。反量化时,我们只能得到一个近似值,这就是误差的来源。

1.4 量化误差的来源

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

  • 舍入误差:公式里的 round() 操作。比如 3.14 量化后变成 3,丢了 0.14。
  • 截断误差:当原始数值超出 [r_min, r_max] 范围时,会被强行截断。比如一个权重是 1.5,但我们的范围只到 1.0,那它就被截成 1.0 了。

避坑指南:我曾经在一个项目中,发现量化后的模型在某个特定输入下输出完全错误。排查了半天,发现是那个输入的某个特征值特别大,超出了量化范围,被截断了。从那以后,我养成了一个习惯:量化前一定要仔细分析数据的分布,特别是那些「离群点」。如果离群点太多,可以考虑用「非均匀量化」或者「逐通道量化」来缓解。这些后面都会讲到。

1.5 量化粒度

量化不是对整个模型一刀切。我们可以选择不同的粒度:

粒度 描述 优点 缺点
逐张量 (Per-Tensor) 整个张量共享一组 S 和 Z 实现简单,计算快 精度损失较大
逐通道 (Per-Channel) 每个通道独立计算 S 和 Z 精度损失小 计算量稍大
逐组 (Per-Group) 将通道分成若干组,每组独立量化 精度和速度的折中 实现复杂

我个人习惯是:对于权重,用逐通道量化;对于激活值,用逐张量量化。这样能在精度和速度之间取得一个不错的平衡。

好了,这一章的基础内容就到这里。量化不是魔法,它是一套严谨的数学工具。理解了这些基础,后面我们才能更好地驾驭它。