【大模型】- 量化

算法

量化

减少模型大小,加速推理

类型: 学习 | 语言: Python | 🏷 前置:《量化》(本系列第 10 篇)

学习目标

  • 理解量化的原理
  • 实现常见的量化方法
  • 使用 GPTQ 和 AWQ 量化模型
  • 评估量化后的模型质量
  • 选择合适的量化策略

量化概述

量化是将模型权重从高精度(FP32/FP16)转换为低精度(INT8/INT4)的过程,以减少模型大小和加速推理。

量化的好处

  1. 减少内存:INT4 量化可将模型大小减少 4-8 倍
  2. 加速推理:低精度计算更快
  3. 降低能耗:更少的内存带宽和计算

量化类型

类型 精度 大小减少 质量影响
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

# 使用 GPTQ 量化
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

# 计算 Hessian 矩阵
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 # 如果增加小于 10%,则可接受

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: # low
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:
# 敏感层使用 FP16
quantized_model[name] = module.half()
else:
# 不敏感层使用 INT4
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
# 使用 bitsandbytes 量化
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 非常有效。

  • 标题: 【大模型】- 量化
  • 作者:
  • 创建于 : 2026-08-19 09:11:00
  • 更新于 : 2026-08-21 16:20:11
  • 链接: https://sxl-space.tk/2026/08/19/010_LLM/010_LLM-11-Quantization/
  • 版权声明: 版权所有 © 宋,禁止转载。