量化


文档摘要

量化 本节摘要:70B 模型 FP16 要 140GB,两张 A100 光装权重。量化到 FP8:一张 80GB 卡;INT4:一台 MacBook。本节带你吃透把高精度数换成低精度数的全部技术:数值格式(FP32/BF16/FP16/FP8/INT8/INT4 每一位的作用)、对称与非对称量化、逐张量 vs 逐通道、敏感度层级(权重最稳、注意力 logits 最敏感)、PTQ vs QAT,以及 GPTQ(海森矩阵引导)、AWQ(激活感知)、GGUF(llama.cpp 格式)三大方法。你会量化同一矩阵在 216 位上扫一遍,看清质量悬崖在哪,并理解为什么 FP8 几乎免费、INT4 损 12 分却翻倍吞吐。

量化

本节摘要:70B 模型 FP16 要 140GB,两张 A100 光装权重。量化到 FP8:一张 80GB 卡;INT4:一台 MacBook。本节带你吃透把高精度数换成低精度数的全部技术:数值格式(FP32/BF16/FP16/FP8/INT8/INT4 每一位的作用)、对称与非对称量化、逐张量 vs 逐通道、敏感度层级(权重最稳、注意力 logits 最敏感)、PTQ vs QAT,以及 GPTQ(海森矩阵引导)、AWQ(激活感知)、GGUF(llama.cpp 格式)三大方法。你会量化同一矩阵在 216 位上扫一遍,看清质量悬崖在哪,并理解为什么 FP8 几乎免费、INT4 损 12 分却翻倍吞吐。

学习目标

阅读完本节,你应当能够:

  1. 实现对称与非对称量化,从 FP16 到 INT8 和 INT4,含逐张量与逐通道缩放。
  2. 计算量化的省显存效果,确定某精度是否装得下给定 GPU 的显存。
  3. 解释训练后量化(PTQ)与量化感知训练(QAT)的区别。
  4. 应用 GPTQ 或 AWQ 量化真实模型,在基准上度量精度-显存权衡。

一、问题与直觉

Llama 3 70B 有 700 亿参数,每个 16 位浮点,即 1400 亿字节、140GB。单张 A100 80GB 连权重都装不下,更别说推理。需要两张 A100(各 2 美元/时)才服务一个模型。

但每参数 16 位很浪费。神经网络里多数权重聚在零附近,FP16 的完整动态范围(从 0.000000059 到 65504)几乎全没用上。测 Llama 3 70B 的实际权重分布,95% 落在 -0.1 到 +0.1——你烧 16 位表示本可 4 位装下的值。

量化用低精度数替换高精度数:FP16→FP8 省一半,FP16→INT4 省到四分之一,140GB 变 35GB,装得下单张消费级 GPU;推到 2 位(激进、有损,但某些任务可用)同一模型在 16GB 笔记本上跑。代价是精度——每去掉一位就毁信息,问题是你损失多少、损失在哪。量化良好的 INT4 模型在多数基准上保留原版 95~99% 质量;朴素 INT4 量化能彻底毁掉模型。差别在技术。

数值格式:每一位做什么

每个浮点数三部分:符号、指数(决定范围)、尾数(决定精度)。

FP32: [1 符号][8 指数][23 尾数] = 32 位 FP16: [1 符号][5 指数][10 尾数] = 16 位 BF16: [1 符号][8 指数][7 尾数] = 16 位 FP8: [1 符号][4 指数][3 尾数] = 8 位 (E4M3) INT8: [1 符号][7 值] = 8 位 (均匀步长) INT4: [1 符号][3 值] = 4 位 (共 16 级)

