量化-03-GPTQ

量化-03-GPTQ
gogongxtGPTQ
上文提到,OBQ 的致命痛点在于 “行依赖(Row-wise dependency)”
由于采用了贪心策略,权重矩阵每一行选出的最优量化顺序都不一样,导致
Hessian 逆矩阵
GPTQ (Generative Pre-trained Transformer Quantization)
正是为了解决这个工程灾难。它在 OBQ
的数学基础上做了极其巧妙的近似和系统级优化,将复杂度成功降到了
GPTQ 主要做了以下四大核心优化
优化一:统一量化顺序
OBQ使用贪心选择量化顺序,实验表明这对LLM带来的收益微乎其微。
GPTQ
团队通过实验和理论发现:如果不去贪心地寻找每次误差最小的权重,而是强行规定对所有行使用相同的、固定的顺序(例如从左到右,按列
这一步为什么是破局的关键?
回顾一下 Hessian 矩阵的定义:
你会发现,
因此,对于权重矩阵的每一行,初始的
在 OBQ 中,因为每行的量化顺序
但在 GPTQ 中,既然规定了所有行的量化顺序完全一致(比如先量化第 1
列,再第 2 列),那么所有行在每一步所面临的
推导结果:
我们只需要在整个层面上,维护和更新唯一一个
优化二:Cholesky 分解重构
在固定了顺序之后,我们再来看看
GPTQ 发现了一个数学等价性:这种每次减去一个由某列外积构成的秩
1 矩阵的操作,在数学上完全等价于对
如果在开始量化之前,我们对初始的 Hessian 逆矩阵做 Cholesky 分解(因为加入阻尼后它是正定对称矩阵):
具体来说,在第
优化三:工程优化:延迟批量更新
虽然数学上的计算量降下来了,但工程实现上还有内存带宽瓶颈。
如果按照列
GPTQ 提出了 Lazy Batch-Updates 策略。
步骤推导:
将权重矩阵的列划分成大小为
的块(Block),比如 。块内处理(贪心保留):在当前 Block 内部,依然按照列的顺序逐个量化。
对于 Block 内的第
列,量化产生误差 。我们利用预处理好的 Cholesky 信息,算出它对应的更新向量
。此时,只更新当前 Block 内部右侧尚未量化的列,而不去碰 Block 外面的庞大权重。
块间更新(批量计算):
当这 128 列(一个 Block)全部量化完毕后,我们将这 128 个误差向量拼成一个误差矩阵
,将对应的 128 个更新向量拼成一个矩阵 。对于 Block 右侧所有极其庞大的剩余权重
,使用一次密集的矩阵乘法进行全局更新:
通过这种batch更新,将大量零碎的内存读写转化为了一次高效的 GEMM 计算,加快计算速度。
优化四:加入 Hessian 阻尼
在实际的大模型校准中,用来计算 Hessian
矩阵的样本数往往远小于特征维度(比如仅仅用 128 条样本来校准
此时
GPTQ 引入了一个工程上非常经典的操作:Hessian Damping。
在求逆之前,强制给对角线元素加上一个与对角线均值成比例的常数:
这里
这个操作在稍微牺牲了一点点理论最优性的前提下,换来了极佳的数值稳定性,保证了后续 Cholesky 分解和求逆的顺利进行。
GPTQ 最终计算流程总结
我们来串一下,当拿到一个大模型的一层权重
收集数据:用少量校准数据通过该层,得到输入激活
。计算并求逆:
计算
。加上阻尼:
。求逆(或直接进行 Cholesky 分解准备更新向量)。
分块执行量化(Lazy Update Loop):
将所有的列分成大小为
的 Block。对每一个 Block:
- (a) 在 Block 内部逐列进行量化。
- (b) 计算该列量化带来的误差
,并根据 的信息仅更新当前 Block 内部的后续列。 - (c) 记录下该 Block 的所有更新指令。
- (d) Block 处理结束后,调用 GPU 执行一次 GEMM
操作,将累积的误差一次性补偿给
中位于该 Block 右侧的所有剩余权重。
结束:全部 Block 遍历完毕,输出量化后的权重
。
总结
OBQ
是理想主义的推导,它告诉了我们如何补偿误差能做到极限最优,但受困于行依赖导致的
GPTQ 是工程与数学结合的典范,它通过统一量化顺序打破了行依赖,通过 Cholesky 揭示数学本质减少了迭代误差,再通过 Lazy Batch-Updates 迎合了 GPU 的底层硬件特性。
最终,它将复杂度降到了真正的实用级别,这也是为什么如今开源社区(如 AutoGPTQ)几乎全都是基于 GPTQ 变体的核心原因。




