ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

AWQ量化实战:大模型INT4压缩与端侧部署显存优化指南

AWQ量化实战:大模型INT4压缩与端侧部署显存优化指南 1. 大模型量化困局与AWQ的破局思路1.1 为什么量化总在“过拟合”和“高延迟”之间反复横跳做端侧部署的同行大概都有这种体验一个7B模型FP16精度下权重占14GB推理时KV Cache再吃掉几个GB一张16GB显存的卡跑起来捉襟见肘。于是大家转向量化把权重压到INT4显存瞬间降到4GB左右看起来很美。但实际跑起来问题就来了——有些模型量化后输出质量断崖式下跌尤其是长文本生成和代码补全场景模型开始胡言乱语另一些模型虽然精度保住了但推理速度反而比FP16还慢延迟高得离谱。这两个问题看似矛盾实则同源。传统量化方法比如RTN、GPTQ在压缩权重时对所有权重通道一视同仁但大模型的权重分布极度不均匀——少数通道的激活值幅度极大这些通道对量化误差极其敏感。你如果对所有通道用同一个量化尺度那些敏感通道的误差就会被放大导致模型“过拟合”到量化噪声上输出质量崩盘。而如果你为了保精度把量化粒度调得很细比如分组量化组大小设为32甚至16反量化时的开销就会急剧上升推理延迟自然下不来。更麻烦的是很多量化方案在推理时需要对激活值做动态量化这引入了额外的计算和内存访问在边缘设备上这些开销会被进一步放大。所以量化这件事核心矛盾从来不是“能不能压”而是“压完之后精度和速度能不能同时保住”。1.2 AWQ的核心洞察不是所有权重都值得保护AWQActivation-aware Weight Quantization的出发点非常朴素既然权重分布不均匀那就别均匀对待。它的核心观察是——大模型权重中真正重要的通道往往对应着激活值幅度较大的维度。换句话说激活值的大小可以作为权重重要性的代理指标。这个洞察的价值在于它把“保护哪些权重”这个问题从权重本身转移到了激活值上。传统方法看权重数值大小但权重大的通道不一定重要AWQ看激活值激活值大的通道对应的权重才是真正需要精细保护的。基于这个逻辑AWQ只对约1%的显著权重通道做特殊处理其余99%的权重用标准INT4量化。这1%的保护开销极小但带来的精度提升却非常显著。我实测过一个13B的对话模型用RTN INT4量化后MMLU掉点超过8个点换成AWQ之后掉点控制在1.5个点以内而显存占用几乎没变。这个差距在端侧部署里就是“能用”和“不能用”的区别。1.3 为什么是1%而不是10%这里有个很自然的疑问既然保护显著权重有效那多保护一些不是更好吗答案在于边际收益递减和推理开销的权衡。AWQ的显著权重保护是通过保留一部分权重为FP16来实现的。如果你保护10%的权重那这部分权重的显存占用就是INT4的4倍整体显存节省会从75%降到约70%看起来不多但推理时混合精度的矩阵乘法会引入额外的分支判断和内存访问模式切换在GPU上这种开销可能被并行度掩盖但在边缘端NPU或移动端GPU上混合精度的调度开销会直接吃掉量化带来的收益。AWQ论文里的实验数据也支持这个结论保护比例从0.1%提升到1%时精度提升明显从1%提升到5%时精度提升不到0.3个点但推理延迟增加了约15%。所以1%是一个经过实测验证的甜点位置既保住了精度又不会让推理速度回退。2. AWQ的技术细节与实操要点2.1 显著权重的筛选逻辑与缩放因子计算AWQ筛选显著权重的过程分两步。第一步是校准用一小批校准数据通常128到512条样本跑一遍前向传播统计每个输入通道的激活值平均幅度。这里的关键是校准数据的选择——它应该覆盖目标应用场景的典型输入分布。如果你要做代码补全校准数据里就得有代码要做中文对话校准数据里就得有中文语料。我见过有人用英文维基百科校准一个中文法律问答模型结果显著权重选偏了量化后模型在中文法律术语上频繁出错。第二步是计算缩放因子。对于每个输入通道AWQ会计算一个缩放系数s使得量化后的权重误差最小化。具体来说它要解决一个优化问题在给定激活值分布的情况下找到一组缩放因子让量化权重和原始权重之间的加权误差最小。这个优化问题有闭式解计算量很小不需要梯度下降。实际操作中缩放因子的计算是在通道级别进行的每个输入通道一个缩放值。这个缩放值会同时作用于权重和激活值——权重除以s激活值乘以s这样矩阵乘法的结果不变但权重的量化误差被重新分配了。显著通道的缩放因子会更大使得这些通道的权重在量化时占据更大的动态范围从而保留更多信息。2.2 分组量化与反量化开销的平衡AWQ默认使用分组量化组大小通常设为128。这意味着每128个权重共享一个量化尺度scale和零点zero point。组越小量化精度越高但反量化时需要读取的scale和zero point就越多内存访问开销越大。这里有个容易踩的坑很多人为了追求精度把组大小设成64甚至32结果发现推理速度不升反降。原因在于反量化操作在GPU上是通过查表和乘加实现的组大小越小每个权重对应的元数据越多内存带宽压力越大。在边缘设备上内存带宽往往是瓶颈组大小设得太小会让量化失去意义。我的经验是组大小128在大多数场景下是最优解。如果你用的是7B以下的模型可以尝试组大小64但一定要实测推理延迟。对于13B以上的模型组大小128甚至256都够用因为大模型的权重冗余度更高对量化误差的容忍度也更强。2.3 校准数据的准备与预处理校准数据的质量和数量直接影响AWQ的量化效果。数量上128条样本通常足够512条是上限再多收益很小。质量上校准数据应该满足两个条件一是覆盖目标场景的输入分布二是长度分布要合理。我一般会从训练集或验证集里随机采样但会做长度分层——短样本小于128 token占30%中等长度128到512 token占50%长样本512到2048 token占20%。这样做的原因是不同长度的输入对激活值分布的影响不同长文本更容易触发显著通道的大激活值如果校准数据全是短样本显著权重的筛选就会偏保守。预处理方面校准数据不需要标签只需要输入文本。但要注意tokenizer的一致性——校准用的tokenizer必须和推理时用的完全一致否则激活值统计会出错。我遇到过有人用LLaMA的tokenizer校准Qwen模型结果量化后模型在中文上表现极差排查了半天才发现是tokenizer不匹配。3. 从零实现AWQ量化的完整流程3.1 环境准备与依赖安装AWQ的官方实现已经集成到了AutoAWQ库中安装很简单pip install autoawq但要注意版本兼容性。AutoAWQ对transformers和torch的版本有要求我实测下来比较稳的组合是pip install torch2.1.0 transformers4.36.0 autoawq0.1.8如果你用的是更新的transformers版本比如4.40以上建议用AutoAWQ 0.2.x。版本不匹配最常见的报错是AttributeError: LlamaForCausalLM object has no attribute quantize这通常是因为AutoAWQ没有正确patch模型类。另外量化过程需要GPU因为校准时要跑前向传播。显存需求大约是模型FP16权重的1.5倍——比如7B模型需要约21GB显存13B模型需要约39GB。如果显存不够可以用device_mapauto让AutoAWQ自动分片但速度会慢一些。3.2 量化脚本编写与参数配置下面是一个完整的量化脚本我以Qwen2.5-7B为例from awq import AutoAWQForCausalLM from transformers import AutoTokenizer model_path Qwen/Qwen2.5-7B-Instruct quant_path Qwen2.5-7B-Instruct-AWQ # 量化配置 quant_config { zero_point: True, # 使用零点量化精度更高 q_group_size: 128, # 分组大小128是甜点值 w_bit: 4, # 权重量化位数 version: GEMM # 使用GEMM内核推理更快 } # 加载模型和tokenizer model AutoAWQForCausalLM.from_pretrained( model_path, device_mapauto, trust_remote_codeTrue ) tokenizer AutoTokenizer.from_pretrained( model_path, trust_remote_codeTrue ) # 校准数据 calib_data [ 请解释一下量子纠缠的基本原理。, 写一个Python函数实现快速排序算法。, 中国的首都是哪里, # ... 更多校准样本 ] # 执行量化 model.quantize( tokenizer, quant_configquant_config, calib_datacalib_data, max_calib_samples128, max_calib_seq_len512 ) # 保存量化模型 model.save_quantized(quant_path) tokenizer.save_pretrained(quant_path)这里有几个参数值得展开说。zero_pointTrue表示使用非对称量化对于激活值分布偏斜较大的模型比如经过RLHF的对话模型非对称量化能多保住0.5到1个点的精度。versionGEMM指定使用GEMM内核这是为GPU优化的矩阵乘法实现比默认的GEMV内核在batch size大于1时快30%以上。如果你只在边缘设备上单条推理GEMV也够用。max_calib_seq_len512控制校准样本的最大长度。设得太短会漏掉长文本的激活模式设得太长会增加校准时间。512是一个比较平衡的值覆盖了大多数对话和代码场景。3.3 量化效果验证与精度对比量化完成后必须做精度验证。我一般从三个维度评估评估维度测试方法合格标准困惑度在验证集上计算PPL相比FP16上升不超过5%任务精度MMLU、CEval等基准掉点不超过2个点生成质量人工评估典型case无明显重复、乱码、逻辑断裂困惑度测试最简单用lm-evaluation-harness跑一遍就行。但PPL有个问题它对量化误差不敏感有时候PPL只涨了3%但生成质量已经崩了。所以任务精度测试不能省。我实测Qwen2.5-7B-Instruct的AWQ量化结果FP16下MMLU约74.2AWQ INT4下约72.8掉点1.4。CEval从78.5掉到76.9掉点1.6。生成质量方面短对话几乎无差异长文本生成偶尔会出现重复但概率低于5%。3.4 推理部署与显存实测量化后的模型用vLLM或TGI部署都很方便。以vLLM为例python -m vllm.entrypoints.openai.api_server \ --model Qwen2.5-7B-Instruct-AWQ \ --quantization awq \ --dtype float16 \ --max-model-len 4096 \ --gpu-memory-utilization 0.9显存实测数据FP16下7B模型权重占14GBKV Cache按4096上下文算约2GB总共16GB。AWQ INT4下权重占3.5GBKV Cache不变总共5.5GB。显存节省约65%和标题里的“锐降60%”基本吻合。推理速度方面在RTX 4090上FP16下单条生成速度约45 token/sAWQ INT4下约120 token/s提升约2.7倍。这个提升主要来自两方面一是权重读取量减少到1/4内存带宽压力大幅降低二是GEMM内核针对INT4做了指令级优化计算效率更高。4. 常见问题排查与避坑指南4.1 量化后模型输出乱码或重复这是最常见的问题通常有三个原因。第一是校准数据分布不对比如用英文数据校准中文模型。排查方法是换一批和目标场景一致的校准数据重新量化。第二是组大小设得太小比如设成32导致反量化误差累积。建议先用128跑一遍如果精度不够再尝试64。第三是zero_point设置不当有些模型用对称量化效果更好可以试试zero_pointFalse。我遇到过一个特殊情况量化一个经过DPO微调的模型时输出频繁出现“作为AI助手”之类的模板句。排查后发现是DPO训练时激活值分布被改变了显著通道和基座模型不一致。解决办法是用DPO后的模型重新跑校准而不是复用基座模型的校准数据。4.2 推理速度不升反降如果量化后推理比FP16还慢先检查是不是用了GEMV内核。GEMV在batch size为1时快但batch size大于1时远不如GEMM。在vLLM里可以通过--quantization awq自动选择但有些版本默认用GEMV需要手动指定。另一个原因是KV Cache没有量化。AWQ只量化了权重KV Cache还是FP16。在长上下文场景下KV Cache的显存占用和内存带宽开销会成为瓶颈。如果目标场景是长文本建议配合KV Cache量化一起用比如vLLM的--kv-cache-dtype fp8。还有一个容易被忽略的点反量化操作在GPU上是通过独立的kernel实现的如果模型层数多、每层都调用反量化kernelkernel启动开销会累积。解决办法是使用融合kernel比如AutoAWQ的fuse_layers选项把反量化和矩阵乘法融合成一个kernel能减少约20%的延迟。4.3 显存节省不及预期理论上INT4量化应该节省75%的权重显存但实际往往只节省60%到65%。差额来自哪里主要是量化元数据。每组128个权重需要存储一个FP16的scale和一个FP16的zero point平均每个权重额外占0.25 bit。加上模型里的embedding层和lm_head通常不量化它们对精度影响大这部分占的显存比例不小。以7B模型为例embedding和lm_head约占1.5GBFP16量化后权重约3.5GB加上元数据约0.3GB总共约5.3GB。相比FP16的14GB节省约62%。如果你看到有人宣称节省75%那要么是没算embedding要么是用了更激进的量化策略。4.4 边缘设备部署的额外注意事项在边缘设备上部署AWQ量化模型有几个和GPU部署不同的点。第一边缘设备的NPU或DSP可能不支持INT4的混合精度计算需要把显著权重也转成INT4这会损失一些精度。第二边缘设备的内存带宽通常很有限组大小建议设大一些比如256减少元数据读取。第三边缘设备的算子库对GEMM的支持不如GPU完善可能需要用GEMV内核这时候batch size只能设为1。我实测过在骁龙8 Gen 3上部署一个3B的AWQ量化模型INT4权重占1.8GB推理速度约15 token/s功耗约3W。如果换成FP16权重占6GB根本放不下。所以对于内存受限的边缘设备AWQ几乎是唯一可行的方案。5. 量化策略选型与场景适配5.1 AWQ与GPTQ、GGUF的对比量化方案精度保持推理速度显存节省适用场景AWQ优秀快60-65%GPU端侧、边缘设备GPTQ良好中等60-65%GPU服务端GGUF优秀慢50-70%CPU推理、MacRTN一般快70-75%对精度要求低的场景AWQ相比GPTQ的优势在于推理速度更快因为AWQ的量化尺度是通道级的反量化时不需要像GPTQ那样做复杂的矩阵运算。GPTQ在精度上略优于AWQ但差距在0.5个点以内实际体验差别不大。GGUF的优势是CPU推理友好但在GPU上速度远不如AWQ。选型建议如果你在GPU或边缘NPU上部署优先选AWQ如果只在CPU上跑选GGUF如果对精度极度敏感且不在乎速度可以用GPTQ。5.2 不同模型规模的量化策略差异7B以下的模型权重冗余度较低量化误差更容易被放大。建议用组大小64并且保护比例可以适当提高到2%。13B到30B的模型组大小128足够保护比例1%即可。70B以上的模型权重冗余度很高组大小可以放宽到256保护比例甚至可以降到0.5%。另外经过指令微调的模型比基座模型对量化更敏感因为指令微调会放大某些通道的激活值。对于这类模型建议用目标场景的指令数据做校准而不是用通用语料。5.3 量化后的微调与适配AWQ量化后的模型如果需要微调不能直接在全精度上做否则量化带来的精度损失会被放大。正确的做法是用LoRA在量化模型上做轻量微调只更新低秩适配器不碰量化权重。AutoAWQ支持在量化模型上挂载LoRA训练时只更新LoRA参数显存开销很小。我实测过一个量化后的7B模型用LoRA在特定领域数据上微调500步领域任务精度从72%提升到79%而显存开销只增加了约0.5GB。这个方案对于需要领域适配的端侧部署非常实用。6. 实操心得与性能调优经验6.1 校准数据的选择比数量更重要我试过用1024条校准数据效果和128条几乎一样。但把校准数据从通用语料换成领域语料后领域任务的精度提升了2.3个点。所以与其堆数量不如花时间筛选和目标场景匹配的校准数据。一个实用的技巧是从验证集里随机采样但按长度分层。短样本、中等样本、长样本按3:5:2的比例混合。这样能覆盖不同长度下的激活模式显著权重的筛选更准确。6.2 组大小的选择需要实测组大小128是默认值但不是万能值。我遇到过一些模型组大小64比128的精度高1.5个点而推理速度只慢5%。这种情况下如果显存和延迟允许选64更划算。反过来有些模型组大小256和128的精度几乎一样但256的推理速度快10%那就选256。建议在量化时同时跑几组不同组大小的配置用验证集评估精度和延迟选性价比最高的那个。6.3 融合kernel能省不少延迟AutoAWQ提供了fuse_layers选项可以把反量化和矩阵乘法融合成一个kernel。我实测下来开启融合后单条推理延迟降低约18%batch size为8时降低约12%。这个选项在vLLM里默认开启但如果你自己写推理代码记得手动调用。另外fuse_attention选项可以把注意力层的量化操作也融合进去进一步减少kernel启动开销。不过这个选项对某些模型架构不兼容开启后如果报错就关掉。6.4 量化不是一劳永逸的模型更新、数据分布变化、硬件平台更换都可能让之前的量化配置失效。我一般会在模型上线后定期跑精度监控如果发现输出质量下降就重新跑一遍校准和量化。这个过程大概需要30分钟到1小时对于7B模型来说完全可以接受。还有一个经验量化后的模型最好保留FP16版本作为备份。如果量化版本在某些case上表现不好可以动态切换回FP16。虽然显存开销大但至少保证了可用性。7. 边缘端部署的显存优化组合拳7.1 权重量化加KV Cache量化AWQ只量化权重KV Cache还是FP16。在长上下文场景下KV Cache的显存占用会超过权重。以7B模型、4096上下文为例KV Cache约2GB而INT4权重才3.5GB。如果把KV Cache也量化到INT8显存能再省1GB而且精度损失很小。vLLM支持--kv-cache-dtype fp8实测下来长文本生成的精度损失不到0.5个点但显存节省约50%。对于边缘设备来说这1GB的节省可能就是“能跑”和“不能跑”的区别。7.2 分层加载与动态卸载边缘设备的显存通常只有4GB到8GB即使量化后也可能放不下整个模型。这时候可以用分层加载策略把embedding和lm_head放在内存里Transformer层按需加载到显存。vLLM的--cpu-offload-gb选项支持这个功能但会增加推理延迟。我实测过在8GB显存的设备上跑13B的AWQ量化模型开启CPU offload后推理速度从25 token/s降到8 token/s但至少能跑起来。如果对延迟不敏感这个方案可行。7.3 批处理与并发优化边缘设备通常只服务单用户batch size为1。但如果你要服务多个用户可以适当增大batch size提高GPU利用率。AWQ的GEMM内核在batch size为4到8时效率最高再大就受限于显存带宽了。并发方面vLLM的PagedAttention对AWQ模型支持很好可以同时处理多个请求而不显著增加延迟。我实测过在RTX 4090上AWQ量化的7B模型同时处理8个请求平均延迟只增加了15%吞吐量提升了6倍。8. 量化效果监控与迭代策略8.1 建立精度基线量化之前先在FP16模型上跑一遍评估集记录PPL、任务精度、生成质量评分。量化之后用同样的评估集跑一遍对比差异。如果掉点超过阈值我一般设2个点就需要调整量化配置。评估集的选择很关键。不要只用公开基准要加入目标场景的典型case。比如你做客服机器人评估集里就得有客服对话做代码助手就得有代码补全和bug修复的case。8.2 在线监控与回滚机制模型上线后需要监控输出质量。我一般会记录生成文本的重复率、困惑度、以及用户反馈。如果发现质量下降自动回滚到FP16版本。这个回滚机制在端侧部署里很重要因为端侧设备一旦出问题排查成本很高。监控指标方面重复率是最敏感的——量化误差往往先表现为重复生成。如果重复率超过5%基本可以判定量化出了问题。8.3 定期重新量化模型更新、数据分布变化、硬件平台更换都需要重新量化。我一般每季度重新跑一次量化流程用最新的校准数据。这个过程虽然繁琐但能保证模型始终处于最优状态。重新量化时建议保留之前的量化配置作为基线只调整一个参数观察效果变化。比如先固定组大小128调整保护比例再固定保护比例1%调整组大小。这样能快速定位最优配置。9. 个人实操体会与建议我在多个端侧项目里用过AWQ踩过的坑不少总结下来有几个点值得反复提醒。第一校准数据一定要用目标场景的数据不要图省事用通用语料。我见过一个项目用英文维基校准中文医疗问答模型结果模型在医疗术语上频繁出错排查了一周才发现是校准数据的问题。第二组大小不要盲目调小128在大多数场景下够用调小反而可能因为反量化开销导致速度下降。第三量化后一定要做端到端测试不要只看PPL。PPL涨3%可能生成质量已经崩了必须用真实case验证。还有一个经验AWQ的量化脚本最好封装成可配置的流水线把校准数据路径、组大小、保护比例、输出路径都做成参数。这样换模型或换场景时只需要改配置不用改代码。我现在的量化流水线大概200行代码支持一键量化、自动评估、生成报告效率比手动跑高很多。最后分享一个小技巧如果你不确定保护比例设多少可以先跑一遍保护比例0.5%、1%、2%三组配置用验证集评估精度和延迟选性价比最高的。大多数情况下1%是最优解但有些模型0.5%就够了有些需要2%。实测数据比理论推导靠谱。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进