FP32 是全精度,23 位尾数给约 7 位十进制精度,训练曾只在 FP32 里做。FP16 减半,10 位尾数给约 3.3 位十进制,指数缩到 5 位大幅减小范围(最大约 65504)——权重(聚零)还行,但激活与梯度训练时会尖峰,需损失缩放防下溢。BF16(Brain Float 16)保留 FP32 的 8 位指数但尾数缩到 7 位,范围同 FP32 但精度低于 FP16,Google 专为深度学习设计:对神经网络范围比精度重要,10^-20 的梯度在 FP16 下溢为零在 BF16 里存活。现代训练基本用 BF16。FP8 两变体:E4M3(推理、精度多)、E5M2(训练梯度、范围多),H100 原生支持,推理比 FP16 快 30~50%、质量损失可忽略。INT8 是整数,无指数无尾数,-128 到 127 共 256 个均匀值,需缩放因子把浮点权重映射进来;整数算术比浮点快且省电,A100 上 INT8 矩阵乘 624 TOPS vs FP16 的 312 TFLOPS。INT4 只 16 个可能值,缩放因子干重活,质量全取决于怎么选缩放、量化哪些权重——先进方法(GPTQ、AWQ)保留 95%+ 质量。

量化怎么工作

核心操作简单:取浮点张量、找缩放因子、乘、四舍五入到最近整数、存整数加缩放因子。

量化:scale = max(abs(tensor)) / max_int; quantized = round(tensor / scale) 反量化:reconstructed = quantized * scale

误差是舍入误差,每值最多偏 scale/2逐张量 vs 逐通道:逐张量用一个缩放因子给整个权重矩阵,简单但有损(若一列大值一列小值,小值失大半精度);逐通道每输出通道(每行或列)一个缩放,开销大(存 N 个而非 1 个)但质量好得多,生产方法都用逐通道或更细。非对称量化加零点偏移 quantized = round(tensor/scale) + zero_point,处理不以零为中心的分布(如 ReLU 激活恒非负,对称量化浪费一半整数范围在从不出现的负值上)。

敏感度层级

模型里并非一切对量化容忍度相同,有明显层级。权重(最稳):训练时慢变、近高斯分布聚零,量化好,逐通道 INT8 几乎无损,INT4 需更精方法但可行。激活(中等敏感):比权重动态范围宽、含离群值,单个注意力头可能产出比均值大 100 倍的激活值,这些离群值对质量关键,朴素量化毁信息——解法是保持离群通道高精度(LLM.int8())、逐 token 或逐通道激活缩放。KV 缓存(高敏感):长上下文时 KV 缓存主导内存(70B、32K 上下文 FP16 约 40GB),量化到 FP8/INT8 省巨量内存,但误差跨所有未来注意力计算累积,质量影响随序列长缩放。注意力 logits(最敏感):softmax 对输入小变化高度敏感,0.01 的预 softmax logit 量化误差能显著移注意力分布,多数方案即使其他都量化也保持注意力计算高精度(FP16/BF16)。

PTQ vs QAT

训练后量化(PTQ) 量化已训模型,不重训,拿 FP16 权重算缩放、舍入、部署——快(分钟到小时)、便宜,INT8/FP8 效果好,INT4 朴素 PTQ 常失败(舍入误差累积),先进 PTQ(GPTQ、AWQ)用校准数据最小化量化误差。量化感知训练(QAT) 在训练前向插入假量化操作,模型学着把权重放在舍入误差小的位置,梯度用直通估计器(STE,假装舍入的梯度为 1)流过假量化——QAT 产出比 PTQ 更好的 INT4/INT2 模型但需完整训练。Google 用 QAT 给 Gemini 高效服务,Meta 用 QAT 给某些 Llama 部署目标。

方面 PTQ QAT
成本 分钟到小时 完整训练
INT8 质量 极好(<0.1% 损失) 极好
INT4 质量 GPTQ/AWQ 好(1~3% 损失) 更好(<1%)
校准数据 128~1024 样本 完整训练集

GPTQ、AWQ、GGUF

GPTQ(GPT 量化) 是一次性 PTQ 方法,逐层量化权重,用小校准集(典型 128 样本)算海森矩阵(二阶信息,输出对每个权重多敏感),海森说重要的权重量化得更仔细。GPTQ 是首个让 LLM INT4 量化实用的方法,HuggingFace 上 TheBloke 发布了数百模型的 GPTQ 版普及了它。AWQ(激活感知权重量化) 观察到一小部分权重(约 1%)因乘大激活值而格外重要,AWQ 用校准数据识别这些显著权重,量化前先放大它们(再缩小对应激活),让重要权重落在 INT4 量化精确的范围。AWQ 通常匹配或略胜 GPTQ 质量,而应用快 1.5~2 倍。GGUF(GPT 生成统一格式) 是 llama.cpp 生态的文件格式,支持混合精度(不同层不同位宽,首尾层保持高精度,中间 INT4/INT3),自包含(权重、分词器、元数据一个文件),专为 CPU 推理与 Apple Silicon 优化。Q4_K_M 是最受欢迎的 GGUF 变体,平衡质量与大小。

