
1. 大模型量化技术到底在解决什么问题第一次接触大模型量化很多人脑子里冒出来的第一个疑问是模型跑得好好的为什么非要折腾它我刚开始做推理部署的时候也有这个困惑直到有一次要把一个70亿参数的模型塞进一张显存只有24GB的卡里才发现不量化根本跑不起来。这就是量化最朴素的出发点——让模型变小、变快、变得能在更便宜的硬件上跑起来。大模型量化说白了就是用更少的比特数来表示模型权重和激活值。训练的时候权重通常是FP32或者BF16每个参数占2到4个字节。一个70亿参数的模型光权重就要占掉14GB到28GB的显存再加上推理过程中的KV Cache和中间激活显存需求轻松突破30GB。量化做的事情就是把这些高精度数值映射到低比特的整数空间比如INT8、INT4甚至更低从而把显存占用压缩到原来的四分之一甚至八分之一。但量化不是简单的“砍精度”。它本质上是一个信息压缩问题如何在尽可能保留模型表达能力的前提下用更少的比特去近似原始权重分布。这里面涉及的核心矛盾是——压缩率越高精度损失越大压缩率越低部署成本越高。所以量化技术的全部努力都围绕着一个目标在给定比特预算下把精度损失压到最小。从应用场景来看量化主要服务于三类需求。第一类是本地部署比如你想在自己的笔记本或者单张消费级显卡上跑一个对话模型不量化基本不可能。第二类是高并发推理服务量化后单卡能承载的请求数翻倍直接降低单位推理成本。第三类是边缘设备部署手机、嵌入式设备上的算力极其有限只有量化后的模型才有可能落地。适合读这篇内容的人我大致分成三类一是刚入门大模型部署、被显存问题卡住的工程师二是想理解量化原理、但被各种论文术语绕晕的算法同学三是需要在成本和精度之间做取舍的技术决策者。不管你属于哪一类接下来的内容都会从原理到实操把量化这件事讲透。2. 量化技术的核心原理拆解2.1 从浮点到定点量化的数学本质量化的数学本质是一个仿射映射。假设原始权重是FP16的浮点数我们要把它映射到INT8的整数空间公式大概是这样的q round(w / scale zero_point)其中w是原始浮点权重scale是缩放因子zero_point是零点偏移q是量化后的整数。反量化的时候就是反过来w_hat (q - zero_point) * scale这个scale怎么定是量化技术的第一个分水岭。最简单的方式是取整个张量的最大值和最小值然后均匀划分。但这样做的问题是如果权重分布里有几个极端大的离群值整个量化区间就会被拉得很宽导致大部分正常权重的量化精度被浪费掉。我举个例子你就明白了。假设一组权重是[-0.1, -0.05, 0.02, 0.03, 0.04, 5.0]最大值是5.0最小值是-0.1。如果按均匀量化INT8的256个刻度要覆盖5.1的范围每个刻度大约0.02。那么0.02和0.03这两个值量化后可能变成同一个整数精度直接丢失。但如果把那个5.0单独处理剩下的值用更细的刻度去量化精度就能保住。这就是为什么后来的量化方法都在想办法处理离群值。GPTQ用的是逐列量化和误差补偿AWQ用的是激活感知的通道缩放本质上都是在解决“怎么让量化区间更贴合真实权重分布”这个问题。2.2 对称量化与非对称量化的选择逻辑对称量化就是让量化区间关于零点对称即zero_point 0scale max(abs(w)) / 127。非对称量化则允许零点偏移scale (max(w) - min(w)) / 255zero_point根据实际分布计算。这两种方式怎么选我的经验是看权重的分布形态。如果权重近似以零为中心对称分布比如大多数Transformer的权重矩阵对称量化就够了而且计算更简单推理时不需要额外的零点偏移运算。但如果权重分布明显偏斜比如ReLU之后的激活值全是非负的那非对称量化能更充分地利用量化区间。实际工程中权重通常用对称量化激活值用非对称量化这是一个比较常见的组合。原因在于权重的分布相对稳定训练完成后就固定了对称量化足够而激活值随输入变化分布动态范围大非对称量化更灵活。2.3 量化粒度per-tensor、per-channel与per-group量化粒度决定了scale和zero_point的共享范围。最粗的是per-tensor整个张量共用一个scale。细一点的是per-channel每个输出通道有自己的scale。更细的是per-group把通道再分组每组独立量化。粒度越细量化精度越高但元数据开销也越大。以INT4量化为例per-channel的话每个通道要存一个FP16的scale假设通道数是4096额外开销就是8KB。如果换成per-groupgroup size设为128那scale的数量就变成4096/12832个开销反而更小不对这里要算清楚per-channel是每个通道一个scale共4096个per-group是每128个通道一个scale共32个。所以per-group的元数据更少同时因为每组独立量化精度还更好。这就是为什么GPTQ默认用group size 128AWQ也支持group量化。在实际操作中group size的选择是一个需要权衡的参数。设得太小元数据开销增加推理时的解量化计算也更复杂设得太大精度又会下降。我实测下来128是一个比较甜的平衡点大多数场景下都能兼顾精度和效率。2.4 训练后量化与量化感知训练的分野训练后量化PTQ是在模型训练完成后直接对权重做量化不需要重新训练。量化感知训练QAT则是在训练过程中模拟量化误差让模型学会适应低精度表示。PTQ的优点是成本低、速度快拿过来就能用。缺点是精度损失相对较大尤其是量化到4比特以下的时候。QAT的优点是精度保持得好但需要额外的训练资源和时间而且训练数据也要准备好。对于大模型来说PTQ是主流选择。原因很简单大模型的训练成本太高了不是每个团队都有资源去做QAT。GPTQ、AWQ这些方法都属于PTQ范畴它们通过巧妙的算法设计在不需要重新训练的情况下把精度损失控制在了可接受的范围内。注意PTQ虽然方便但在极低比特比如2比特、3比特场景下精度损失可能会比较明显。如果你的应用对精度极其敏感要么提高比特数要么考虑QAT。3. 主流量化方法实操对比3.1 GPTQ逐层量化与误差补偿的经典实现GPTQ的核心思想是逐层量化并且在量化每一列权重的时候用剩下的未量化权重去补偿已经量化带来的误差。具体来说它把量化问题转化为一个最小化重构误差的优化问题argmin ||WX - W_hat X||^2其中W是原始权重W_hat是量化后的权重X是校准数据集的输入。GPTQ通过Hessian矩阵来指导量化顺序和误差补偿使得每一列的量化误差都能被后续列部分吸收。实操上用GPTQ量化一个模型大概是这样几步。首先安装依赖pip install auto-gptq transformers accelerate然后准备校准数据通常用模型训练数据的一个子集比如128到1024条样本。校准数据的质量对量化效果影响很大我一般会从训练集里随机采样确保覆盖不同的输入分布。from auto_gptq import AutoGPTQForCausalLM, BaseQuantizeConfig from transformers import AutoTokenizer model_name your-model-path quantize_config BaseQuantizeConfig( bits4, group_size128, desc_actFalse, ) tokenizer AutoTokenizer.from_pretrained(model_name) model AutoGPTQForCausalLM.from_pretrained(model_name, quantize_config) # 准备校准数据 calibration_data [...] # 你的校准样本列表 model.quantize(calibration_data) model.save_quantized(output-path)这里有几个参数值得注意。bits决定量化比特数4比特是最常用的。group_size控制量化粒度128是默认值。desc_act决定是否对激活值也做排序量化开启后精度更好但推理速度会慢一些。我踩过的一个坑是校准数据的选择。有一次我图省事直接用了几十条无关的文本做校准结果量化后的模型在特定任务上表现明显下降。后来换成和目标任务分布接近的数据精度就回来了。所以校准数据一定要有代表性这是GPTQ量化效果的关键。3.2 AWQ激活感知的权重缩放策略AWQ的出发点和GPTQ不同。它观察到权重的重要性不是均匀的只有一小部分权重对模型输出影响很大。如果能识别出这些重要权重并在量化时保护它们就能在相同比特数下获得更好的精度。AWQ的做法是在量化前对权重做通道缩放。具体来说它通过分析激活值的分布找出那些对应大激活值的权重通道然后对这些通道乘以一个缩放因子让它们在量化时占据更大的动态范围。缩放因子是通过网格搜索优化的目标是最小化量化后的输出误差。用AWQ量化模型的操作流程和GPTQ类似但工具链略有不同pip install autoawqfrom awq import AutoAWQForCausalLM from transformers import AutoTokenizer model_path your-model-path quant_path output-path quant_config {zero_point: True, q_group_size: 128, w_bit: 4, version: GEMM} model AutoAWQForCausalLM.from_pretrained(model_path) tokenizer AutoTokenizer.from_pretrained(model_path) model.quantize(tokenizer, quant_configquant_config) model.save_quantized(quant_path)AWQ的一个优势是推理速度通常比GPTQ快因为它的缩放操作可以融合到前一层里不增加额外的计算。实测下来在相同比特数下AWQ的精度和GPTQ互有胜负具体取决于模型和任务。我的建议是两个都试一下用你的实际评测集去选。3.3 各方法关键参数对照方法比特数量化粒度校准数据需求推理速度精度保持GPTQ2/3/4/8per-group需要中等好AWQ4per-group需要快好GGUF2-8多种不需要中等中等bitsandbytes4/8per-tensor不需要慢中等这个表格是我根据实际使用经验整理的不是绝对标准。比如GGUF其实也支持校准但它的设计初衷是方便CPU推理所以对校准数据的依赖没那么强。bitsandbytes的NF4量化在QLoRA微调里用得很多推理速度确实偏慢但胜在即插即用。3.4 量化方法选型的决策框架面对这么多量化方法怎么选我一般按这几个维度来决策。第一看硬件。如果是NVIDIA GPU推理GPTQ和AWQ都有CUDA内核优化速度都不错。如果是CPU或者Apple SiliconGGUF是更好的选择llama.cpp对它的支持最完善。第二看精度要求。如果任务对精度极其敏感比如代码生成或者数学推理建议用4比特AWQ或者GPTQ并且用你的评测集验证。如果只是日常对话4比特甚至3比特都能接受。第三看部署框架。vLLM对GPTQ和AWQ的支持都很好TensorRT-LLM对AWQ的支持更成熟llama.cpp则主推GGUF。选量化方法的时候要和你用的推理框架匹配不然可能白忙活。第四看时间成本。如果只是想快速跑起来看看效果bitsandbytes的load_in_4bit是最省事的一行代码就能加载。如果要追求极致性能那就花时间做GPTQ或AWQ量化并且调参优化。4. 量化实操全流程与避坑指南4.1 环境准备与依赖安装量化操作对环境有一定要求主要是CUDA版本和PyTorch版本的匹配。我一般建议用conda创建一个独立环境避免和系统里的其他包冲突。conda create -n quant python3.10 conda activate quant pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 pip install transformers accelerate datasets pip install auto-gptq # 或者 autoawqCUDA版本要根据你的显卡驱动来选。我遇到过好几次因为CUDA版本不匹配导致量化过程中报错的情况排查起来很费时间。建议先用nvidia-smi看一下驱动支持的CUDA版本然后装对应的PyTorch。另外量化过程中需要加载完整模型所以显存要足够。以70亿参数模型为例FP16加载需要大约14GB显存量化过程中还会有额外的中间变量建议预留20GB以上的显存。如果显存不够可以用device_mapauto让accelerate自动分配但速度会慢一些。4.2 校准数据集的构建与处理校准数据的质量和数量直接影响量化效果。数量上GPTQ论文建议128到1024条样本我一般用512条效果和1024条差别不大但速度快一倍。质量上校准数据应该和你的目标任务分布接近。构建校准数据集的步骤大概是从训练集或者领域数据里随机采样每条样本长度控制在模型最大上下文长度以内然后tokenize成模型需要的格式。如果是对话模型要注意保留对话模板。from datasets import load_dataset dataset load_dataset(your-dataset, splittrain) calibration_samples [] for i in range(512): text dataset[i][text] tokens tokenizer(text, return_tensorspt, truncationTrue, max_length2048) calibration_samples.append(tokens) # 保存校准数据方便复用 torch.save(calibration_samples, calibration_data.pt)提示校准数据一定要保存下来。同一个模型用同一份校准数据量化结果才可复现。我习惯把校准数据和量化配置一起存档后面排查问题的时候很有用。4.3 量化执行与显存监控量化执行过程中要盯着显存变化。以GPTQ为例量化一个70亿参数模型大概需要10到30分钟具体取决于校准数据量和硬件性能。过程中显存占用会先上升后下降如果看到显存持续增长不释放可能是校准数据太多或者batch size太大。# 监控显存 watch -n 1 nvidia-smi我一般会在量化脚本里加一个显存打印每处理完一层就输出当前显存占用这样能及时发现异常。import torch def print_gpu_memory(): if torch.cuda.is_available(): allocated torch.cuda.memory_allocated() / 1024**3 reserved torch.cuda.memory_reserved() / 1024**3 print(fAllocated: {allocated:.2f} GB, Reserved: {reserved:.2f} GB)量化完成后模型会保存成safetensors格式文件大小大约是原始模型的四分之一4比特量化。加载量化模型的时候要用对应的量化加载器不能直接用AutoModelForCausalLM.from_pretrained。4.4 量化后模型的精度验证方法量化完不是就完事了必须做精度验证。我通常从三个层面来评估。第一个层面是困惑度Perplexity。在WikiText或者你的领域数据集上算一下量化前后的困惑度差距在0.5以内算正常超过1就要警惕了。from transformers import AutoModelForCausalLM, AutoTokenizer import torch def calculate_perplexity(model, tokenizer, texts): model.eval() total_loss 0 total_tokens 0 with torch.no_grad(): for text in texts: inputs tokenizer(text, return_tensorspt).to(model.device) outputs model(**inputs, labelsinputs[input_ids]) total_loss outputs.loss.item() * inputs[input_ids].shape[1] total_tokens inputs[input_ids].shape[1] return torch.exp(torch.tensor(total_loss / total_tokens))第二个层面是任务指标。如果你的模型是做分类或者生成的跑一下你的评测集看准确率或者BLEU、ROUGE这些指标掉了多少。我一般要求掉点不超过2%超过的话就要考虑换量化方法或者调参。第三个层面是人工抽查。随机抽一些输入对比量化前后的输出看看有没有明显的退化比如重复、胡言乱语、逻辑断裂。这一步虽然主观但能发现一些指标看不出来的问题。4.5 常见量化问题速查表问题现象可能原因排查方法解决方案量化后模型输出乱码量化配置错误检查bits和group_size重新量化确认参数显存不足报错模型太大或batch太大nvidia-smi查看占用减小校准batch用device_map量化速度极慢校准数据太多检查数据量减少到512条以内精度下降明显校准数据不匹配对比困惑度换校准数据提高比特数加载量化模型报错加载器不匹配检查量化方法用对应的量化加载器推理速度没提升量化方法不适合硬件测推理延迟换AWQ或GGUF这个表是我在实际项目中踩坑总结的基本上覆盖了80%的常见问题。遇到问题的时候先对照排查能省不少时间。5. 量化技术的边界与进阶方向5.1 极低比特量化的可行性与限制4比特量化现在已经很成熟了但2比特、3比特量化仍然是个挑战。极低比特下权重的信息被压缩得太厉害精度损失很难避免。我试过用2比特GPTQ量化一个13亿参数的模型困惑度从12涨到了25基本没法用。不过也有一些研究在尝试突破这个限制。比如三元量化ternary quantization把权重限制在{-1, 0, 1}三个值上理论上压缩率极高。但实际效果嘛目前还停留在论文阶段真正能用的产品级方案很少。我的建议是除非你的场景对精度要求极低否则不要轻易尝试3比特以下的量化。4比特是目前性价比最高的选择8比特则适合对精度要求高的场景。5.2 量化与微调的协同QLoRA的思路QLoRA是一个很有意思的思路先把基座模型量化到4比特然后在量化模型上做LoRA微调。这样显存需求大幅降低微调一个70亿参数模型只需要一张24GB的卡就能跑。QLoRA的关键在于它把量化误差和微调过程解耦了。基座模型量化后冻结不动只训练LoRA适配器。因为LoRA的参数很少所以即使基座模型有量化误差微调也能在一定程度上补偿。from transformers import BitsAndBytesConfig from peft import LoraConfig, get_peft_model bnb_config BitsAndBytesConfig( load_in_4bitTrue, bnb_4bit_quant_typenf4, bnb_4bit_compute_dtypetorch.bfloat16, ) model AutoModelForCausalLM.from_pretrained( model_name, quantization_configbnb_config, device_mapauto, ) lora_config LoraConfig( r16, lora_alpha32, target_modules[q_proj, v_proj], lora_dropout0.05, ) model get_peft_model(model, lora_config)QLoRA的实操细节很多比如target_modules的选择、学习率的设置、batch size的调整每一个都会影响最终效果。我后面会单独写一篇QLoRA的实操总结这里就不展开了。5.3 量化推理框架的选型建议量化模型训好了最终要落到推理框架上。目前主流的推理框架对量化的支持情况大致是这样的。vLLM对GPTQ和AWQ的支持最好吞吐量高适合服务端部署。TensorRT-LLM对AWQ的支持很成熟性能极致但编译过程比较复杂。llama.cpp主推GGUFCPU推理首选Apple Silicon上表现也很好。Ollama底层用的就是llama.cpp适合快速本地部署。选框架的时候我一般先看社区活跃度和文档完善程度。vLLM和llama.cpp的文档都比较全遇到问题容易找到答案。TensorRT-LLM性能最好但踩坑成本也最高适合有专门推理优化团队的情况。注意不同推理框架对量化模型格式的要求不同。GPTQ模型在vLLM上能跑在llama.cpp上就不行。量化之前先确定推理框架再选量化方法能少走很多弯路。5.4 量化技术的未来演进方向量化技术还在快速演进。从趋势上看有几个方向值得关注。一是硬件感知量化。不同的硬件对量化运算的支持程度不同未来的量化方法可能会针对特定硬件做优化比如专门为某款芯片设计的量化方案。二是动态量化。目前的量化大多是静态的scale在量化后就固定了。动态量化会根据输入实时调整量化参数精度更好但计算开销更大。三是量化与稀疏化的结合。量化和剪枝都是模型压缩的手段两者结合有可能实现更高的压缩率。目前已经有研究在探索这个方向但离产品化还有距离。四是端到端的量化训练。把量化作为训练的一部分而不是训练后的附加步骤。这样模型从一开始就适应低精度表示精度损失最小。这些方向我都在持续关注有些已经在小规模实验中验证了可行性。等有成熟的结果再和大家分享。6. 个人实操经验与建议做了这么多量化项目有几个体会特别深。第一个体会是量化不是免费的午餐。4比特量化确实能把显存降到四分之一但精度损失是客观存在的。关键在于你的应用能不能接受这个损失。我一般会在项目初期就做量化精度的评估如果掉点太多要么提高比特数要么换更好的量化方法要么干脆不量化。第二个体会是校准数据比量化算法更重要。同样的GPTQ算法用不同的校准数据量化后的精度可能差很多。我现在的习惯是校准数据一定从目标任务的数据分布里采样而且会做去重和清洗确保质量。第三个体会是量化后的模型一定要做端到端测试。困惑度和任务指标只能反映一部分问题实际推理中的表现才是最终标准。我遇到过量化后困惑度正常但生成结果里出现大量重复的情况这种问题只有实际跑一遍才能发现。第四个体会是不要迷信排行榜。Open LLM Leaderboard上的量化模型评分只能作为参考因为评测集和你的实际任务可能差别很大。我一般会用自己的评测集做最终决策排行榜只是初筛。最后分享一个实用技巧如果你不确定选哪种量化方法可以先用bitsandbytes的4比特加载跑一下看看精度能不能接受。如果能接受再花时间做GPTQ或AWQ量化追求更好的推理性能。这样能快速验证可行性避免在量化上浪费太多时间。量化这个领域变化很快新的方法和工具层出不穷。保持学习多动手实验比看再多论文都管用。