【大模型】- 量化
[2026-08-19]
mod:2026-11-05
wc:1.7k
rt:7
量化
减少模型大小,加速推理
类型: 学习 | 语言: Python | 🏷 前置:《量化》(本系列第 10 篇)
学习目标
- 理解量化的原理
- 实现常见的量化方法
- 使用 GPTQ 和 AWQ 量化模型
- 评估量化后的模型质量
- 选择合适的量化策略
量化概述
量化是将模型权重从高精度(FP32/FP16)转换为低精度(INT8/INT4)的过程,以减少模型大小和加速推理。
量化的好处
- 减少内存:INT4 量化可将模型大小减少 4-8 倍
- 加速推理:低精度计算更快
- 降低能耗:更少的内存带宽和计算
量化类型
| 类型 |
精度 |
大小减少 |
质量影响 |
| FP16 |
16位 |
2x |
极小 |
| INT8 |
8位 |
4x |
小 |
| INT4 |
4位 |
8x |
中等 |
| INT3 |
3位 |
10x |
较大 |
| INT2 |
2位 |
16x |
显著 |
基本量化方法
线性量化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import torch import numpy as np
def linear_quantize(tensor, num_bits=8): """线性量化:将浮点数映射到整数范围""" qmin = 0 qmax = 2**num_bits - 1 min_val = tensor.min() max_val = tensor.max() scale = (max_val - min_val) / (qmax - qmin) zero_point = qmin - min_val / scale quantized = torch.round(tensor / scale + zero_point) quantized = torch.clamp(quantized, qmin, qmax).to(torch.int8) return quantized, scale, zero_point
def linear_dequantize(quantized, scale, zero_point): """线性反量化""" return (quantized.float() - zero_point) * scale
|
对称量化
1 2 3 4 5 6 7 8 9 10 11
| def symmetric_quantize(tensor, num_bits=8): """对称量化:零点为 0""" qmax = 2**(num_bits - 1) - 1 max_abs = tensor.abs().max() scale = max_abs / qmax quantized = torch.round(tensor / scale) quantized = torch.clamp(quantized, -qmax - 1, qmax).to(torch.int8) return quantized, scale
|
分块量化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| def block_quantize(tensor, block_size=128, num_bits=8): """分块量化:每块独立量化""" original_shape = tensor.shape tensor = tensor.view(-1, block_size) quantized_blocks = [] scales = [] for block in tensor: q, s = symmetric_quantize(block, num_bits) quantized_blocks.append(q) scales.append(s) return quantized_blocks, scales, original_shape
|
GPTQ 量化
GPTQ 是一种训练后量化方法,通过最小化量化误差来优化权重:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| from transformers import GPTQConfig, AutoModelForCausalLM
quantization_config = GPTQConfig( bits=4, dataset="c4", group_size=128, desc_act=True, damp_percent=0.01, )
model = AutoModelForCausalLM.from_pretrained( "model_name", quantization_config=quantization_config, device_map="auto" )
|
GPTQ 实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| class GPTQQuantizer: def __init__(self, model, num_bits=4, group_size=128): self.model = model self.num_bits = num_bits self.group_size = group_size def quantize(self, calibration_data): """使用校准数据进行 GPTQ 量化""" activations = self.collect_activations(calibration_data) for name, module in self.model.named_modules(): if isinstance(module, nn.Linear): self.quantize_layer(name, module, activations[name]) return self.model def quantize_layer(self, name, module, activations): """量化单个层""" weight = module.weight.data H = activations.T @ activations for col in range(weight.shape[1]): w = weight[:, col] q, scale = symmetric_quantize(w, self.num_bits) error = w - q * scale if col + 1 < weight.shape[1]: weight[:, col+1:] += error.unsqueeze(1) * H[col, col+1:] / H[col, col] module.weight = nn.Parameter(q) module.scale = scale
|
AWQ 量化
AWQ(Activation-aware Weight Quantization)根据激活值的重要性来量化权重:
1 2 3 4 5 6 7 8 9 10 11 12 13
| from transformers import AwqConfig
awq_config = AwqConfig( bits=4, fuse_max_seq_len=2048, do_fuse=True, )
model = AutoModelForCausalLM.from_pretrained( "model_name", quantization_config=awq_config, device_map="auto" )
|
评估量化质量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| def evaluate_quantization(original_model, quantized_model, eval_dataset): """评估量化对模型质量的影响""" original_ppl = compute_perplexity(original_model, eval_dataset) quantized_ppl = compute_perplexity(quantized_model, eval_dataset) ppl_increase = (quantized_ppl - original_ppl) / original_ppl print(f"原始困惑度: {original_ppl:.4f}") print(f"量化困惑度: {quantized_ppl:.4f}") print(f"困惑度增加: {ppl_increase:.2%}") return ppl_increase < 0.1
def compute_perplexity(model, dataset): """计算模型困惑度""" total_loss = 0 total_tokens = 0 model.eval() with torch.no_grad(): for batch in dataset: input_ids = batch["input_ids"].to(model.device) attention_mask = batch["attention_mask"].to(model.device) outputs = model(input_ids, attention_mask=attention_mask, labels=input_ids) total_loss += outputs.loss.item() * input_ids.shape[1] total_tokens += input_ids.shape[1] avg_loss = total_loss / total_tokens perplexity = torch.exp(torch.tensor(avg_loss)) return perplexity.item()
|
量化策略
选择量化精度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| def select_quantization_precision(model_size_gb, target_size_gb, quality_requirement): """根据模型大小和目标选择量化精度""" if quality_requirement == "high": if target_size_gb >= model_size_gb / 2: return "fp16" else: return "int8" elif quality_requirement == "medium": if target_size_gb >= model_size_gb / 4: return "int8" else: return "int4" else: if target_size_gb >= model_size_gb / 8: return "int4" else: return "int3"
|
混合精度量化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| def mixed_precision_quantization(model, sensitivity_threshold=0.1): """混合精度量化:敏感层使用高精度""" quantized_model = {} for name, module in model.named_modules(): if isinstance(module, nn.Linear): sensitivity = evaluate_layer_sensitivity(module) if sensitivity > sensitivity_threshold: quantized_model[name] = module.half() else: quantized_model[name] = quantize_to_int4(module) return quantized_model
|
部署量化模型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| from transformers import BitsAndBytesConfig
bnb_config = BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_use_double_quant=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16, )
model = AutoModelForCausalLM.from_pretrained( "model_name", quantization_config=bnb_config, device_map="auto" )
inputs = tokenizer("Hello, world!", return_tensors="pt").to(model.device) outputs = model.generate(**inputs, max_new_tokens=50)
|
总结
量化通过降低权重精度来减少模型大小和加速推理。常见方法包括 GPTQ 和 AWQ。选择合适的量化策略需要平衡模型大小、推理速度和质量。
下一步
下一课将介绍推理优化技术,进一步加速模型推理。
📚 本文改编自 AI Engineering from Scratch(MIT License · 作者 Rohit Ghumare),中文内容来自官方中文镜像。原课程共 503 课 · 20 阶段 · 免费开源,教程网站见 aiengineeringfromscratch.com。
📝 自我检查(课程配套测验)
Q1(学前) 70B 参数模型在 FP16 下仅权重需要多少 VRAM?
A. 35 GB
B. 70 GB
C. 140 GB
D. 280 GB
答案: C 解析: 700 亿参数 × 每个 FP16 参数 2 字节 = 1400 亿字节 = 140 GB。这超过单张 A100(80GB),仅加载权重至少需要两张 GPU。
Q2(学前) 在 LLM 语境中,quantization 是什么?
A. 移除未使用的 model layer
B. 降低权重的数值精度(例如 FP16 到 INT4)以减少内存占用并提高 inference 速度
C. 压缩训练数据
D. 缩小 vocabulary 大小
答案: B 解析: quantization 将高精度浮点 weight 映射到低精度整数。INT4 quantization 每个 weight 用 4 bit 而非 16 bit 存储,内存减少约 4 倍,精度损失通常很小。
Q3(学后) 训练后 quantization(PTQ)与 quantization-aware training(QAT)的关键区别是什么?
A. PTQ 更准确
B. PTQ 在训练后量化、无需 retraining;QAT 在训练中模拟 quantization,使 model 学会适应降低的精度
C. QAT 不使用 gradient
D. PTQ 需要更多数据
答案: B 解析: PTQ 快速(校准并量化即可)但可能损失精度。QAT 在训练中包含 fake quantization,让 model 调整 weight 以更好适应精度损失。QAT 通常精度更好。
Q4(学后) 「per-channel」quantization 是什么意思?为什么优于「per-tensor」?
A. 分别量化每个 output channel,每个 channel 使用不同的 scale/zero-point,减少 quantization 误差
B. 一次处理一个 color channel
C. 每个 channel 使用独立 GPU
D. 一种 data parallelism
答案: A 解析: per-tensor 对整个 weight matrix 使用一个 scale factor。per-channel 对每个 output channel(行)使用独立 scale。不同 channel 数值范围不同,per-channel 能更准确地捕捉。
Q5(学后) 为什么 Llama 3 70B 中 95% 的 weight 落在 -0.1 到 +0.1 之间?
A. model 训练不佳
B. 训练中的 weight decay 和 normalization 将 weight 推向较小值,使完整 FP16 范围显得浪费
C. weight 尚未收敛
D. 这是 Llama architecture 特有的
答案: B 解析: weight decay regularization 将 weight 向零收缩。layer normalization 使 activation 居中。两者结合产生集中在零附近的 weight 分布,使低精度 quantization 非常有效。