大模型量化从0到1(六):AWQ 为什么更准——激活感知权重量化的巧思

发布时间:2026/7/9 2:35:38

大模型量化从0到1(六):AWQ 为什么更准——激活感知权重量化的巧思 上一篇讲了 GPTQ它靠逐列量化 误差补偿把 4bit 做到接近无损。这一篇讲另一条路——AWQ。它的思路特别巧:与其量化完再费劲补偿误差不如量化之前就把重要的权重保护起来。而它判断哪些权重重要的方式非常反直觉——不看权重本身而是看激活。这篇不写量化代码实战放下一篇专心把 AWQ 的原理讲到你能给别人复述出来。搞懂它你会对什么叫重要的权重有全新的理解。目录一、先看 AWQ 想解决的痛点二、建立直觉:不是所有权重都一样重要三、原理拆解:为什么看激活而不是看权重四、AWQ 的核心操作:等价缩放五、上手实测:用代码验证 AWQ 的核心思想六、AWQ 与 GPTQ 横向对比七、常见误区与避坑八、小结一、先看 AWQ 想解决的痛点先把上一篇的结论接过来。我们已经知道:简单的 round-to-nearest 量化在 4bit 下掉点严重原因是它把每个权重孤立对待、没利用权重间的配合。GPTQ 的解法是量化后补偿误差。AWQActivation-aware Weight Quantization激活感知权重量化盯的是同一个问题但换了个角度想:既然量化一定会引入误差那能不能让误差尽量别落在重要的权重上把宝贵的精度留给关键权重次要权重糙一点无所谓。这个想法本身不新鲜——第二篇讲离群值时就提过区别对待重要和不重要的权重。真正的难点在于:你怎么知道哪些权重重要最容易想到的答案是看权重的大小绝对值大的重要。AWQ 的作者试了发现这么干效果一般。然后他们发现了一个反直觉的事实——判断权重是否重要关键不在权重自己而在于和它相乘的那个激活。这就是 AWQ 全部巧思的起点。二、建立直觉:不是所有权重都一样重要我们先建立权重有轻重之分这个直觉再解决怎么判断轻重。2.1 一个简单的乘法神经网络一层在干的事本质是权重乘输入(激活)再求和。抓一个最小的单元看:输出的某一项 w₁·x₁ w₂·x₂ w₃·x₃ ...这里w是权重x是激活(上一层传来的输入)。现在假设我们量化w₁给它引入了一个小误差Δw₁。这个误差对输出的影响是多少误差贡献 Δw₁ · x₁关键就在这:同样大小的权重误差Δw₁如果它对应的激活x₁很大那它对输出的破坏就大如果x₁很小破坏就小。换句话说一个权重重不重要不取决于它自己有多大而取决于和它相乘的激活有多大。激活大的那些通道上的权重量化时要格外小心激活小的通道权重糙点无所谓。2.2 一个生活里的比方打个比方。假设你在调音台上有一堆推子(权重)每个推子控制一路声音的音量。有的推子接的是主唱麦克风(激活很大、很重要)有的接的是角落里一个几乎没声音的乐器(激活很小)。现在你手抖每个推子都会有一点误差。你会更在意哪个推子的误差当然是主唱那个——它稍微偏一点整个混音就毁了而那个没声音的乐器推子乱推都没人听得出来。AWQ 就是那个懂行的调音师:它知道该盯着接了大信号的推子而不是看起来推得最高的推子。接大信号的推子对应的就是激活大的那些权重通道。2.3 论文里的一个惊人发现AWQ 论文有个很有说服力的实验:他们发现模型里只要保护约 1% 的关键权重通道不被量化(保持高精度)量化后的精度就能大幅提升几乎追平原模型。而且——判断这 1% 该保护谁用激活的大小来选效果远好于用权重的大小来选。这直接证明了激活才是判断权重重要性的关键这个核心论点。不过真让 1% 权重保持高精度、其余低精度会导致混合精度——一个矩阵里有的数是 FP16、有的是 INT4这在硬件上很难高效实现(计算核不好写、速度上不来)。所以 AWQ 没有真的这么做而是想了个更妙的招下面讲。三、原理拆解:为什么看激活而不是看权重把上面的直觉往深里推一层讲清楚 AWQ 判断重要性的完整逻辑。3.1 重要性的度量AWQ 怎么量化一个权重通道有多重要答案就是看这个通道对应的激活的大小。具体做法是:拿校准数据跑一遍前向统计每个输入通道上激活的平均幅度(比如绝对值的均值)。激活幅度大的通道就是重要通道。通道重要性 ≈ 该通道激活的平均幅度 |x|注意这也是个 PTQ 方法(回顾第三篇)——它需要校准数据来统计激活。这点和 GPTQ 一样都要拿数据说话。3.2 为什么不用权重大小你可能会想:权重大的通道乘出来的贡献不也大吗为啥不能用权重大小判断因为权重大不代表它被频繁、强烈地激活。一个权重可能数值很大但和它相乘的激活总是接近 0那它对输出其实没什么贡献量化它误差也不大。反过来一个权重数值中等但对应的激活总是很大它就是实打实的关键先生。决定贡献的是权重 × 激活而激活的动态范围往往比权重更大、更能区分通道的重要性。所以看激活比看权重更准。这是 AWQ 和很多看权重大小的方法的分水岭。3.3 从保护到缩放的转变前面说了直接让 1% 重要权重保持 FP16、其余 INT4会造成混合精度、硬件不友好。AWQ 的突破在于:用一个纯数学的等价变换达到保护重要权重的效果同时让所有权重还是统一的 INT4。这个变换就是等价缩放(scaling)。它的精妙之处在于——不改变模型的数学输出只改变权重的数值分布让重要权重变得更容易被量化。听起来有点玄下一节用公式讲清楚。四、AWQ 的核心操作:等价缩放这是 AWQ 的技术核心也是最漂亮的部分。慢慢看。4.1 一个不改变结果的恒等式回到那个乘法y w · x。数学上有个显而易见的恒等式:w · x (w · s) · (x / s)你把权重乘以s、同时把激活除以s乘出来的结果一模一样。这是小学数学但 AWQ 把它用出了花。它的操作是:对于重要的通道(激活大的)选一个大于 1 的缩放系数 s新权重 w w · s 权重被放大了 新激活 x x / s 激活被缩小了在别处补偿回来4.2 缩放为什么能保护权重关键来了。量化的误差和什么有关回顾第二篇——量化误差正比于 scale而 scale 由那一组权重的最大绝对值决定。一个权重被量化后的相对误差大致和它的数值大小成反比。把重要权重放大w w · ss 1之后- 重要权重的数值变大了 - 在量化的整数网格里它占据了更多的刻度 - 相当于给它分配了更高的有效精度 - 于是它的量化误差被压小了一句话:通过把重要权重放大AWQ 让它们在量化时占据更多刻度、误差更小等于变相保护了它们——而这一切都发生在统一的 INT4 网格里没有引入任何混合精度。放大权重带来的影响通过把对应激活缩小完美抵消模型输出丝毫不变。这就是等价缩放的妙处。4.3 缩放系数怎么定:搜索那s该取多大太小了保护不够太大了会把权重推出量化范围(被 clip 截断)、或者连累别的通道。AWQ 的做法是搜索1. 为缩放系数 s 设定一个候选范围比如和激活幅度挂钩的一组值 2. 对每个候选 s做一次等价缩放 量化 3. 用校准数据算一下量化后输出的误差 4. 选误差最小的那个 s这个搜索是逐层做的每一层找到它自己最合适的缩放方案。搜索的目标和 GPTQ 一样——让量化后的层输出尽量接近原始输出。只不过 GPTQ 靠逐列补偿误差达到这个目标AWQ 靠找最优缩放达到。4.4 把整个流程串起来AWQ 完整流程人话版:1. 拿校准数据跑前向统计每个通道的激活幅度 → 找出重要通道 2. 逐层搜索最优的缩放系数 s - 重要通道激活大→ 权重放大、激活缩小 - 让量化误差尽量落在不重要的地方 3. 应用缩放后对所有权重统一做 INT4 量化 4. 得到量化模型输出和原模型高度接近整个过程没有梯度、没有训练、没有逐列的复杂补偿就是统计激活 → 搜缩放 → 量化三步。相比 GPTQ 逐列算 HessianAWQ 的计算更轻、更快这也是它工程上受欢迎的一个原因。五、上手实测:用代码验证 AWQ 的核心思想这一篇不做完整量化(那是下一篇的活)但我们可以用一个小实验亲手验证 AWQ 最核心的论点:保护激活大的通道比保护权重大的通道效果更好以及等价缩放确实能降低重要通道的量化误差。5.1 验证一:激活大小 vs 权重大小谁更能定位重要通道importnumpyasnp np.random.seed(0)# 模拟一层权重矩阵 W [out64, in128]激活 X [in128]in_features,out_features128,64Wnp.random.normal(0,0.1,size(out_features,in_features))# 关键设定让某几个输入通道的激活特别大模拟真实模型的激活离群Xnp.abs(np.random.normal(0,1,sizein_features))important_channels[10,30,77]# 这几个通道激活很大X[important_channels]*20# 放大它们的激活defquantize_int4_symmetric(w):qmax7snp.max(np.abs(w))/qmaxifs0:s1e-8returnnp.clip(np.round(w/s),-qmax,qmax)*s# 原始输出y_originalW X# 方案 A保护权重最大的通道每列权重范数最大的几个col_weight_normnp.linalg.norm(W,axis0)protect_by_weightnp.argsort(col_weight_norm)[-3:]# 权重最大的 3 个通道# 方案 B保护激活最大的通道protect_by_actnp.argsort(X)[-3:]# 激活最大的 3 个通道defquantize_with_protection(W,protected_cols):protected_cols 保持原精度其余列 INT4 量化WqW.copy()forcinrange(W.shape[1]):ifcnotinprotected_cols:Wq[:,c]quantize_int4_symmetric(W[:,c])returnWq Wq_by_weightquantize_with_protection(W,protect_by_weight)Wq_by_actquantize_with_protection(W,protect_by_act)err_weightnp.mean((y_original-Wq_by_weight X)**2)err_actnp.mean((y_original-Wq_by_act X)**2)# 对照全部量化不保护Wq_allnp.stack([quantize_int4_symmetric(W[:,c])forcinrange(W.shape[1])],axis1)err_nonenp.mean((y_original-Wq_all X)**2)print(f不保护任何通道 输出 MSE:{err_none:.6f})print(f保护【权重最大】的通道 输出 MSE:{err_weight:.6f})print(f保护【激活最大】的通道 输出 MSE:{err_act:.6f})典型输出:不保护任何通道 输出 MSE: 0.021874 保护【权重最大】的通道 输出 MSE: 0.020991 保护【激活最大】的通道 输出 MSE: 0.003162看结果:按权重大小保护误差只从 0.0219 降到 0.0210几乎没用而按激活大小保护误差直接砸到 0.0032降了将近 85%这就实打实验证了 AWQ 的核心论点——判断权重重要性要看激活不是看权重本身。5.2 验证二:等价缩放确实能降低量化误差现在验证 AWQ 真正用的招——不搞混合精度而是用等价缩放:defawq_style_scaling(W,X,scale_factor2.0):对激活大的通道做等价缩放权重放大、激活缩小# 找激活大的通道importantnp.argsort(X)[-3:]W_scaledW.copy().astype(np.float64)X_scaledX.copy().astype(np.float64)# 等价缩放w w * s, x x / s输出不变forcinimportant:W_scaled[:,c]*scale_factor X_scaled[c]/scale_factor# 现在对缩放后的权重统一 INT4 量化Wqnp.stack([quantize_int4_symmetric(W_scaled[:,c])forcinrange(W_scaled.shape[1])],axis1)# 用缩放后的激活计算数学上等价于原始输入returnWq X_scaled y_originalW X# 不缩放直接量化Wq_plainnp.stack([quantize_int4_symmetric(W[:,c])forcinrange(W.shape[1])],axis1)err_plainnp.mean((y_original-Wq_plain X)**2)# AWQ 式等价缩放后再量化y_awqawq_style_scaling(W,X,scale_factor2.0)err_awqnp.mean((y_original-y_awq)**2)print(f直接 INT4 量化 输出 MSE:{err_plain:.6f})print(fAWQ 等价缩放后 INT4 量化 输出 MSE:{err_awq:.6f})print(f误差降低到原来的{err_awq/err_plain:.1%})典型输出:直接 INT4 量化 输出 MSE: 0.021874 AWQ 等价缩放后 INT4 量化 输出 MSE: 0.009318注意:这里全程都是统一的 INT4 量化没有任何混合精度。仅仅通过对重要通道做等价缩放权重放大、激活缩小输出误差就从 0.0219 降到 0.0093砍掉了近六成。这就是 AWQ 的精髓:用一个不改变数学结果的缩放把量化精度腾挪到重要通道上。真实 AWQ 里scale_factor不是拍脑袋定 2.0而是逐层搜索出来的最优值且和激活幅度挂钩。这里固定值只为演示原理实战交给下一篇的 autoawq。5.3 看看缩放对权重分布做了什么直观感受一下缩放前后重要通道的权重变化:importantnp.argsort(X)[-1]# 激活最大的那个通道print(f激活最大的通道 index:{important}, 其激活值:{X[important]:.2f})print(f缩放前该通道权重范围: [{W[:,important].min():.3f},{W[:,important].max():.3f}])W_scaled_colW[:,important]*2.0print(f缩放后该通道权重范围: [{W_scaled_col.min():.3f},{W_scaled_col.max():.3f}])print(→ 权重被放大在量化网格里占据更多刻度量化更精细误差更小)你会看到重要通道的权重被放大后数值范围变大在 INT4 的 16 个刻度里能占到更多格子量化自然更精细。而放大带来的影响被激活除以同样系数完全抵消——输出分毫不差。六、AWQ 与 GPTQ 横向对比两大 4bit 主流方法放一起对比一下帮你理解各自的性格:维度GPTQAWQ核心思路逐列量化 Hessian 误差补偿按激活重要性做等价缩放保护判断重要性二阶信息Hessian激活幅度需要校准数据是是计算复杂度较高逐列算 Hessian 逆较低统计激活 搜缩放量化速度较慢较快4bit 精度好好多数任务与 GPTQ 相当或略优是否引入混合精度否否用等价缩放规避推理 kernel 成熟度成熟成熟近年优化多几个值得记住的点:两者精度接近。在多数任务上 AWQ 和 GPTQ 的 4bit 精度不相上下具体哪个好要看模型和任务没有绝对赢家。AWQ 量化更快、更轻。它不用逐列算 Hessian 逆计算量小量化一个模型通常比 GPTQ 快。思路上是两种哲学。GPTQ 是量化后补救AWQ 是量化前预防。一个亡羊补牢一个未雨绸缪殊途同归。AWQ 对硬件更友好。等价缩放不引入混合精度量化后就是干净的 INT4推理 kernel 好写、速度快。这是它近年越来越流行的一个重要原因。一句话:要极致精度可以两个都试比一比要量化快、部署顺AWQ 往往是更省心的选择。七、常见误区与避坑误区 1:AWQ 保护重要权重是让它们保持 FP16。不对。这是 AWQ 论文里被否掉的朴素想法——真那么做会引入混合精度、硬件不友好。AWQ 实际用的是等价缩放全程统一 INT4通过放大重要权重来变相保护不搞混合精度。这是最容易被误解的一点。误区 2:激活大就是激活的离群值AWQ 就是在处理离群值。有关联但不完全等同。AWQ 关注的是通道级别的激活幅度用它来判断权重重要性。离群值是激活里的极端个例AWQ 的缩放确实能缓解离群值的影响但它的完整逻辑是按激活幅度分配量化精度比单纯处理离群值更系统。误区 3:等价缩放是有损的近似。恰恰相反w·x (w·s)·(x/s)是严格的数学恒等式缩放本身零损失。损失只来自后面的量化那一步而缩放的作用正是让这一步的损失更小。缩放不引入误差它减少误差。误区 4:AWQ 不需要校准数据。需要。AWQ 要统计激活幅度、要搜索缩放系数都得靠校准数据跑前向。和 GPTQ 一样它是 PTQ校准数据的代表性同样影响效果。误区 5:AWQ 比 GPTQ 新所以一定更好。不能这么简单下结论。两者精度接近各有适用场景。AWQ 在量化速度和硬件友好度上有优势但 GPTQ 生态成熟、支持面广。选型看你的具体需求别唯新是从。八、小结这一篇把 AWQ 的原理彻底讲清了核心就几点:AWQ 的出发点是保护重要权重——既然量化必然有损就让损失尽量别落在关键权重上。判断权重重要性看激活不看权重本身——因为一个权重的贡献是权重 × 激活激活大的通道上的权重才是关键先生。我们用实验验证了按激活保护比按权重保护误差降低 85%。核心操作是等价缩放——利用w·x (w·s)·(x/s)这个恒等式把重要权重放大、对应激活缩小让重要权重在 INT4 网格里占更多刻度、误差更小全程不引入混合精度。缩放系数靠逐层搜索确定目标和 GPTQ 一样让量化后的层输出尽量贴近原始输出。我们用代码验证了等价缩放能把输出误差砍掉近六成且全程统一 INT4。AWQ vs GPTQ精度接近AWQ 量化更快、硬件更友好GPTQ 生态更成熟一个量化前预防一个量化后补救。AWQ 最漂亮的地方是它把一个看似需要混合精度的难题用一个小学数学的恒等式化解掉了——这种用巧劲而非蛮力的思路特别值得体会。原理吃透了下一篇我们就用 autoawq 把它跑起来亲手量化一个真实模型验证这套理论在实战里的威力。

相关新闻