模型量化怎么做的原理是什么

文章导读
模型量化的原理,是把权重(部分方案还包括激活)的浮点张量,按一组 scale(必要时再加 zero_point)映射到 8 位或 4 位整数;推理时要么先反量化回浮点,要么直接用整数核参与计算。所以「模型量化怎么做的原理和方法」在整条链路里对应的就是一步:确定量化粒度(per-tensor、per-channel、group-wise)、用对称还是非对称、校准数据(或 importance mat
📋 目录
  1. 壹 模型量化怎么做的原理和方法
  2. 贰 模型量化之前先确认这三件事
  3. 叁 模型量化:最短可用路径
  4. 肆 模型量化:一个真实场景的完整做法
  5. 伍 模型量化:跑不通先查这三处
A A

模型量化的原理,是把权重(部分方案还包括激活)的浮点张量,按一组 scale(必要时再加 zero_point)映射到 8 位或 4 位整数;推理时要么先反量化回浮点,要么直接用整数核参与计算。所以「模型量化怎么做的原理和方法」在整条链路里对应的就是一步:确定量化粒度(per-tensor、per-channel、group-wise)、用对称还是非对称、校准数据(或 importance matrix)从哪来,并把这套参数固化进模型文件。判断是否量化成功,不看脚本有没有跑完,而看模型文件里是否真的留下了量化类型与 scale,以及同一 prompt 下输出是否还通顺。

如果只是想把一个已训练好的模型压小后在本机或单卡上跑,优先做训练后量化(PTQ),GPTQ、AWQ、GGUF 的 K-quants、bitsandbytes 的 NF4 都属于这一类,不需要重训。判断顺序是:先确认模型目录里的 dtype 和 architectures,再看输出文件里有没有 scale 与量化类型字段,最后用同一 prompt 对比原模型和量化模型的输出。边界在于,4 位量化通常会带来可感知的质量变化,校准集或 importance matrix 与业务语料越接近,损失越可控;这一步无法靠调参完全消除,只能通过对比验收。

模型量化怎么做的原理和方法

按你说的这个原理,一次量化实际只做三件事,顺序不能乱:求范围、定映射、存参数。

  • 求范围:从权重里取 amax(一般按 per-channel 或按 group 取,例如每 128 个元素一组),得到 scale = amax / qmax;对称量化 qmax 取 127(INT8)或 7(INT4)。
  • 定映射:对称量化是 q = round(x / scale);非对称量化多一个 zero_point,q = round(x / scale) + zp,反量化时 x̂ = (q - zp) * scale。激活值分布偏斜时通常用非对称,权重大多用对称。
  • 存参数:把 scale、zero_point 和量化后的整数张量一起写进模型文件,并打上量化类型标记(GGUF 里是张量类型,HF 里是 config.json 的 quantization_config)。

确认手上的模型是不是已经量化过,直接看文件,不要凭文件名猜:

# HF 目录:看有没有量化配置和原始精度
python -c "import json;c=json.load(open('config.json'));print(c.get('quantization_config'), c.get('torch_dtype'))"

# GGUF 文件:看各张量实际存的类型(在 llama.cpp 目录下执行,字段名以本地 gguf 包版本为准)
python -c "import gguf,collections;r=gguf.GGUFReader('your-model-Q4_K_M.gguf');print(collections.Counter(t.tensor_type.name for t in r.tensors))"

如果输出里量化配置为空、张量类型全是 F16 或 F32,那这个文件只是转换过格式,并没有量化。

模型量化之前先确认这三件事

  1. 版本匹配。转换脚本、量化工具、推理框架三者版本要配套。用 python -c "import torch,transformers;print(torch.__version__, torch.version.cuda, transformers.__version__)" 看 Python 侧版本,./llama-quantize --help 看二进制支持的量化类型,nvidia-smi 右上角的 CUDA Version 是驱动支持上限,不是实际运行版本。
  2. 写权限与磁盘空间。量化的中间文件通常和原模型差不多大。执行 df -h ~/models 看剩余空间,ls -ld ~/models ~/.cache/huggingface 看目录归属;容器里还要确认挂载点是可写的,否则会在最后写文件时才失败。
  3. 入口位置。确认三样东西的绝对路径:原始模型目录(要含 config.json 和 tokenizer 文件)、转换脚本(llama.cpp 仓库根目录下的 convert_hf_to_gguf.py)、输出目录。模型面板或 config.json 里的 architectures 字段决定转换脚本走哪个分支,字段名对不上就是版本不支持该模型。

模型量化:最短可用路径

以 GGUF 路线为例,四步能跑通;换到 GPTQ 或 bitsandbytes,只是把第 2 步换成对应脚本,原理不变。

  1. 转成 F16 GGUF:python convert_hf_to_gguf.py ~/models/your-model --outfile ~/models/your-model-f16.gguf --outtype f16。做完应看到输出目录出现一个约为原模型一半大小的 .gguf 文件。
  2. 执行量化:./llama-quantize ~/models/your-model-f16.gguf ~/models/your-model-Q4_K_M.gguf Q4_K_M 8(最后的 8 是线程数)。做完应看到逐层处理的日志和写盘完成提示,文件体积明显小于 F16。
  3. 冒烟:./llama-cli -m ~/models/your-model-Q4_K_M.gguf -p "用一句话解释什么是量化" -n 64 --temp 0。应看到连贯中文输出,而不是重复单字或乱码。
  4. 对照:把 -m 换回 F16 文件,同一个 prompt、同样 -n 和 --temp 再跑一次,比较语义是否明显退化。

注意新版 llama.cpp 的可执行文件名是 llama-quantize、llama-cli,旧版本里叫 quantize、main,报 command not found 时先看 ls build/bin。

模型量化:一个真实场景的完整做法

场景:把一个约 7B 参数的中文对话模型(HF 格式,FP16 权重约 14GB,存放在 ~/models/your-model/)做成 4 位权重,用于本地单卡推理;手里有约 200 条业务问答日志,每条约 500 token,作为校准语料。

模型量化怎么做的原理是什么

做法是先算 importance matrix,再带 imatrix 做量化,让分组 scale 更贴近实际输入分布:

# 1) 生成 importance matrix,语料放在 ~/data/calib.txt(纯文本,一行一条)
./llama-imatrix -m ~/models/your-model-f16.gguf -f ~/data/calib.txt -o ~/data/your-model.imatrix --chunks 200 -c 512

# 2) 带 imatrix 量化
./llama-quantize --imatrix ~/data/your-model.imatrix ~/models/your-model-f16.gguf ~/models/your-model-Q4_K_M.gguf Q4_K_M 8

配置取值与所在位置:量化类型选 Q4_K_M,写在命令行参数里;imatrix 文件路径在 ~/data/your-model.imatrix;线程数按物理核数给,例如 8;输出路径固定在 ~/models/。若在 Python 侧用 bitsandbytes 加载,等价配置写在加载代码里而不是命令行:load_in_4bit=True、bnb_4bit_quant_type="nf4"、bnb_4bit_compute_dtype=torch.bfloat16,这几项写进 BitsAndBytesConfig,模型精度取自 config.json 的 torch_dtype。

验收时把业务问题里最容易答错的两三条抽出来,分别用 F16 和 Q4_K_M 各跑一遍,重点看数字、专有名词和格式是否走样,而不是只看回答流不流畅。

模型量化:跑不通先查这三处

卡点一:转换脚本报不认识的模型。典型报错是 KeyError: 'model.layers.0.self_attn.q_proj.weight',或 ValueError: Model type xxx not supported。这说明 convert_hf_to_gguf.py 的版本落后于模型架构,去 config.json 核对 architectures 与 model_type 的实际取值,再决定是升级脚本还是换量化后端。

卡点二:显存或内存不够。报错原文多为 CUDA out of memory. Tried to allocate ...,或加载时 llama_model_load: failed to allocate ...。先用 nvidia-smi 看是否已有别的进程占卡,再考虑降低并行层数(-ngl)、减小上下文(-c)、或改到 CPU 上先做转换。

卡点三:量化后输出异常。模型能加载但回答重复、串行或不停输出符号,通常是 chat template 没用对,而不是量化本身坏了。去 tokenizer_config.json 看 chat_template 字段是否存在、llama-cli 是否加了对应的对话模式参数;确认模板无误后,再回看张量类型里是否混了不同量化格式。