
你蹲在服务器前面看着模型加载进度条一点一点往前走显存占用数字却跟血压计一样往上升。好不容易跑起来并发一高显存直接爆掉。这是很多人在本地部署大模型时都遇到过的场景。而我最近几个月一直在折腾大模型量化从最基础的INT8到GPTQ、AWQ这些更聪明的权重量化方案算是把降低部署成本这条路踩了个遍。这篇文章就是我做量化实战的完整记录会从原理讲到部署把那些网上很难查到的坑一并填上。现在的开源大模型动辄7B、13B起步FP16权重就要占14GB到26GB显存很多人的显卡根本跑不动更别提放到生产环境里去支撑业务。量化的本质就一句话用更少的比特数去近似表示原始权重让模型体积变小、推理变快、显存占用降下来。INT8把每个权重从16比特压缩到8比特直接省一半GPTQ和AWQ则是更激进的4比特方案能把7B模型从14GB压到4GB左右普通消费级显卡都能跑。这篇文章适合谁看正在做模型本地化部署的开发者、想要降低推理成本的技术决策者、以及对量化原理有好奇心的同学。1. 量化到底在做什么从FP16到INT8的存储与计算逻辑在真正动手之前得先把量化的底层逻辑讲透。很多人觉得量化就是个“缩小模型”的黑科技其实背后的数学非常朴素就是做一次数值映射。1.1 数据格式与显存换算的直观理解FP16和INT8的区别得从计算机怎么存数字说起。FP16用16个比特位去表示一个数其中有符号位、指数位和尾数位它能表达的范围很大但代价就是每个数要占用2个字节。INT8用8个比特位去表示数通常就是-128到127之间的整数每个数只占1个字节。举个例子一个7B参数量的模型FP16权重就是 7 × 2 14GB换成INT8就是 7 × 1 7GB再做4比特量化就是 7 × 0.5 3.5GB。这就是量化最直接的收益——显存占用大幅下降。这里要注意量化掉的不是参数量而是每个参数的存储精度。模型还是那个模型但数值从“精确的小数”变成了“接近的整数”。这就好比记账的时候原来你记每一笔都要精确到分现在只记到元账虽然说粗略了一点但总量不会差太多。当然差别大不大取决于你记账的方式聪不聪明这就是GPTQ和AWQ存在的意义。1.2 量化误差的来源从四舍五入到校准最朴素的量化方式是RTNRound to Nearest直接把浮点数四舍五入到最近的整数。比如权重是0.3那量化后就是0。看起来很方便但对大模型来说几亿个参数这么一折腾误差不断累积输出质量可能就崩了。为什么会这样因为量化实际上是有损压缩。FP16到INT8的映射过程本质上是把一个连续区间压缩到128个离散点上。这个过程中每个点的近似误差看似不大但大模型动辄几十层层层叠加误差就会被放大。更麻烦的是不同层对误差的敏感度完全不一样。有些层的权重分布很均匀量化后损失很小有些层的权重有几个特别大的异常值其他值都很小这种分布量化起来就非常吃亏。这也是为什么单纯做RTN量化经常会出现“量化后数值不动”或者“跑通但生成质量严重下降”的原因。1.3 量化粒度和分组per-tensor、per-channel与group size实际做量化时还有一个维度很关键——量化粒度也就是大家都在说的per-tensor、per-channel和group。用生活化的类比解释一下。假设你要压缩一组数值per-tensor的意思是整组共享一个缩放系数相当于给每个人发同样的限额per-channel是按每一列/每一行单独算一个缩放系数相当于每个人按自己的收入单独设额度group则是按一小块一小块分组每组单独计算是前两者的折中。实操中W8A8权重和激活都是8比特常用per-channel处理权重、per-tensor处理激活而GPTQ这类4比特方案引入了一个叫做group size的参数通常取128或32意思是每128个参数共享一组量化参数。group size越小量化粒度越细精度保留越好但存储的元数据也就越多。我一般首选group size 128它在精度和压缩率之间比较均衡。如果模型用128量化后精度下降明显需要排查校准数据、量化敏感层等问题这时再尝试32通常会有更精细的量化效果但模型体积也会相应增大。2. INT8量化实战与精度问题排查INT8量化是门槛最低的入门方案也是很多人在本地项目里第一个尝试的路径。但它远没有想象中那么简单尤其是当你在ONNX Runtime或者TensorRT里跑INT8时精度下降和数值不动的坑几乎人人都会踩到。2.1 从动态量化到静态量化先搞清楚你在做哪一种ONNX Runtime里INT8量化主要分两种动态量化和静态量化。动态量化比较好理解就是运行时根据实际激活值动态计算缩放参数精度保留相对好一点但推理时会多出计算量。静态量化则是提前用校准数据把缩放参数算好推理时直接复用推理速度快但精度强依赖校准集的代表性。很多人在网上看教程上来就调一句话搞定动态量化但上线后发现延迟不达标又改去做静态量化。我建议如果你面向生产部署直接一步到位做静态量化。动态量化的计算开销在CPU上可能不明显但在GPU或者端侧设备上会被放大。具体做静态量化时校准集的选择是关键中的关键。这里的校准集不是让你拿训练集全量跑一遍那是开玩笑而是从真实推理场景里抽一小部分代表性样本。我自己的做法是从实际业务数据里随机抽500到1000条样本覆盖各种输入长度和文本风格。注意一点校准集的数据分布要和线上真实分布尽量一致否则算出来的缩放系数会整体偏移精度直接崩。2.2 “量化后精度下降数值不动”的根因分析回到很多论坛里问得最多的问题“我刚量化完INT8模型跑推理出来数值不变完全等于原FP16的预测结果这是怎么回事”这个问题初看很诡异但排查思路其实很清晰。数值完全不动说明你的推理根本没有走量化后的计算路径。最常见的原因是量化后的模型在推理时仍然回退到了FP32/FP16实现比如在ONNX Runtime里某些算子不支持INT8执行运行时就会自动插入反量化节点实际上用的还是浮点计算。表面上跑的是量化模型实际上精度没有任何变化自然也没看到显存和速度的改善。那怎么确认模型真的走了INT8计算路径在ONNX Runtime里加上session_options的profiling或者直接打开enable_profilingTrue跑一次推理后去生成的JSON文件里看每个节点的执行类型。如果看到大量DequantizeLinear之后接着QuantizeLinear那就说明算子没有融合成功整个模型存在严重的精度回退问题。另外一个常见的坑是你把模型从FP16先转成FP32再做INT8或者反过来导致某些中间层的数值范围严重超出预期量化后的缩放系数变得极其离谱最终输出数值被极端压缩看起来就好像“数值不动”。2.3 哪些层必须保留高精度识别量化敏感的算子即便算子支持良好完全走INT8路径不同层的精度损失也大不相同。我在实际项目中做了一个很朴素的实验把模型每一层单独量化其余层保持FP16跑验证集看PPL困惑度变化。结论是LayerNorm和残差连接相关的算子基本不能量化部分Embedding层也不建议碰。原因很简单LayerNorm的目标是标准化到均值为0、方差为1数值分布非常集中一旦量化细微的数值差异就会被抹掉模型输出就会产生漂移。所以几乎所有量化框架默认都会把LayerNorm保留为高精度。实操上如果你在用ONNX Runtime的量化工具可以通过nodes_to_exclude参数把LayerNorm、Softmax、Gelu这些算子排除在量化之外。这在精度敏感的任务里几乎是必选项要认真对待。我测过7B模型如果不排除这些算子量化后PPL会从6.5涨到9以上排除之后能控制在7.2左右。3. GPTQ与AWQ4比特权重量化的原理与选型如果说INT8是一种“有手就行”的量化方案那GPTQ和AWQ就是把量化当成一门精密手艺通过算法层面的优化把4比特量化做到了几乎无损的境地。3.1 GPTQ原理基于Hessian矩阵的逐层补偿GPTQ的核心思想是量化不是一次性把所有参数批量四舍五入而是逐层、逐列地进行优化补偿。它借鉴了OBQOptimal Brain Quantization的思路——量化一个权重后用Hessian矩阵来估算这个误差对后续权重的影响然后调整剩余未量化的权重去补偿已经产生的误差。这个流程很像你整理行李箱你放进去一个体积比较大的东西发现剩下某个区域塞不下了就会调整旁边物品的位置来腾出空间。GPTQ在量化过程中也要对权重做类似的“重新摆放”让整体误差降到最低。不过OBQ一次只处理一个权重在大模型上百亿参数面前计算开销太大了。GPTQ的工程贡献在于引入了批量化处理一次量化多列并利用了Hessian矩阵的数值结构来加速运算使得原本慢到不能忍的优化过程在单个GPU上就能对7B模型完成量化。使用GPTQ时有一个关键参数要引起重视desc_act全称是activation descending。开启后量化会按照激活值的敏感度从高到低排列权重列优先量化不敏感的列。这个设置虽然会增加权重和显存的额外开销但对于敏感度差异大的模型能明显改善精度。实践中7B模型通常建议开启它。3.2 AWQ原理不搞迭代精准保护重要通道AWQActivation-aware Weight Quantization的设计思路和GPTQ完全不同。它的核心假设是权重的重要性不取决于权重自身分布而取决于它对激活值的敏感度。换句话说那些被大激活值“命中”的权重通道对模型输出影响最大。打个比方一段路有的车道车流量特别大有的车道几乎没车。交通管制时优先保畅通行的肯定是大车流车道。AWQ做的就是找到这些“大车流车道”——即对激活值影响最大的通道然后在量化时对它们的缩放因子做特殊保护。这个做法的巧妙之处在于它不需要像GPTQ那样迭代计算也不依赖重训练只通过统计少量校准数据的激活值找到敏感通道然后微调缩放因子就能完成量化。所以AWQ的速度非常快而且泛化能力更号即使校准集的分布偏移了一点精度的衰减也不会那么剧烈。3.3 GPTQ vs AWQ实测对比与选型建议参数对比放在表格里更直观。我基于7B和13B模型各做了几组测试综合来看对比项GPTQAWQ量化原理基于Hessian矩阵逐层补偿误差基于激活值感知保护敏感通道是否迭代优化是耗时较长否速度快很多校准数据要求依赖校准集数据偏移影响较大对校准集分布相对不敏感推理速度在部分GPU上更快在部分硬件上差异不大模型体积4bit约3.5-4GB/7B模型与GPTQ接近常见部署端vLLM、ExLlama、transformersvLLM、SGLang、TinyChat很多人的刻板印象是GPTQ精度一定比AWQ好其实不一定。我测试了几个真实业务模型后发现AWQ在很多场景下的PPL甚至略优于GPTQ而且因为不需要复杂的优化迭代整个量化流程快了好几倍。再加上AWQ对校准集的依赖更弱如果你在管线里经常要换模型AWQ的泛化性会让你省心很多。选型建议是这样的如果你的目标是上线稳定服务有充分时间做离线校准和精度验证优先考虑GPTQ如果你需要快速迭代、频繁量化新模型或者数据分布不太稳定AWQ会更顺手。4. 部署实操从权重下载到推理验证前面讲的都是原理接下来进入动手环节。我会用transformers配合auto-gptq和autoawq这两个常见库带你走一遍完整的量化部署流程顺便把我在实操中遇到的高频报错一并讲清楚。4.1 环境准备与库安装先说要装哪些东西。基础环境是PyTorch然后根据模型和方案选择量化库。如果是GPTQ可以用AutoGPTQ库也可以直接用transformers的集成接口如果是AWQ就用AutoAWQ库。我建议直接使用最新版的transformers再配合对应的量化后端库。pip install torch torchvision torchaudio pip install transformers accelerate # GPTQ pip install auto-gptq # AWQ pip install autoawq安装时有两个小细节要注意。第一auto-gptq和autoawq都可能依赖特定版本的CUDA工具链如果你的CUDA版本太老编译安装会直接报错建议先确认显卡驱动支持的CUDA版本用对应型号的PyTorch预编译包。第二bitsandbytes是另一个常用的量化方案主要是NF4但它的API和GPTQ/AWQ完全不同别跟这俩混在一起装了。4.2 GPTQ模型加载与离线量化操作如果你用的是别人已经量化好的GPTQ模型加载非常省事。直接指定device_mapauto加载速度和解码速度都会有明显进步from transformers import AutoModelForCausalLM, AutoTokenizer model_id TheBloke/Llama-2-7B-Chat-GPTQ model AutoModelForCausalLM.from_pretrained( model_id, device_mapauto, trust_remote_codeTrue ) tokenizer AutoTokenizer.from_pretrained(model_id) input_ids tokenizer(请介绍一下量化技术, return_tensorspt).input_ids.cuda() output model.generate(input_ids, max_new_tokens128) print(tokenizer.decode(output[0], skip_special_tokensTrue))但如果你手头有一个自己的微调模型想自己做GPTQ量化那就需要进入离线量化流程了。这里要依靠AutoGPTQ提供的接口from auto_gptq import AutoGPTQForCausalLM, BaseQuantizeConfig quantize_config BaseQuantizeConfig( bits4, group_size128, desc_actTrue, damp_percent0.01, ) model AutoGPTQForCausalLM.from_pretrained( your-base-model-path, quantize_configquantize_config, trust_remote_codeTrue, ) # 你的校准数据收集完成后不换行直接接在下一个命令块后 calibration_data [ 量化技术是降低大模型推理成本的关键路径, 激活值感知量化在近期研究中取得了显著进展, 前向传播过程中需要关注数值精度的影响, ] model.quantize(calibration_data) model.save_quantized(your-quantized-output-path)校准数据的质量在这里体现得格外明显。我建议不要随便从训练集里截片段而是准备一些和线上服务真实输入风格接近的句子长度、用词都要有代表性。我曾经拿一套开源通用语料做校准量化出来PPL涨了快1.5换成业务真实请求数据后损失降到了0.3以内。4.3 AWQ模型量化体验语法简洁很多AWQ的量化接口相比GPTQ要简洁很多核心就两步加载模型、跑量化save。下面用awq.quantize来操作from transformers import AutoTokenizer from awq import AutoAWQForCausalLM model_path your-base-model-path quant_path your-awq-output-path quant_config {zero_point: True, q_group_size: 128, w_bit: 4, version: GEMM} tokenizer AutoTokenizer.from_pretrained(model_path, trust_remote_codeTrue) model AutoAWQForCausalLM.from_pretrained(model_path, trust_remote_codeTrue) # 校准样例 calibration_data [ 量化技术是降低大模型推理成本的关键路径, 激活值感知量化在近期研究中取得了显著进展, ] model.quantize(tokenizer, quant_configquant_config, calib_datacalibration_data) model.save_quantized(quant_path)这里有个参数值得单独拿出来讲——version。AWQ主流的kernel版本有GEMM和GEMVGEMM适合批量推理场景GEMV在单Token逐个生成也就是大模型的decode阶段的时候会更快。如果你用vLLM这类推理服务vLLM内部对AWQ的kernel有自己的选择逻辑不一定要在AWQ量化阶段指定但如果你是直接加载模型做推理version的差异会影响吞吐。4.4 推理过程中的典型报错cannot find the config file for AWQ操作中会遇到的报错几乎是一道必考题。很多人在加载AWQ模型时都会遇到ValueError: Cannot find the config file for AWQ.这个报错的字面意思是找不到AWQ对应的配置文件。最常见的触发原因是你用的是transformers的常规加载方法但AWQ模型目录下没有完整的配置文件或者quant_config.json缺失、损坏。还有一种情况是模型本身是旧版本AWQ导出的格式跟当前库不完全兼容。我最常采用的修法非常简单去模型的仓库页检查是否包含config.json和quant_config.json这两个文件如果没有就先用模型发布者提供的正确文件补进去重新加载如果发布者没有提供换个官方确认过的仓库版本通常就能解决。另外如果你是自己在本地用AutoAWQ量化后保存的模型加载前要先确认生成目录里是否完整包含了quant_config.json以及model.safetensorsmodel.safetensors.index.json是否正常。保存路径不能有中文或空格否则个别旧版库在拼路径时会出幺蛾子。5. 端侧与嵌入式设备ONNX INT8和RKNN部署的特殊性桌面端GPU跑量化模型是一回事在端侧设备上安卓手机、树莓派、RK3588这类开发板又是另一回事。近半年来这个方向的咨询量增长非常明显核心原因无非是越来越多的人想把模型推到端侧绕开服务器成本顺便把用户隐私留在本机。5.1 为什么端侧量化链路更痛苦从PyTorch到ONNX再到RKNN在GPU上你拿到一个safetensors权重文件就能直接加载很省心。但到了端侧尤其是走Rockchip芯片方案时链路就长得多PyTorch模型先转成ONNXONNX再做INT8静态量化最后转成RKNN格式。很多人在这一步就开始撞墙。PyTorch动态图某些操作比如varying-length输入、带条件的控制流根本没法导出成静态的ONNX图。即便导出成功ONNX阶段做INT8量化算子支持也是一个巨坑。RKNN-Toolkit在转模型时并非所有ONNX算子都有对应的NPU实现遇到不支持的算子轻则报警告重则边缘设备直接cpu回退整个算力优势荡然无存。5.2 onnx转rknn的int8量化要点先给一个最直接的结论ONNX转换RKNN时量化失败率最高的原因往往是原始ONNX模型里混入了大量未标定的动态shape而后端RKNN做静态shape推理时直接报错。所以第一步就是把模型固定到典型输入尺寸上再把动态轴改成静态。ONNX静态量化这一步的选择会对后续RKNN量化效果产生直接影响。如果ONNX阶段已经做过INT8量化转成RKNN时通常不需要再做一遍量化校准直接复用权重和缩放系数即可如果你只在ONNX层保存FP32权重把量化完全交给RKNN-Toolkit去做那么校准集的选取要使用RKNN提供的quantized_dataloader并准备几百张有代表性的图片或文本序列。RKNN的int8量化比较容易“数值不动”的原因我提一下端侧芯片对int8的计算通常有对齐要求如果你的输入tensor没有按正确的布局或通道对齐方式排列NPU内部的算子可能直接跳过或做了截断输出就恒定了。遇到这种情况建议先检查输入格式再做一次简单的单算子调试而不是直接怀疑RKNN量化算法本身。5.3 端侧低比特方案NVFP4、int4与未来的方向搜索热词里还有一条消息值得展开部分硬件厂商已经开始在自家芯片里支持NVFP4、INT4这类更极端的低比特数据格式。这意味着4比特模型的推理不一定非得靠软件的GPTQ/AWQ去模拟芯片已经原生支持这种精度的矩阵运算了性能自然更优。我个人的直觉是端侧大模型的下一步不是继续只盯INT8而是向INT4甚至混合精度方向发展。未来大概率会出现一种“感知层用INT8/INT4、注意力层用混合精度、非线性层保留FP16”的混合量化方案让算力和精度达到一个更极致的平衡。6. 常见问题速查表与踩坑经验总结实战阶段遇到的杂七杂八的问题实在太多我挑几个高频的整理成一张速查表方便你直接对照排查。这也是我写这篇文章的初衷之一——把文档里查不到的经验沉淀下来。问题现象可能原因排查与解决INT8量化后输出数值与FP16完全一致算子回退到浮点执行或量化节点没有融合打开profiling查看节点执行情况确认是否存在反量化节点量化后精度明显下降校准集分布和线上输入不一致使用业务真实数据重新校准排除LayerNorm/Softmax等敏感算子报错“cannot find the config file for awq”模型目录缺少quant_config.json检查配置完整性重新下载官方权重避免路径含中文7B模型AWQ加载慢权重文件为未量化格式或IO瓶颈确认量化后文件小于1GB个文件否则做分片存储onnx转rknn时报算子不支持模型中存在动态shape或自定义算子固定输入尺寸替换不支持的算子为等效实现GPTQ量化时显存爆掉batch_size或seq_len过大减小校准集样本长度适当增加damp_percent最后分享一个我自己的小技巧不管用什么量化方案量化之后不要只盯着PPL看一定得在真实的业务 prompt 上做一遍生成质量的抽查。PPL是一个宏观指标它好代表整体偏差可控但一些安全敏感回复或者某个特定领域的表达还是必须在量化前后做一次对比。我在实际项目里就遇到过一个模型PPL漂移很小、但特定邮编地址的生成全错的情况后来排查发现是这个领域的数据在量化敏感通道里扎堆出现了异常。量化之外的一些总结大模型量化不是什么黑魔法本质上就是用合理的数值近似换取更低的存储和算力成本。INT8方案门槛最低但精度问题需要你仔细排查算子回退和校准集选择GPTQ和AWQ把4比特量化做到了实用级别是目前私有化部署的主流方案两者的技术路线不同各有各的适用场景。我踩过最深的坑是以为量化完模型就算部署完事了结果上线之后显存确实降了但延迟没有任何改善。后来才意识到量化带来的好处不只有显存压缩如果你用的推理框架没有针对低比特做算子优化单纯做权重量化并不能带来明显的速度提升。所以选型的时候一定要把推理框架一并考虑进去这也是我只建议用vLLM等专门优化过低比特算子的框架跑生产服务的原因。量化是一门性价比很高的工程手段但它不会把垃圾硬件变成神器它能做的是把你手上的资源利用到极致。