质量度量

困惑度:最常用指标,越低越好,在留出集(WikiText-2 标准)上算原版与量化的困惑度,差值告诉你量化毁了多少信息——经验:<0.5 优秀,0.51.0 好,1.02.0 多数任务可接受,>2.0 出问题了。任务基准:跑量化模型在 MMLU、HumanEval、GSM8K 或你的自定义评估,与原版对比,量化对不同能力影响不均(数学与代码比通用知识更敏感精度损失)。输出对比:同提示从两模型生成响应比较,LLM-as-judge 算胜率。延迟与吞吐:量化是为更快更便宜,测每秒 token、首 token 时间、内存,量化模型若更慢则比无用还糟。

模型 格式 大小 困惑度 MMLU token/秒(A100)
Llama 3 70B FP16 140GB 3.12 79.5% 38
Llama 3 70B FP8 70GB 3.14 79.3% 55
Llama 3 70B GPTQ INT4 35GB 4.32 77.8% 72
Llama 3 70B AWQ INT4 35GB 4.18 78.1% 75

模式:FP8 几乎免费,INT4 损 1~2 个 MMLU 分但翻倍吞吐、省四分之一内存——对几乎每个部署都值。

二、从零实现

Step 1:对称量化(逐张量与逐通道)

def quantize_symmetric(tensor, num_bits=8): qmax = 2**(num_bits-1) - 1 scale = np.max(np.abs(tensor)) / qmax if np.max(np.abs(tensor))>0 else 1.0 return np.clip(np.round(tensor/scale), -qmax-1, qmax).astype(np.int32), float(scale) def quantize_per_channel(tensor, num_bits=8, axis=0): qmax = 2**(num_bits-1) - 1 abs_max = np.max(np.abs(tensor), axis=axis, keepdims=True) scales = np.where(abs_max==0, 1.0, abs_max) / qmax return np.clip(np.round(tensor/scales), -qmax-1, qmax).astype(np.int32), scales.squeeze()

Step 2:质量度量

def quantization_error(original, reconstructed): diff = original - reconstructed mse = float(np.mean(diff**2)) snr_db = 10*np.log10(np.mean(original**2)/max(mse,1e-20)) cosine = float(np.dot(original.flatten(), reconstructed.flatten()) / (np.linalg.norm(original)*np.linalg.norm(reconstructed))) return {"mse":mse, "snr_db":float(snr_db), "cosine_similarity":cosine}

Step 3:位宽扫描

同一张量在 2、3、4、8、16 位上量化,各测质量,看清质量悬崖在哪。

Step 4:敏感度实验

模拟量化 Transformer 不同部分,度量哪些组件最敏感——验证敏感度层级:权重 < 激活 < KV 缓存 < 注意力 logits。

Step 5:模拟 GPTQ

GPTQ 用海森矩阵决定如何分配舍入误差。这是简化版,抓核心:用校准数据度量权重重要性,然后更激进地量化不重要的。对每列:算缩放、量化、测误差,把误差补偿传播到后续列(海森加权的近似)。

Step 6:模拟 AWQ

AWQ 识别显著权重(乘大激活的)并量化前缩放保护它们。用校准数据找激活幅度最大的通道,缩放这些通道的权重再量化,反量化后再缩回——净效果是重要权重量化得更精确。

Step 7:显存计算器

def memory_calculator(params_b, bits_per_param): return params_b * 1e9 * (bits_per_param/8) / (1024**3) # 输出:7B FP16=14G, INT4=3.5G;70B FP16=140G, INT4=35G;405B FP16=810G, INT4=202G

三、框架对比

