大模型量化从入门到选型:原理、方案与硬件真相
1. 先解决一个现实问题:为什么我要冒着精度风险去量化
1.1 显存是表象,内存带宽才是隐藏瓶颈
先说清楚,大模型量化里的“量化”,跟股票量化交易、资金量化指标完全是两个东西。这里说的量化,是把模型权重从FP16这种高精度浮点数,压成INT8、INT4这类低比特数值的技术手段。如果你是第一次被“CUDA out of memory”逼到这个领域,这篇文章能帮你把原理和选型一次理清。
很多教程喜欢把量化包装成数学魔法,实际上它就是一笔显存账加一笔带宽账。拿一块24GB显存的消费级显卡举例,想跑70B模型,光是FP16权重就有140GB,显存连零头都装不下。就算换成7B模型,FP16权重也要14GB,再算上KV Cache和激活值,长上下文一开,24GB照样紧张。我最初接触大模型量化,就是被这种“放不下、跑不动”的处境逼着去试的,后来发现事情远不止省显存这么简单。
生成每个token时,GPU原则上要把模型权重从显存里完整读一遍,这个读取成本直接受内存带宽约束。RTX 4090显存带宽约1008GB/s,一个7B的4bit量化模型权重约4GB,理论上每个token延迟下限是4GB除以1008GB/s,大概4毫秒,换算成速度能到250token/s左右。换成FP16的7B模型,权重14GB,延迟下限变成14毫秒,速度上限直接掉到70token/s上下。所以在显存和带宽双受限的场景里,量化不单是“塞得下”的问题,它直接决定了输出能跑多快。
CPU上的差异更夸张。普通双通道DDR5内存带宽只有90GB/s上下,FP16的7B模型读一遍要150毫秒以上,跑起来就是著名的“老牛拉车”;换成4bit模型,体积变小四倍,同样条件下能快三倍。这也是llama.cpp这类本地推理工具默认就推GGUF量化文件的原因——省显存是真的,变快也是真的。
1.2 量化不是简单“砍精度”,而是一个信息压缩问题
有人把量化理解为“把浮点数四舍五入成几个整数”,这个说法不算错,但漏了一个关键步骤:换算。一个1.23456的小数,直接塞进int8会丢掉大量信息;正确的做法是先定一个缩放因子(scale),把浮点数的分布范围映射到整数刻度上,取整后用的时候再按比例还原。这个“映射、取整、还原”的过程才是量化的本质。
从这个角度讲,量化本质是一个信息压缩问题:我要用更少的比特去表达原来数值的分布,关心的不是每个数还原得对不对,而是整层输出经过量化后误差够不够小。后面你会见到的GPTQ、AWQ、SmoothQuant、GGUF的K量化,不管名字多花哨,本质上都在回答同一个问题:这个映射怎么设计,才能让信息损失最小。
2. 量化原理的核心:scale、zero_point和反量化
2.1 浮点转整数就是“归一化后取整”
量化最基础的形式很简单,一个浮点数组r,量化到整数q,公式是:
q = clamp(round(r / scale) + zero_point, q_min, q_max)
反量化则把它还原回来:
r_approx = (q - zero_point) * scale
scale负责缩放,zero_point负责对齐零点,clamp负责把越界整数剪回合法范围。听起来简单,但scale怎么取,直接决定了量化误差长什么样。我写一段最朴素的示例代码,方便对照理解:
import numpy as np def quantize_asymmetric(r, bits=8): q_min, q_max = 0, 2 ** bits - 1 scale = (r.max() - r.min()) / (q_max - q_min) zero = q_min - np.round(r.min() / scale) q = np.round(r / scale + zero).clip(q_min, q_max) return q, scale, zero def dequantize(q, scale, zero): return (q - zero) * scale实际推理时,可以先把整数反量化回浮点再计算,也可以直接用整数算子计算。这两条路线在工程上差别很大:前者主要为了省存储和带宽,后者为了启用硬件加速计算。很多4bit方案属于前者,服务端常见的W8A8全整形推理属于后者。
2.2 对称与非对称:zero_point到底什么时候需要
权重分布通常以0为中心,两边大致对称,这时候用对称量化,强制zero_point等于0,公式能简化成:
q = clamp(round(r / scale), q_min, q_max)
scale直接用整个数组的最大绝对值除以整数上限。对称量化的好处是计算内核简单,很多硬件整数单元没有偏置加法,对称结构更友好。
但激活值分布不一定对称。经过ReLU之后,大量激活值是非负的,最小可能是0,最大可能是10,这时候硬套对称量化,等于拿[-127, 127]的刻度去表达[0, 10]的区间,一半整数空间被浪费,精度白白损失。所以一旦要量化激活、KV Cache,或者做全整形推理,非对称量化搭配逐通道、逐组的scale和zero_point,就变得非常重要。
权重量化方案里对称量化很常见,因为权重天生近似对称;激活量化必须考虑非对称,这是理解整篇内容的关键线索。
2.3 从INT8压到INT4:损失到底从哪来
INT8有256个整数刻度,INT4只有16个,表达能力强弱一眼可见。打个比方,FP32是把无限细分的游标卡尺,INT8是一把256格子的卷尺,INT4是一把只有16格子的粗尺,读数精度完全不同。
量化误差近似等于scale的一半,scale等于数值区间除以刻度数。区间越大,刻度越少,误差越大。到INT4这个位宽时,极端权重和长尾分布很难被16个格子覆盖,于是“模型变笨”的现象就出来了。其实不是模型变笨,是少数关键参数的表达精度跟不上。
这也是为什么后续所有高级量化方案都在避重就轻:把有限的刻度资源优先分给影响更大的权重,提前补偿掉那些可以预判的误差。理解了这一点,后面看任何量化论文都会轻松很多。
3. 主流量化方案的原理与取舍:从PTQ到GGUF
3.1 朴素舍入不够用,GPTQ用二阶信息补偿误差
最早期权重量化就是对浮点数直接四舍五入,行业叫RTN(Round To Nearest)。对大模型来说,RTN压到INT8基本能扛,很多7B模型变成INT8后几乎无感;但压到INT4就开始崩,因为单纯四舍五入的误差被矩阵乘法逐层放大,某些层会集体跑偏。
GPTQ是目前服务端最主流的4bit方案,思路是拿少量校准数据跑一遍模型,找到每层权重变化对输出误差的影响,再逐个权重做最优补偿。它把量化后的输出误差写成目标函数,用Hessian矩阵(二阶梯度信息)近似误差曲面,然后贪心地调整剩余权重的舍入方向,让一部分权重的舍入误差去抵消另一部分误差。
我当时看GPTQ论文也觉得绕,后来总结成一句话:它不让每个权重单独硬舍入,而是站在整层输出的高度做全局补偿,谁对最终结果影响大,就优先保谁。实测下来,GPTQ 4bit在7B以上模型的困惑度损失往往只有零点几,这也是AutoGPTQ、vLLM这些框架把GPTQ作为默认选项的原因。不过要注意,GPTQ是权重量化,推理时大多数实现还是要把权重反量化回高精度计算,它省的是显存和带宽,省不了算力。
3.2 AWQ和SmoothQuant:除了权重,激活也在闹情绪
权重分布再对称,激活值也在不断捣乱。激活张量里存在大量离群值,某些维度数值特别大,一旦把激活也量成INT8,一个离群值就会把scale拉大,剩下普通数值全部丢精度。
AWQ的核心观察是:大模型里大约1%的权重通道比其余通道重要得多。它不靠梯度,而是通过激活值统计识别出这些显著通道,然后对权重做通道级缩放,让量化在次要位置上多损失一点,在关键位置上少损失一点。AWQ不需要重训,校准开销比GPTQ还小,精度却非常扎实,这是它在vLLM、TensorRT-LLM里站稳脚跟的原因。
SmoothQuant走的是另一条路。它观察到激活离群值往往集中在固定通道,于是将激活除以一个平滑系数、权重乘以同系数,数学上完全等价,但激活分布被压均匀了,权重也能一起用INT8张量核跑。这是服务端全整数量化推理的关键路径,TensorRT-LLM里的W8A8方案基本绕不开这个思路。
一句话总结:只做权重量化时,GPTQ和AWQ已经够好;想榨干硬件算力,就要让激活和权重一起量化,这时SmoothQuant这类方案才是核心。
3.3 QAT:把量化误差塞回训练流程里
PTQ是模型训练完后用少量校准数据完成量化,成本低,是大模型量化的首选;QAT则是训练阶段就把量化误差加进去,让模型学会“带伤作战”。
QAT的实现叫fake quantization:前向传播时,把模拟量化和反量化后的结果喂给网络;反向传播时,通过直通估计(STE)让梯度近似穿过量化算子。模型在训练过程中会自动调整权重分布,去适应16个或256个粗糙刻度。因为量化误差是在训练中内生出来的,模型有机会针对性地“长出”抗压能力,所以QAT的效果通常比PTQ好,尤其在小模型和超低比特场景下,差距明显。
它的代价也明确:需要训练数据、训练算力和完整训练流程。对市面上的开放大模型来说,绝大多数部署场景不值得为4bit重跑一遍QAT,训练几十亿参数的成本早就超过买一块更大显存显卡了。所以QAT更多出现在对精度极度敏感、或模型本身需要长期维护的生产项目中。
3.4 GGUF的K量化:本地玩家绕不开的选择
聊大模型量化,绕不开llama.cpp和GGUF格式,GGUF里的Q4_K_M、Q5_K_S就是量化实践中出现频率最高的文件名。
K-quants和GPTQ思路完全不同。它按超块组织权重,一个super-block装256个权重,然后根据权重重要性在块内混合使用不同比特位宽,一部分权重用2bit、一部分用6bit、平均等效4bit。这种混合方式把有限的bit预算分配给了更关键的位置,压缩率和质量兼顾。Q4_K_M、Q5_K_S里的S、M、L分别对应更小、中等、更大的体积与精度取向。
本地玩家喜欢GGUF,首先是格式封装完整,分词器、模型配置、metadata全打包进单文件,下载即用,Ollama、LM Studio这些工具都直接认。其次是可以CPU和GPU之间灵活offload,显存不够就把一部分层丢回内存跑。最后是K量化的校准成本极低,文件体积直接决定显存占用,部署体验极其友好。
我自己的习惯是:本地能上Q4_K_M先上Q4_K_M,质量敏感任务升Q5_K_M或Q6_K,极少情况回到FP16。这套组合基本覆盖了我从7B到70B的日常使用。
3.5 别忘了微调场景:NF4与QLoRA的关系
量化不只在推理侧发光,微调侧同样重要。QLoRA就是用4bit量化加载基础模型权重,然后插入LoRA低秩适配器进行微调的技术,让一张24GB显卡微调70B模型成为可能。
QLoRA用的格式叫NF4,全称4bit NormalFloat。它基于信息论设计,针对权重近似正态分布的特性做了最优映射,普通INT4是均匀分布刻度,NF4则把更密集的刻度放在权重出现概率高的区域,所以同等bit下精度更好。训练时基础模型权重冻结在NF4,梯度通过反量化后的权重反向传播,真正更新的是LoRA那几十万参数。
这也是量化的一道隐藏分水岭:如果你只想部署,选GPTQ、AWQ、GGUF这类成熟方案;如果你要微调,得认准NF4/QLoRA这条技术路线,两者不能混着用。
4. 硬件视角下的量化真相:算力、带宽与指令集
4.1 为什么说推理瓶颈是“内存墙”而不是“计算墙”
很多文章介绍量化时喜欢列FLOPS,却不讲带宽。其实自回归生成场景里,每个token都要把所有权重读一遍才能算下一个token,这个读取成本远大于纯粹的计算成本。
算一笔账。RTX 4090的FP16算力约82TFLOPS,显存带宽约1TB/s,跑7B Q4模型时,每生成一个token要读取4GB权重,耗时约4毫秒。在这4毫秒里,实际需要的浮点运算量远没有喂饱和GPU算力,真正的天花板是带宽而不是FLOPS。这叫“内存墙”:权重大小和读取速度决定生成速度,算力再高也填不满。
这带来一个有趣现象:量化模型在GPU上有时加速不明显,因为显存够用、权重又小,计算反而顶上来了;同样的量化模型在CPU上,速度提升立竿见影,因为CPU和内存之间的带宽更稀缺。量化省下的每一个字节,最终都流向了系统最痛的瓶颈。
4.2 不同位宽在不同硬件上的真实支持情况
量化要和硬件绑定看,不是bit越少越好。
INT8是几乎所有平台都支持的一档,NVIDIA的张量核、Intel的AMX、各种NPU里都能跑整数矩阵乘。服务端全整数量化推理,INT8是最成熟的选择。
FP8是数据中心的新宠,H100这一代起有原生FP8张量核,它不是整数而是低比特浮点,分为e4m3和e5m2两种格式。e4m3动态范围约正负448,精度接近FP16的六七成,适合大多数模型权重和激活;e5m2动态范围更大,但精度低,适合梯度这类跨度大的数值。如果你的显卡比较老,FP8权重也未必被硬件加速,很多时候只是省了显存,计算仍会被打回FP16完成。
INT4是目前讨论最热烈、但硬件原生支持最弱的一档。GPU上很少有针对4bit的高性能矩阵乘内核。大家常说的“4bit模型跑得快”,本质是因为权重文件小了、带宽需求低了、能塞进显存了,而不是硬件把4bit算得飞快。CPU端,llama.cpp通过精巧的算子和打包方式确实能受益,但原理依然是围绕带宽做文章。
4.3 显存不够时,CPU offload怎么搭配
实际操作里,24GB显存跑70B的Q4模型其实也悬,权重就有40GB左右。这时要么多卡,要么做CPU offload。
用llama.cpp举例,你可以通过-ngl参数把一部分层放到GPU,剩下的放内存。GPU层数多,速度快但显存压力大;全部丢CPU,速度可能只剩每秒一两token。我自己的做法是:先保证模型能加载,再把耗时占比高的前若干层尽量放GPU,其余层随它去。自回归生成中每层都会被访问,任何一层落在CPU都会拖慢整体,所以这本质是速度与容量的取舍。
如果是服务端多用户部署,就别考虑CPU offload了,vLLM、TensorRT-LLM这类框架在KV Cache管理、连续批处理和调度上专业得多。这时应当选择AWQ或GPTQ 4bit模型,再配合张量并行,才能真正把服务吞吐做起来。
5. 量化效果的评估与避坑
5.1 用perplexity做体检:损失多少算合格
判断量化有没有把模型“搞笨”,最直接的方法是用困惑度(perplexity)在固定测试集上对比。llama.cpp自带的perplexity工具就能做,transformers社区也有现成脚本,跑法不复杂:
./build/bin/llama-perplexity \ -m ./models/qwen-7b-q4_k_m.gguf \ -f ./data/wikitext-2-raw/wiki.test.raw \ -ngl 999同一个模型,FP16版和量化版在同一个测试集上的perplexity差值,比任何“我觉得好像变笨了”都靠谱。按我的经验,7B级别模型从FP16换到Q4_K_M,perplexity上升通常在0.1到0.3之间;要是涨了0.5以上,先别急着骂量化,检查一下校准集和实际任务是否同分布。
还有一点,别只看全局指标。代码类模型用新闻语料做校准,金融对话模型用通用文本做量化,测试集上可能很漂亮,一上真实业务就露馅。校准数据集的任务相关性,远比数量重要。
5.2 越小越脆:为什么小模型怕量化,大模型反而抗造
有个反直觉的规律:模型越大,量化后的相对损失越小;模型越小,量化越容易翻车。原因是大型模型的参数冗余度高,分布更稳定,同一层里大量连接可以分担误差;小模型本来就没有多少余量,每个参数都承担着较重职责,一压缩能力就塌方。
社区里“2B模型用INT4基本没法看,70B模型用INT4照样聪明”的现象就是这么来的。不是量化算法偏心,是冗余度差着数量级。所以小模型建议保守一点,上Q6_K甚至直接用FP16;大模型反而可以放心压到4bit。这条经验对选型的影响非常大。
5.3 KV Cache量化与RoPE位置编码的兼容问题
聊到长上下文,躲不开KV Cache量化。KV Cache是推理中持续增长的显存大户,几千token时还好,几十万token长上下文下,KV Cache体积会反超权重。所以不少推理框架提供了KV量化选项,比如llama.cpp的cache_type_k/q8_0。
但KV Cache和权重不一样,里面存的是Key和Value。Key在RoPE旋转位置编码之后参与注意力计算,对数值精度极其敏感。RoPE会把高维向量旋转成不同相位,很小的量化误差经过旋转就可能被放大成错位。实践上要压KV位宽,要么选用带补偿的方案,要么先用q8_0这类接近无损的档位。别为了省显存直接压KV到4bit,长上下文的输出质量会肉眼可见地崩。
5.4 实战中容易忽略的几个坑:分组粒度、文件单位、框架偏好
量化配置里有个参数叫group size,例如group size 128表示每128个权重共用一组scale和zero_point。分组越小,scale越贴合局部数值分布,精度越好,但计算开销越大。很多人只看bit数不看group size,同样标称Int4,group size 32和group size 256的模型,实际效果差得不止一点半点。
还要小心单位陷阱。模型参数量说的GB通常指二进制GiB(约1.07GB),而量化文件标注的GB往往是十进制。下载前看一眼文件的实际字节数,差0.5GB可能就决定你的显存还够不够塞。这个细节我吃过亏,下载了号称“刚好能装下”的模型,加载时又差一口气。
最后是框架偏好。服务端vLLM对GGUF的支持并不完善,优先选GPTQ或AWQ格式配合PagedAttention;本地单机用户就老老实实用GGUF,Ollama和LM Studio都会帮你处理好细节。跨框架迁移之前,先确认目标框架的算子表,否则模型是能加载,速度却可能慢到没法用。
6. 选型:不同场景下到底选哪种量化
6.1 一张表捋清主流格式
| 格式/方案 | 7B权重体积 | 近似精度 | 推荐硬件 | 主要场景 |
|---|---|---|---|---|
| FP16/BF16 | 约14GB | 基准 | 显存充足 | 微调、精度敏感 |
| Q8_0(GGUF) | 约7.7GB | 几乎无损 | CPU/GPU通用 | 本地高质量 |
| Q4_K_M(GGUF) | 约4.1GB | 很小损失 | CPU/GPU、可offload | 本地均衡首选 |
| Q2_K(GGUF) | 约3GB | 明显损失 | 显存极紧 | 应急跑大模型 |
| GPTQ Int4 | 约4GB | 很小损失 | NVIDIA GPU | vLLM/服务端 |
| AWQ Int4 | 约4GB | 很小损失 | NVIDIA GPU | 服务端质量敏感场景 |
| FP8(W8A8) | 约7GB | 极小损失 | H100等新卡 | 数据中心高吞吐 |
这个表只适合当起点。实际选型永远要追着推理框架的算子支持走,追着硬件指令集走,追着校准数据的任务分布走。
6.2 不同部署形态的推荐组合
个人本地(笔记本或台式机,GPU 8到24GB):首选GGUF Q4_K_M,综合速度和体积最均衡。显存紧张降到Q4_K_S;质量敏感升Q5_K_M;跑3B以下的小模型就别折腾量化,FP16最省心。
服务端GPU(A100、H100这类数据中心卡):老架构选AWQ或GPTQ Int4,新架构能上FP8就FP8。在线服务并发量大时,FP8的硬件张量核支持能在延迟和吞吐上同时占优。现在不少团队在H100上直接用FP8跑7B到13B模型,这也是数据中心里比4bit更值得关注的方案。
边缘设备(RK3588、Jetson等):这里不是大模型的主场,但量化原理相通。NPU算子支持天差地别,比如RK3588上用RKNN-Toolkit做YOLOv5 Int8量化时,很多层需要混合精度或逐层优化,不能直接搬GPU的量化结果。核心策略永远是先让工具链默认量化跑通,再根据精度报告逐层调。
6.3 最后聊聊我这几年的切身经验
量化技术这几年演进非常快,今天好用的方案,过一年可能就让位给新思路。但底层的scale、zero_point、信息压缩、带宽瓶颈这套原理,并没有变过。我自己踩坑踩到最多的,就是不看硬件就选位宽、不看框架就下格式、不看任务就挑校准集。这三件事可以说是量化实践里最大的翻车点。
所以我的建议一直是:预算允许就不极端追低bit,卡确实不够再量化;量化之后永远用指标说话,别靠“感觉”判断模型变没变笨;先求工程上能落地,再谈精度优化。如果你能做到这三点,大模型量化对你来说就不再是玄学,而是一笔清清楚楚的性价比账。