量化基础:浮点数与定点数、量化误差、均匀量化与非均匀量化

量化,说白了就是把连续的值掰成离散的台阶。我第一次在嵌入式设备上跑量化模型时,以为这只是个简单的精度取舍,结果被功耗和性能的现实狠狠教训了一顿。嗯,从那天起,这块内容我就没敢再跳过。

为什么要量化?

深度学习模型训练时,我们通常用单精度浮点(FP32)。每个参数占 32 位。一个 GPT-3 级别的模型,光存参数就得几百 GB 显存,这还没算中间激活值。

  • 模型体积:FP32 的参数存成 INT8,体积直接缩到四分之一。
  • 推理速度:整数运算比浮点运算快得多,尤其在 GPU 张量核心和移动端 NPU 上。
  • 能耗控制:整数乘加器比浮点乘加器省电。数据中心里,每一瓦电都算成本。

量化就是用一点点精度损失,换取推理效率的大幅提升。

浮点数与定点数

这两个概念,是我在调校第一个量化模型时花时间最多的部分。

浮点数,比如 FP32,由三部分组成:符号位、指数、尾数。它能表示从 1.4e-45 到 3.4e38 这么大的范围,但每个区间内能表示的数值密度不同。靠近零的地方,表示得很密;离零越远,表示得越稀疏。

定点数,比如 INT8,就简单多了。它只有一个符号位和一个整数位。数值范围固定(比如 -128 到 127),步长是固定的 1。它不会浪费位数在指数上,所以同等位宽下,定点数的存储效率和计算效率都更高。

我建议这样理解:浮点数是“可伸缩的尺子”——精度随距离变化;定点数是“固定刻度的尺子”——精度在全范围一致。

浮点与定点对比
特性 FP32 INT8
位宽 32 8
数值分布 非均匀,靠近零更密 均匀
资源消耗 高(乘法器面积大) 低(乘法器面积小)
推理延迟 高 低
在 K-quant 中 主要用于校准参考 最常见的量化目标
核心洞察:量化的本质就是找到一个映射函数,把浮点数的分布映射到定点数的离散台阶上。这个映射的质量,决定了你的模型精度还剩多少。

量化误差

量化一定会引入误差。误差的来源主要有两个:

  • 截断误差:浮点数超出定点数表示范围的部分会被截断。比如你想用 INT8 表示一个 300 的数,它就超出了 -128 到 127 的范围,结果直接饱和成 127。这在数学上叫 clipping。
  • 舍入误差:浮点数落在两个整数台阶之间的部分,被近似到最近的整数值。同样的数值范围,台阶越少,舍入误差越大。
我曾经在量化一个 NLP 模型时,忽略了激活值中的极端异常点。结果这些异常值被饱和截断后,整个注意力机制的输出全部崩溃。后来我才明白,量化的第一步不是选位宽,而是先了解你的数据分布,尤其是两端的长尾。

量化误差怎么管理?很简单,缩短台阶间距——要么拓宽表示范围,要么增加台阶数量。但位宽是固定的,所以关键就在于怎么把有限的台阶放在最有价值的位置上。

均匀量化与非均匀量化

这两种方式,在 K-quant 框架里我都会结合着用。它们没有绝对的好坏,关键看数据。

均匀量化:台阶在整个范围上等间距排列。INT8 就是典型的均匀量化。实现起来最快,GPU 张量核心对 INT8 也最友好。但问题是,如果数据分布很集中,中间区域分辨率就不够;如果数据分布很稀疏,两端区域又浪费台阶。

非均匀量化:台阶不等距。在数据密集区用小台阶,在数据稀疏区用大台阶。举个常见做法:对数量化。用对数把大值压缩,小值拉伸。这样既能处理很宽的动态范围,又能保证小值区域的精度。在 K-quant 中,我经常对权重用均匀量化,对激活值用非均匀量化,因为激活值往往有长尾。

均匀 vs 非均匀量化
维度 均匀量化 非均匀量化
实现难度 低(硬件支持好) 高(查表或分段计算)
分布适应性 弱 强
典型场景 权重、卷积核 激活值、Embedding
精度保留 数据均匀时最好 数据长尾或尖峰时更好
K-quant 倾向 默认对称量化 动态位宽分配
个人习惯:我一般先用均匀量化做第一次校准。如果发现某个层的输出误差特别大,再针对这一层换成非均匀量化。这样能快速定位瓶颈,避免对所有层都做复杂的非均匀处理。

说到非均匀量化,还有一个常见思路叫“二进制编码”或“权重共享”。你想想看,如果权重的分布呈现多个峰值,每个峰值附近用一组相同的量化值去表示,那就相当于对不同的峰值区间做了不同的均匀量化。这样做,本质上也是非均匀的。在 K-quant 框架里,这种灵活性非常重要。

下面的图把这个量化过程的核心逻辑做了整理:

量化过程核心逻辑 量化过程核心逻辑 原始高精度数据 FP32 / BF16 权重、激活值 异常值裁剪 截断超出范围的极端值 映射变换 浮点数 → 整数刻度(缩放 + 零点偏移) 量化编码 均匀量化的整数;非均匀量化的分段/查表 存储 / 计算 INT8 矩阵乘法、查表运算 解码 / 恢复 逆变换回近似的浮点数 ⚠ 精度损失点 截断 → 溢出误差 舍入 → 分辨率不足 均匀/非均匀选择

从这张图你能看到,量化的核心不是简单截断,而是一整套映射、编码、解码的流程。K-quant 框架里的很多参数调优,都围绕着这幅图的每一步展开:

  • 要不要做异常值裁剪?裁多大比例?
  • 映射是线性还是对数?零点偏移如何设置?
  • 编码时选均匀还是非均匀?非均匀的量化区间怎么划分?
一句话总结:量化是精度与效率之间的博弈。均匀量化简单高效但指标粗放;非均匀量化灵活精细但实现复杂。在 K-quant 里,我需要做的就是在复杂度和精度之间找到那个最经济的平衡点。说白了,就是搞清楚你的数据到底长什么样,然后决定怎么给它“配尺子”。