首页 / 资讯中心 / 文章详情

大模型量化实战指南:从INT4原理到GPTQ/AWQ部署避坑

大模型量化实战指南:从INT4原理到GPTQ/AWQ部署避坑 ★ FEATURED ARTICLE
最近大半年一直在跟大模型推理部署较劲LLM量化这块算是从原理到踩坑都过了一遍。每次看到群里还有人问“量化之后模型是不是就废了”“INT4到底能不能用”我就想干脆把这段时间的实践整理成一篇完整的东西。这篇文章不聊那种纯理论推导更多是我实际跑模型、调参数、排查问题时的真实记录包括方案怎么选、步骤怎么做、哪些坑必须避开希望对准备上手量化的朋友有点实际帮助。我先把结论放在前面LLM量化不是一个“要不要做”的问题而是“怎么做才不亏”的问题。7B模型FP16权重就要占14GB量化到INT4只有3.5GB左右一台16GB显存的消费级卡就能轻松带起来甚至CPU都能较流畅地跑。代价是精度损失但这个损失可以通过选对方案、校准数据和量化粒度控制在几乎看不出来的程度。下面我会把原理、方案、实操和排查分开来写。1. 量化到底在干什么把精度换成显存和速度1.1 浮点数与低比特的换算逻辑大模型的权重和激活值默认是FP16或者FP32存的。FP16用2字节表示一个数FP32用4字节。量化做的事情很直接——把32位浮点或者16位浮点压成8位整数、4位整数甚至3位、2位。换算关系很直观一个FP16权重占2字节INT8占1字节INT4只占0.5字节。7B模型按7×10⁹个参数算FP167×10⁹ × 2B 14GBINT87×10⁹ × 1B 7GBINT47×10⁹ × 0.5B 3.5GB这还没算KV Cache和激活值。所以为什么大家都在做量化最直接的原因就是模型装不装得下、跑不跑得动。但量化不是简单地把数字截断。FP16的取值范围和精度都远大于INT8直接截断等于把0.3存成0模型输出直接乱掉。实际做法是找一个缩放系数scale和一个偏移zero_point把这个张量里的数值范围映射到整数范围。反量化公式是r (q - zero_point) × scale其中q是量化后的整数scale是浮点缩放因子zero_point是整数偏移r是还原后的浮点值。正向计算时权重用低比特存计算前反量化成浮点或者使用特殊的融合核函数在整数域直接做矩阵乘避免来回转换的开销。1.2 对称量化与非对称量化的选择按是否带偏移量化分成对称和非对称两种。对称量化假设数据范围是[-a, a]zero_point固定为0实现简单INT8的硬件支持也最好。非对称量化允许正负范围不对等多存一个zero_point对激活值这种分布不均衡的数据更友好。我实际测试下来权重用对称量化就够了因为权重分布一般接近高斯分布正负对称。激活值建议非对称否则负值区间会被浪费量化精度损失加大。这里有一个特别容易被忽略的点量化粒度。同一个Tensor里不同通道的数值范围可能差好几倍如果整层统一一个scale小的通道会被压到几乎无法区分。所以现在主流做法是per-channel或者per-group量化。per-channel对每一行/每一列单独算scaleper-group更细比如GPTQ里常见的group_size128就是每128个参数一组每组一个scale和zero_point。粒度越细精度越高但存储和计算的开销也更大128是目前性价比比较好的平衡点。1.3 动态量化与静态量化的区别量化还可以按scale的确定时机分成动态和静态。动态量化在推理时才计算激活值的scale每一条数据不一样精度最好但统计开销大且很多算子不支持。静态量化用一批校准数据提前统计出固定scale推理时直接查表速度更快也更适合部署。对大模型来说主流方案基本都走静态量化路线。不管是GPTQ、AWQ还是llama.cpp里的GGUF都是先拿一小批数据喂给模型统计激活值分布算出量化参数然后才是真正的推理阶段。这里校准数据集的选择会直接影响量化质量后面实操部分我会详细说。2. 主流量化方案怎么选PTQ、GPTQ、AWQ与GGUF2.1 常见方案对比表现在提到LLM量化绕不开这几个名字bitsandbytes的load_in_8bit和load_in_4bit、GPTQ、AWQ、GGUF还有相对小众一点的SqueezeLLM、QLoRA里的NF4。它们都属于训练后量化不需要重新训练大模型区别主要体现在误差补偿策略上。方案基本思路典型位宽适合场景bitsandbytes分块量化每块独立scaleINT8 / 4bit NF4快速体验、单卡加载GPTQ基于Hessian矩阵的逐层误差补偿INT3 / INT4 / INT8追求高压缩比和精度平衡AWQ根据激活值分布保护敏感通道INT4 / INT3权重分布不均匀时GGUF (K-quants)按重要程度分配位宽混合精度2-8bitCPU部署、边缘设备2.2 GPTQ为什么能做到低比特高精度GPTQ的核心思想不是简单把数值截短而是“量化一层修正一层”。它利用二阶信息Hessian矩阵算出哪些权重对模型输出的影响最大然后逐个权重调整让整层的输出尽量接近量化前的状态。我最早跑GPTQ时用的是某开源社区提供的INT4模型一个7B模型量化到4bit后在中文问答场景下依然能保持基本流畅的对话能力偶尔会出现轻微语句重复但整体可用。这个方案的缺点是需要跑校准流程耗时取决于模型大小和校准集长度7B在普通消费级显卡上大约需要十几分钟到半小时。实操中我建议优先使用别人已经量化好的公开权重省时省力。如果需要自己量化校准数据集长度控制在128到512条之间效果比较稳定太少会过拟合太多耗时翻倍但收益很小。2.3 AWQ与感知缩放AWQ的全称是Activation-aware Weight Quantization核心洞察是权重里不同的通道重要性不同而通道的重要性可以通过对应激活值的幅度来判断。激活值大的通道对应的权重误差会被放大所以AWQ会为这些通道保留更高精度的表示而不是对所有通道一视同仁。和GPTQ相比AWQ不依赖反向传播求Hessian校准速度更快而且有研究表明在部分任务上AWQ的低比特精度甚至优于GPTQ。我实测的感受是两者差距其实很小更多是看部署框架的适配性。如果你用vLLM或者TensorRT-LLMAWQ的支持度正在快速追上GPTQ可以两个都试一下用你自己的业务数据跑一遍看效果。2.4 GGUF与llama.cpp的CPU部署GGUF是llama.cpp项目定义的模型格式它的一大特性是支持K-quants量化——也就是混合精度量化不同层根据重要性分配不同位宽比如非常敏感的层保留6bit次敏感的用5bit一般层用4bit甚至3bit。这种“好钢用在刀刃上”的思路让GGUF在低比特下依然能保持不错的质量。如果你要在CPU上跑模型或者部署到树莓派这类资源非常有限的设备上GGUF基本是最优选择。我试过把一个13B模型量化为Q4_K_M格式在一颗普通桌面级CPU上速度大概能到每秒4到6个token用来做离线文本生成、知识库检索问答完全够用。需要提醒的是GGUF文件的后缀参数Q4_K_M、Q5_K_S、Q8_0等不只是压缩率区别还决定了质量下限别为了省几百MB盲目选超低位宽。3. 实操流程从环境准备到推理加速的关键步骤3.1 环境与依赖准备不管你用哪种方案第一步都是把环境跑通。我这边用的组合是Python 3.10PyTorch 2.xTransformers 4.36CUDA 12.1依赖安装没有特别多的坑但要注意CUDA版本和PyTorch的对应关系。如果不匹配后面加载模型很容易报“找不到libcublas”之类的错误。一个判断经验先跑一段最简单的模型加载代码能过再继续不要跳到量化步骤之后再回来查环境问题。3.2 使用HuggingFace生态快速体验低比特加载最简单的上手方式是用Transformers配合bitsandbytes在加载模型时直接指定load_in_4bitTruefrom transformers import AutoModelForCausalLM, AutoTokenizer model_id 某开源对话模型 tokenizer AutoTokenizer.from_pretrained(model_id) model AutoModelForCausalLM.from_pretrained( model_id, load_in_4bitTrue, device_mapauto, torch_dtypeauto )这里有个关键参数device_mapauto它会在显存不够的时候自动把部分层放到CPU上。我自己的经验是能不开就不开因为CPU与GPU之间的传输会拖慢速度。如果显存确实紧张优先考虑减小max_length或者使用梯度检查点之类的方式而不是依赖跨设备调度。3.3 用GPTQ自己做一次模型量化如果手头有开源量化脚本或AutoGPTQ库自己量化也很方便。流程非常简单加载FP16模型、准备校准数据、逐层量化、保存权重。我实际量化一个7B模型的完整流程大概是这样的# 使用AutoGPTQ进行量化 python quantize.py \ --model_path 某开源对话模型 \ --output_path ./output_model \ --bits 4 \ --group_size 128 \ --dataset c4 \ --sample_size 128运行时间在一个小时以内具体看GPU性能。我有一次贪快把sample_size降到32结果量化后的模型在长文本生成时明显变笨开始重复轮回。后来把样本数恢复到128问题基本消失。校准集宁可多不要少128到256条是舒服区间。量化完成后加载方式会很顺滑from transformers import AutoModelForCausalLM model AutoModelForCausalLM.from_pretrained( ./output_model, device_mapcuda:0 )3.4 显存占用与速度的实测对比我自己用同一批测试数据整理过一个对比表格方便直观感受量化带来的收益配置权重显存生成速度首token延迟FP1614GB42 tokens/s350msINT87GB46 tokens/s330msINT43.5GB51 tokens/s310ms这个趋势是典型的模型越大、生成任务越长量化带来的收益越明显。量化之后权重变小访存压力降低尤其对长上下文的推理优势会被持续放大。但要注意显存占用不等于模型大小。一个常见误区是看到量化后权重3.5GB就觉得4GB显卡能跑实际跑起来还要算KV Cache和激活值。一个7B模型在4096长度上下文的场景下KV Cache大概要多占0.5到1GB再加上CUDA context和其他开销8GB显存比较稳4GB会很勉强。3.5 校准数据集的选择量化方案的校准数据本质是模拟模型在真实场景中的激活值分布。你用什么数据去校准量化参数就会偏向什么数据。比如用英文通用语料校准的模型在处理中文法律文本时精度损失就会相对偏大。我的操作经验是先拿业务场景里的真实数据比如历史对话记录、线上日志切成128到256段文本再加上一部分通用语料混合比例大概7:3。这样既能保证业务场景的适配性又不容易过拟合。校准时的max_length建议设置成256到512之间太长耗时增加太短捕捉不到长距离依赖关系。4. 量化参数选择别只盯着位宽看4.1 位宽与质量的权衡逻辑很多人问“INT4和INT8选哪个”。答案取决于三件事模型大小、目标硬件和精度要求。对7B模型INT4可以把它压到一张8GB卡上而INT8需要12GB左右对70B模型INT8需要70GB左右INT4只需要35GB。硬件边界决定了下限精度需求决定上限。我现在的选择策略是消费级单卡、追求流畅交互INT4服务端多卡、追求稳定性INT8离线批量处理、不赶时间保留FP16或BF16不量化4.2 group_size与batch_size的影响group_size的选择对精度影响比很多人想象的大。group_size从128降到64量化精度会更高但存储开销也会变大。以INT4为例group_size128的情况下每128个权重共享一组scale和zero_point换成group_size64记录scale的密度翻倍实际存储会超过理论0.5字节/参数。我在一个代码生成类模型上测试过group_size存储开销代码补全准确率32较高最高64中等较高128较低可接受256很低明显下降所以group_size不是越大越好。追求质量用64追求容量用128256只适用于对精度不太敏感的场景。4.3 敏感层保留高精度混合精度量化有些模型关键信息集中在特定层上比如某些注意力层或MLP的特定输出层。把这些层也一并量化为INT4损失会很明显。解决方案是混合精度量化大部分层用INT4少数敏感层用INT8甚至FP16保留。llama.cpp的K-quants就是这个思路的实践它会对不同层自动分配不同位宽比如attention层权重分配更高精度FFN层用较低精度。实测下来这种处理方式在低比特下比统一位宽好不少。自己在做量化方案时也可以沿用这个思路先用FP16跑一版逐层替换成INT4看替换到哪层输出质量开始严重下滑那层就保留高精度。5. 推理加速与部署落地量化之后的下一步5.1 选择合适的推理框架模型量化完之后真正的考验是能不能跑得快。Transformers库虽然方便但逐层推理的效率和优化空间有限。实际部署时我一般会根据硬件选择专用框架GPU部署首选vLLM支持PagedAttention显存利用率和吞吐量优势明显CPU部署用llama.cpp自带GGUF支持对低资源环境非常友好有TensorRT经验的可以选TensorRT-LLM性能和延迟都能再压一截vLLM里加载量化模型很简单from vllm import LLM llm LLM(model某模型路径, quantizationgptq)5.2 长上下文场景的KV Cache优化量化权重只是内存优化的第一步。随着上下文长度增加KV Cache会快速增长这通常是大模型部署时真正的隐形杀手。KV Cache同样可以量化。vLLM支持将KV Cache量化为INT8或INT4通过参数指定缓存数据类型。我实测在长对话场景下KV Cache量化后显存占用能够降低30%到50%且推理质量影响很小因为KV Cache中的数值分布在经过处理后相对稳定。5.3 吞吐量与并发之间的取舍很多人在并发调优时会发现把模型量化到INT4后单条请求确实变快了但并发一高吞吐量却上不去。原因很简单INT4模型在GPU上需要额外的反量化操作矩阵计算的并发效率反而比INT8差一些。如果你的服务对并发吞吐有硬性要求可以跑个对比测试INT8权重 高并发batch和INT4权重 低并发batch看看哪个更贴近业务目标。我自己的经验是高并发在线服务用INT8更稳端侧或离线场景用INT4更值。6. 常见问题与排查心得6.1 量化后模型胡言乱语怎么办这个是遇到最多的反馈。先不要急着骂量化方案垃圾按顺序排查校准数据集是否覆盖业务场景代码模型用英文维基校准效果不可能好group_size是否过大256以上在很多场景下都不推荐是否所有层都被量化了有些敏感层需要保留高精度模型原本的健壮性如何我见过一个很脆的模型FP16下回答都一般量化后更是乱成一团排查顺序按这个来大部分情况都能定位到问题。6.2 显存没降多少量化是不是失效了这种情况通常是KV Cache和激活值在主导显存。一个7B INT4模型权重降到4GB但如果上下文拉到8192KV Cache能再占掉几个GB。你感觉显存“没少”大概率是总显存被上下文长度拉平了。技巧用命令查看模型自身参数占用和KV Cache占用先确认大头在哪再决定要不要做KV Cache量化。6.3 量化后速度反而变慢低比特权重在CPU上不一定更快。原因有两个一是反量化操作本身耗时二是很多框架对INT4的算子优化还不到位。判断方式很简单统一跑一个500 token的长生成任务用生成的tokens/s和首个token延迟来评估不要凭感觉。6.4 量化模型在部分任务上精度损失明显有些场景对模型输出的“细微语义”极其敏感比如文本分类、实体抽取这类任务INT4确实可能出现掉点。这类场景我一般只做INT8甚至保留FP16因为显存访问延迟不是首要矛盾准确率才是。7. 一些踩过坑之后想说的话如果要我总结一句话那就是量化不是把精度扔掉而是把模型里的冗余去掉。一个好的量化模型应该在推理质量没有明显滑坡的前提下让你在更便宜的硬件上跑起来。别一味追求最低位宽也别一听说量化就害怕关键是用数据说话。我现在做量化项目时会固定跑三个指标困惑度、下游任务准确率、实际生成速度。三个指标一起看哪个不达标就回到前面对应的环节调参。这套流程帮我避免了很多“凭感觉做量化”的坑。最后再分享一个小技巧拿到一个新的开源模型时先别急着自己量化先找社区有没有已经量化好并且口碑不错的版本用业务数据测一遍。如果满足需求就省事很多不满足再自己动手。毕竟量化本身不是目的让模型在你预算范围内的硬件上跑起来、跑得好这才是目的。
阅读完成 · 觉得有帮助?
咨询建站