本质
- 将原数值区间缩放映射到新的精度所在的区间
核心思想
其中:
- :原始浮点值
- :量化后的整数值
- :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
- 模拟量化:
- 前向传播:x_fake = dequant(quant(x)),由 round() 引入误差
- 反向传播:梯度照常流,用 STE 直通估计,直接将梯度定义为
相当于梯度值不变,直接传过去。
- 推理时将量化与反量化分离,是真量化。
疑问
- 这里相当于是固定 S,Z(由校准数据预先算好),然后让权重去适应固定的量化格,相当于给权重设置了一个固定的筛子,让权重自己挤进去,而不是让筛子本身也跟着学
- LSQ (Learned Step Size Quantization) 就是在做这件事:将 S 作为可学习参数,同样使用 STE 穿透
- 但是 Z 的处理较为麻烦,因为 Z 是离散的整数。主流有两种处理方式:
- 直接固定 Z,只学 S,用对称量化令 Z=0,直接绕开问题
- Z 也学,对 Z 也用 STE,把它当成连续变量处理,反向传播时假装可导