AutoGPTQ

from auto_gptq import AutoGPTQForCausalLM, BaseQuantizeConfig model = AutoGPTQForCausalLM.from_pretrained(model_id, BaseQuantizeConfig(bits=4, group_size=128)) model.quantize(calibration); model.save_quantized("llama-8b-gptq-int4")

AutoAWQ

from awq import AutoAWQForCausalLM model = AutoAWQForCausalLM.from_pretrained(model_id) model.quantize(tokenizer, quant_config={"w_bit":4, "q_group_size":128})

转 GGUF(llama.cpp)

python convert_hf_to_gguf.py meta-llama/Llama-3.1-8B --outtype q4_k_m --outfile llama.gguf llama-server -m llama.gguf -c 4096 -ngl 99

vLLM 服务量化模型

vLLM 原生支持 AWQ 和 GPTQ,处理矩阵乘中的反量化,用分页注意力管 KV 缓存。H100 上 FP8 加 --dtype float8_e4m3fn

四、可复用产物

本节产出 outputs/skill-quantization.md——一个选对量化策略的决策框架。给定模型大小、目标硬件、质量要求,它告诉你用哪种格式、方法、验证步骤,含显存预算、各组件精度建议、vLLM/llama.cpp/TensorRT-LLM 部署配方。

五、练习

  1. (Easy) 实现分组量化:每 128 权重一个缩放(而非每通道),这是 GPTQ/AWQ 实际用的。对比组大小 32、64、128、256,小组质量好但缩放开销大。

  2. (Medium) 构建混合精度量化器:首尾层 INT8、中间层 INT4,对比均匀 INT4/INT8 的端到端质量,测省的显存。

  3. (Medium) 实现直通估计器(STE)做量化感知训练:在两层回归网络前向插假量化/反量化,对比先训后 PTQ 到 INT4 vs 从头 QAT 的最终损失。

  4. (Hard) 构建 LLM.int8() 启发的离群感知量化器:检测激活幅度超均值 6 倍的通道,这些保持 FP16 其余 INT8,测离群阈值 3×、6×、10× 的端到端质量。

  5. (Hard) 构建量化质量仪表盘:给定权重矩阵显示分布直方图、量化误差分布、逐通道缩放、最差量化通道(重建误差最高)、100 随机输入上的余弦相似度,识别该保持高精度的通道。

本节要点回顾

  1. 量化省显存换精度:70B FP16=140GB(两 A100),INT4=35GB(单卡),2 位=16GB(笔记本)。
  2. 数值格式每一位有分工:符号定正负,指数定范围,尾数定精度;BF16 同 FP32 范围低精度,适合训练。
  3. 量化=找缩放+舍入+存整数加缩放:误差是舍入误差,每值最多偏 scale/2。
  4. 逐通道远胜逐张量:逐通道每输出通道一个缩放,大幅降误差,生产方法都用之。
  5. 非对称量化加零点:处理不以零为中心的分布(如 ReLU 激活恒正)。
  6. 敏感度层级:权重最稳(高斯聚零)、激活(含离群)、KV 缓存(误差累积)、注意力 logits(softmax 放大误差,保持 FP16)。
  7. PTQ 快便宜,QAT 精度好:PTQ 用校准数据,适合 INT8/FP8;QAT 训练时假量化,适合 INT4/INT2。
  8. GPTQ 用海森:二阶信息定权重重要性,逐层最小化输出误差,首个让 LLM INT4 实用的方法。
  9. AWQ 激活感知:识别乘大激活的 1% 显著权重,量化前缩放保护,通常略胜 GPTQ 且应用更快。
  10. GGUF 混合精度:llama.cpp 格式,首尾层高精度中间 INT4,CPU/Metal 优化,Q4_K_M 最流行。
  11. FP8 几乎免费:H100 上 30~50% 提速、<0.1% 质量损失,每个 H100 部署都该用。

下一节,推理优化:prefill 算力受限、decode 内存受限,每个优化都针对其一或两者——KV 缓存、连续批处理、PagedAttention、投机解码。


发布者: 作者: Rohit Gupta 转发
评论区 (0)
U