本质

  • 将原数值区间缩放映射到新的精度所在的区间

核心思想

其中:

  • :原始浮点值
  • :量化后的整数值
  • :scale,表示整数步长对应的浮点间隔
  • :zero-point,表示浮点数 对应的整数位置

量化公式

给定量化范围:

量化:

反量化:

量化方案

Asymmetric Quantization

适合激活值 activation,因为激活值分布通常不关于 0 对称。

设浮点范围为:

scale 为:

zero-point 为:

并进行裁剪:

量化:

反量化:

Symmetric Quantization

适合权重量化 weight,因为权重通常以 0 为中心近似对称分布。

设:

对于 signed int8,通常取:

scale 为:

zero-point 固定为:

量化:

反量化:

粒度

Per-Tensor Quantization

整个张量共享一个 scale 和 zero-point:

特点:

  • 实现简单
  • 计算开销小
  • 精度可能较低

Per-Channel Quantization

每个输出通道单独计算 scale 和 zero-point。

对于第 个通道:

量化:

反量化:

特点:

  • 更适合权重量化
  • 精度更高
  • 每个通道需要存储一个

Calibration

Min-Max Calibration

最简单的校准方式是直接使用张量的最大最小值:

然后计算:

Clipping Calibration

为了减少异常值 outlier 的影响,可以先选定截断范围:

然后:

scale 为:

zero-point 为:

量化误差

量化后的近似值为:

量化误差为:

均方误差为:

也可以通过搜索不同的 clipping 范围,使 MSE 最小:

矩阵乘法中的量化计算

给定线性层 / 矩阵乘法(W 为权重):

量化表示为:

因此:

展开后:

其中:

是浮点缩放因子。

括号内:

主要由整数乘法和整数加减完成:

  • -bit 整数乘法
  • 如果权重和量化参数固定,和权重相关的 zero-point 修正项可以尝试预计算;和 activation 相关的项通常仍依赖运行时输入
  • 特别地,在 symmetric quantization 中,,这一项直接消失:,如果 activation 也用对称量化,,那展开式进一步简化,只剩:

最后再加上输出 zero-point:

这是 -bit integer addition。

KV Cache 量化执行流程

Forward Pass -> 计算 K,V (FP16/BF16) -> 量化发生(存入之前)-> 存入 KV Cache(INT8) -> (等待下一个 token)-> 反量化发生(读出之后)-> 还原为 FP16/BF16 -> Attention 计算(必须是浮点)

训练与部署

PTQ: Post-Training Quantization

  • 流程:训好的 FP32 模型 -> 用少量校准数据统计各层数值分布 -> 确定每层的 S 和 Z -> 量化完毕,直接部署
  • 缺点:精度损失相对较大,尤其是低 bit 时

QAT: Quantization-Aware Training

  • 核心:在训练过程中模拟量化误差,让模型适应 PTQ
  • 模拟量化:
  1. 前向传播:x_fake = dequant(quant(x)),由 round() 引入误差
  2. 反向传播:梯度照常流,用 STE 直通估计,直接将梯度定义为

相当于梯度值不变,直接传过去。

  • 推理时将量化与反量化分离,是真量化。

疑问

  • 这里相当于是固定 S,Z(由校准数据预先算好),然后让权重去适应固定的量化格,相当于给权重设置了一个固定的筛子,让权重自己挤进去,而不是让筛子本身也跟着学
  • LSQ (Learned Step Size Quantization) 就是在做这件事:将 S 作为可学习参数,同样使用 STE 穿透
  • 但是 Z 的处理较为麻烦,因为 Z 是离散的整数。主流有两种处理方式:
  1. 直接固定 Z,只学 S,用对称量化令 Z=0,直接绕开问题
  2. Z 也学,对 Z 也用 STE,把它当成连续变量处理,反向传播时假装可导

Outlier Problem