AWQ,GPTQ,GGUF量化
AWQ
AWQ,全称Activation-Aware Weight Quantization(激活感知权重量化)。
AWQ量化的核心思想就是,不是所有的权重都同等重要。在神经网络中,只有极少数(大约 $1\%$)的权重对模型的性能起到了决定性的作用。如果你把这些显要权重也进行粗暴的4-bit量化,模型的智力会迅速崩塌。
那么,如何定义哪些权重是“显要”的呢?AWQ 并没有看权重本身的大小,而是去看激活值。逻辑很简单:如果某个通道输入的信号特别强,那么与之相乘的权重即使只有微小的量化误差,也会在输出端被放大成巨大的偏离。因此,AWQ 的核心就在于通过观察模型推理时的激活分布,找出那些承载了大信号的权重列。
找到了这些重要的权重后,最直接的想法是把它们留在 FP16 精度,其他的量化。但这样做会导致硬件推理时非常低效,因为你得在一个矩阵里处理两种不同的数据格式。AWQ 优雅地避开了这个问题,它采用了一种自动缩放的策略。具体来说,它会给那些重要的权重列乘以一个大于 $1$ 的缩放因子 $s$,同时为了保持数学上的等价性,它会把对应的输入激活值除以相同的 $s$。
为什么给权重放大之后再量化误差更小?我们来举个例子说明:
直接进行线性量化
假设此时我们有一个非常强的输入信号 $X = 100$(这就是所谓的离群值),而对应的权重是一个很精细的浮点数 $W = 0.1234$。在理想的高精度计算中,结果应该是:
\[Y = 100 \times 0.1234 = 12.34\]现在,我们要把这个 $W$ 塞进一个刻度只有整数(1, 2, 3…)的 4-bit 量化器里。按照四舍五入,$0.1234$ 只能变成 $0$。那么量化后的计算变成了:
\[\hat{Y} = 100 \times 0 = 0\]误差直接就是 12.34,原来的信号特征全没了,这就是典型的量化灾难。
先放大再量化
AWQ 观察到了这个 $X=100$ 的强信号,它意识到这个 $W$ 非常重要,不能随便“舍入”掉。于是它决定引入一个缩放因子 $s = 10$。
第一步,它把权重 $W$ 放大 10 倍,变成 $1.234$。同时,为了保证数学上的结果不变,它把输入 $X$ 缩小 10 倍,变成 $10$。
这时候,公式变成了:$Y = (100 / 10) \times (0.1234 \times 10) = 10 \times 1.234$。
第二步,进行量化。现在权重是 $1.234$,再次放到那个整数刻度的量化器里。这次,$1.234$ 被四舍五入成了 $1$。
那么量化后的计算变成了:
\[\hat{Y}_{awq} = 10 \times 1 = 10\]虽然仍然有误差,但比起直接把信号弄丢(变成 0),AWQ 成功保住了这个通道的大部分有效信息。
GPTQ
GPTQ 是一种针对大型语言模型的后训练量化(Post-Training Quantization, 即PTQ)技术,它的核心目标是把原本占用巨大显存的 FP16 精度权重,压缩到4-bit甚至更低,同时尽可能不让模型的智力掉线。
GPTQ 并不是简单的四舍五入,它的高明之处在于利用了所谓的二阶导数信息,也就是海森矩阵(Hessian Matrix)。在量化的过程中,它会逐层处理参数。每当一个权重被强制压缩时,GPTQ 会计算这个改动对输出造成的误差,然后利用海森矩阵的信息去补偿该层中尚未量化的其他权重。
利用海森矩阵补偿信息
如果我们对量化的误差函数在原始权重处进行泰勒展开,你就会发现,一阶导数(梯度)在模型收敛点附近几乎为零,所以真正主导误差变化的就是那个二阶导数项,也就是海森矩阵。
这个矩阵记录了函数表面的曲率信息:如果某个权重的二阶导数很大,说明这个点稍微动一下,整体误差就会爆炸;如果二阶导数很小,说明这个点比较迟钝,随便怎么压也不会出大问题。GPTQ 的核心思想,就是利用这个海森矩阵来指导权重的牺牲与补偿。
在实际操作中,GPTQ 采用的是一种逐列量化策略。想象一下,我们有一排权重需要量化,当我们决定把第一个权重从 $0.8765$ 强行压缩成 $1$ 的时候,这个“强行舍入”产生的误差就会被记录下来。这时候,二阶导数补偿就上场了。GPTQ 不会坐视这个误差不管,它会利用海森矩阵的逆矩阵,把刚才那个权重产生的误差按比例分配给这一行中剩下的、还没被量化的权重。也就是说,后面的权重会根据前面的误差进行微调,通过这种主动的“偏置”来抵消前面量化带来的扭曲。当你把整行权重都处理完时,虽然每一个数字都变粗糙了,但它们合力产生的输出,却惊人地接近原始的高精度状态。
想象一下,我们正在处理一行权重向量 $w$。当我们把第 $i$ 个元素 $w_i$ 量化为 $\hat{w}_i$ 时,产生了一个微小的扰动 $\delta = \hat{w}_i - w_i$。为了让这一层的总输出改变量最小,我们需要调整剩余的、还没量化的权重(也就是索引 $j > i$ 的那些元素)。
这里最关键的数学工具就是海森矩阵的逆矩阵 $H^{-1}$。在 GPTQ 的逻辑里,第 $i$ 个权重的量化误差对后面权重的补偿量,是正比于 $H^{-1}$ 的第 $i$ 列向量的。简单来说,补偿量 $\Delta w_{j}$ 的计算公式大致表现为:
\[\Delta w_{j} = - \delta \cdot \frac{[H^{-1}]_{ji}}{[H^{-1}]_{ii}}\]这个公式告诉我们,后面每一个权重该变动多少,取决于它与当前量化权重之间的“协同敏感度”。
为什么是逆矩阵?在多维空间里,海森矩阵描述了误差曲面的形状。如果两个权重之间存在强烈的相关性(由海森矩阵的非对角线元素体现),那么量化其中一个产生的破坏,就必须由另一个进行大幅度的反向补偿才能抵消。
逆矩阵在这里充当了一个“相关性解码器”。通过 $[H^{-1}]_{ji}$ 这个比值,GPTQ 能够精确地计算出:为了抵消第 $i$ 个位置的精度损失,第 $j$ 个位置需要做出多大的补偿调整。
总结来说,GPTQ的核心思想就是抛弃注重单个权重量化产生的误差,而是通过利用当前权重量化误差去对后面还未量化的权重进行调整,从而使得整层网络输出的误差最小。
GGUF
GGUF 并不是一种单一的量化算法,它是一套完整的二进制文件格式和量化体系。它的核心逻辑不是在数学上高深的二阶导数,而是在工程实现和硬件适配上做到了极致的通用。
GGUF 最硬核的底层技术其实是 K-Quants(或者叫分块线性量化)。它不像 GPTQ 那样盯着全局的海森矩阵看,而是采取了极其细致的“网格化管理”。
想象一个巨大的权重矩阵,GGUF 会把它切成无数个小方块(通常是每 32 个或 64 个参数为一个 Block)。在每一个小块内部,它会独立寻找一个缩放因子和偏移量。这种做法的逻辑优势在于:它能局部地适应权重的剧烈波动。即便某一部分权重分布很诡异,它也只会影响那 32 个参数的量化精度,而不会像全局量化那样为了照顾几个离群值而毁掉整个矩阵的刻度。
混合精度
GGUF 的另一个精妙之处在于它提供了极其丰富的量化方案选择,也就是经常看到的 Q4_K_M、Q5_K_S 等等。这里的“K”代表了不同的量化策略。
以 Q4_K_M(Medium)为例,它的逻辑非常聪明:它并不是把所有层都一刀切地压到 4-bit。它会识别出模型中哪些层对智力最关键(比如 Attention 的核心权重),并给这些层分配稍高的位宽(比如 6-bit),而对那些不那么敏感的层(比如多层感知机的某些部分)使用4-bit压缩。这种层间混合精度的平衡感,使得 GGUF 模型在同等体积下,往往能展现出比单纯线性量化更稳健的逻辑推理能力。
更广泛的硬件支持
传统的量化方案往往高度依赖 NVIDIA 的 CUDA 环境,而 GGUF 在设计之初就考虑到了 Apple Silicon(Metal) 和 普通 CPU(AVX/NEON 指令集)。
它的文件格式里包含了模型运行所需的所有元数据,比如分词器设置、层数、架构参数等。这意味着你只需要一个 .gguf 文件,就能在几乎任何计算平台上跑起来,而不需要再配置复杂的 Python 环境或依赖库。这种“开箱即用”的工程闭环,极大地降低了大模型的准入门槛。
虽然 GPTQ 在纯显卡环境下吞吐量更高,但 GGUF 在内存压缩和多硬件协同上的逻辑是无敌的。它支持把模型的一部分放在显存里,剩下的跑在内存里(Offloading),这让很多显存不足的用户也能跑起巨大的 70B 甚至更大的模型。
总结
| 维度 | GPTQ | AWQ | GGUF |
|---|---|---|---|
| 核心算法 | OBQ误差补偿 | 激活感知保护 | k-means聚类混合精度 |
| 硬件要求 | NVIDIA GPU (CUDA) | NVIDIA GPU (CUDA) | CPU / GPU / Apple Silicon |
| 推理速度(GPU) | 最快 | 快 | 中等 |
| 推理速度(CPU) | 不支持 | 不支持 | 最快(唯一选择) |
| 量化质量 | 中等 | 最好 | 中等 |
| 长上下文表现 | 一般 | 好 | 中等 |
| 文件格式 | 多文件 | 多文件 | 单文件 |
| 消费级友好度 | 低 | 低 | 高 |
| vLLM支持 | 支持 | 原生支持 | 有限支持 |
参考文章:https://www.zhihu.com/question/633365088/answer/2009931891990606483