模型量化与压缩深度解析:INT8/INT4/AWQ/GPTQ 全面指南

4960 字
25 分钟
模型量化与压缩深度解析:INT8/INT4/AWQ/GPTQ 全面指南

1. 引言:为什么需要模型量化?#

1.1 大模型的体积困境#

大语言模型的体积增长已经超出硬件发展速度:

模型体积增长 vs GPU 显存增长:
模型参数增长:
┌─────────────────────────────────────────────────────────┐
│ GPT-2 (1.5B) 3 GB │
│ LLaMA-2 (7B) 14 GB │
│ LLaMA-2 (13B) 26 GB │
│ LLaMA-2 (70B) 140 GB │
│ GPT-4 (~1.8T) ~3600 GB (估计) │
└─────────────────────────────────────────────────────────┘
GPU 显存发展:
┌─────────────────────────────────────────────────────────┐
│ RTX 3090 24 GB │
│ RTX 4090 24 GB │
│ A100 40GB 40 GB │
│ A100 80GB 80 GB │
│ H100 80GB 80 GB │
└─────────────────────────────────────────────────────────┘
差距:
→ 70B 模型需要 140 GB(2 张 A100 80GB)
→ 量化后:70B 模型只需 35 GB(1 张 A100 40GB)

1.2 量化的核心思想#

量化(Quantization)是将高精度数值映射到低精度表示的过程:

┌─────────────────────────────────────────────────────────────┐
│ 量化原理 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 原始权重: [0.8234, -1.2345, 0.5678, -0.9012, ...] │
│ FP32 (32-bit, 4 bytes) │
│ │
│ ↓ 量化 │
│ │
│ 量化索引: [ 12, 3, 10, 5, ...] │
│ INT8 (8-bit, 1 byte) │
│ │
│ ↓ 存储 │
│ │
│ 存储: 索引 (1 byte) + 缩放因子 (4 bytes) │
│ │
│ 压缩率: 4 bytes → ~1.2 bytes (70% 减少) │
│ │
└─────────────────────────────────────────────────────────────┘

1.3 本系列文章关联#

文章关联
PEFT/LoRA 深度解析LoRA 等 PEFT 方法
QLoRA 深度解析量化 + LoRA 组合
后训练深度解析对齐训练中的量化应用

2. 量化基础理论#

2.1 数值表示基础#

class NumericalRepresentations:
"""
数值表示基础
"""
def fp32_structure(self):
"""
FP32 (Float32) 结构
符号位: 1 bit
指数位: 8 bits
尾数位: 23 bits
范围: ±1.18e-38 到 ±3.4e38
精度: ~7 位十进制有效数字
"""
return {
"sign": 1,
"exponent": 8,
"mantissa": 23,
"total_bits": 32,
"bytes": 4,
}
def fp16_structure(self):
"""
FP16 (Float16) 结构
符号位: 1 bit
指数位: 5 bits
尾数位: 10 bits
范围: ±6.1e-5 到 ±65504
精度: ~3-4 位十进制有效数字
"""
return {
"sign": 1,
"exponent": 5,
"mantissa": 10,
"total_bits": 16,
"bytes": 2,
}
def bf16_structure(self):
"""
BF16 (Brain Float) 结构
符号位: 1 bit
指数位: 8 bits (与 FP32 相同)
尾数位: 7 bits
范围: 与 FP32 相同
精度: ~2-3 位十进制有效数字
优势:比 FP16 更大的动态范围
"""
return {
"sign": 1,
"exponent": 8,
"mantissa": 7,
"total_bits": 16,
"bytes": 2,
}
def int8_structure(self):
"""
INT8 (Integer 8-bit) 结构
有符号: -128 到 127
无符号: 0 到 255
没有小数点,需要缩放因子
"""
return {
"signed": {"min": -128, "max": 127},
"unsigned": {"min": 0, "max": 255},
"total_bits": 8,
"bytes": 1,
}

2.2 量化类型#

class QuantizationTypes:
"""
量化类型
"""
def uniform_quantization(self):
"""
均匀量化
量化间隔均匀分布
x_q = round(x / s)
其中 s = (x_max - x_min) / (2^b - 1)
"""
return {
"type": "均匀",
"pros": "简单高效",
"cons": "对非均匀分布效果差",
"use_case": "权重分布较均匀时",
}
def non_uniform_quantization(self):
"""
非均匀量化
量化间隔按概率分布设计
常见方法:
- 分位数量化 (Quantile)
- NF4 量化 (Normal Float)
- 对数量化 (Logarithmic)
"""
return {
"type": "非均匀",
"pros": "更好地保留关键值精度",
"cons": "实现复杂,查找表开销",
"use_case": "权重分布不均匀时(如正态分布)",
}
def per_tensor_vs_per_channel(self):
"""
Per-Tensor vs Per-Channel 量化
Per-Tensor:
- 整个张量共享一个缩放因子
- 计算简单,精度较低
Per-Channel:
- 每个通道独立的缩放因子
- 计算稍复杂,精度更高
- 常用于量化权重
"""
return {
"per_tensor": {
"scale_sharing": "整个张量",
"memory_overhead": "低",
"accuracy": "中等",
},
"per_channel": {
"scale_sharing": "每个通道",
"memory_overhead": "高 (num_channels 个缩放因子)",
"accuracy": "高",
},
}

2.3 量化误差分析#

class QuantizationError:
"""
量化误差分析
"""
def compute_error(self, original, quantized):
"""
计算量化误差
"""
# 均方误差
mse = torch.mean((original - quantized) ** 2)
# 均方根误差
rmse = torch.sqrt(mse)
# 相对误差
relative_error = torch.mean(torch.abs(original - quantized) / (torch.abs(original) + 1e-8))
# 信噪比
signal = torch.mean(original ** 2)
noise = mse
snr = 10 * torch.log10(signal / noise)
return {
"MSE": mse.item(),
"RMSE": rmse.item(),
"Relative Error": relative_error.item(),
"SNR (dB)": snr.item(),
}
def error_sources(self):
"""
量化误差来源
"""
return {
"rounding_error": "round() 操作的截断误差",
"clamping_loss": "截断极端值造成的损失",
"dynamic_range": "动态范围选择不当",
"outlier_sensitivity": "异常值对整体量化的影响",
}

3. INT8 量化#

3.1 INT8 量化原理#

class INT8Quantization:
"""
INT8 量化实现
"""
def __init__(self):
self.scale = None
self.zero_point = None
def quantize_tensor(self, tensor, bits=8, scheme="symmetric"):
"""
量化张量
Args:
tensor: 输入张量
bits: 量化位数
scheme: "symmetric" 或 "asymmetric"
"""
if scheme == "symmetric":
return self.symmetric_quantize(tensor, bits)
else:
return self.asymmetric_quantize(tensor, bits)
def symmetric_quantize(self, tensor, bits=8):
"""
对称量化
特点:
- 零映射到零
- 缩放因子 = max(|x|) / (2^(bits-1) - 1)
- 量化值范围: [-127, 127] (INT8)
"""
max_val = torch.max(torch.abs(tensor))
# 缩放因子
scale = max_val / (2 ** (bits - 1) - 1)
# 量化
quantized = torch.round(tensor / scale).clamp(
-(2 ** (bits - 1) - 1),
(2 ** (bits - 1) - 1)
)
self.scale = scale
self.zero_point = 0
return quantized.to(torch.int8)
def asymmetric_quantize(self, tensor, bits=8):
"""
非对称量化
特点:
- 使用零点(zero_point)偏移
- 缩放因子 = (max - min) / (2^bits - 1)
- 量化值范围: [0, 255] (UINT8)
"""
min_val = torch.min(tensor)
max_val = torch.max(tensor)
# 缩放因子和零点
scale = (max_val - min_val) / (2 ** bits - 1)
zero_point = torch.round(-min_val / scale).clamp(0, 2 ** bits - 1)
# 量化
quantized = torch.round(tensor / scale + zero_point).clamp(0, 2 ** bits - 1)
self.scale = scale
self.zero_point = zero_point
return quantized.to(torch.uint8)
def dequantize(self, quantized):
"""
反量化
对称: x_rec = x_q * scale
非对称: x_rec = (x_q - zero_point) * scale
"""
if self.zero_point == 0:
return quantized.float() * self.scale
else:
return (quantized.float() - self.zero_point) * self.scale

3.2 LLM.int8():Transformer 的 INT8 量化#

class LLMint8:
"""
LLM.int8() 量化方法(Frantar et al., 2022)
核心思想:
1. 混合精度量化
2. 异常值用 FP16 保留
3. 普通值用 INT8
"""
def __init__(self, threshold=6.0):
self.threshold = threshold # 异常值阈值
def quantize_linear(self, weight):
"""
量化线性层
"""
# Step 1: 识别异常值
# 异常值通常出现在某些特定维度
abs_weight = torch.abs(weight)
outlier_mask = abs_weight > self.threshold
# Step 2: 分离
weight_normal = weight.clone()
weight_normal[outlier_mask] = 0
# Step 3: 量化正常值
max_val = torch.max(torch.abs(weight_normal))
scale = max_val / 127.0
quantized_normal = torch.round(weight_normal / scale).to(torch.int8)
# Step 4: 保留异常值(FP16)
outlier_weight = weight.clone()
outlier_weight[~outlier_mask] = 0
return {
"quantized": quantized_normal,
"scale": scale,
"outliers": outlier_weight,
"outlier_mask": outlier_mask,
}
def forward(self, x, weight_data, scale, outliers, outlier_mask):
"""
前向传播
"""
# 正常值的 INT8 矩阵乘法(使用向量量化)
output_normal = self.int8_matmul(x, weight_data, scale)
# 异常值的 FP16 矩阵乘法
output_outlier = x @ outliers.T
return output_normal + output_outlier
def int8_matmul(self, x, weight, scale):
"""
INT8 矩阵乘法
使用向量量化加速
"""
# 将激活量化为 INT8
x_scale = torch.max(torch.abs(x)) / 127.0
x_quant = torch.round(x / x_scale).to(torch.int8)
# INT8 矩阵乘法(结果是 INT32)
result_int32 = x_quant @ weight.T
# 反量化
result = result_int32.float() * (x_scale * scale)
return result

3.3 INT8 量化的实践#

class INT8Practice:
"""
INT8 量化的实践
"""
def with_bitsandbytes(self):
"""
使用 bitsandbytes 库
"""
return """
from transformers import AutoModelForCausalLM
from bitsandbytes import BitsAndBytesConfig
# INT8 配置
bnb_config = BitsAndBytesConfig(
load_in_8bit=True,
)
# 加载 INT8 模型
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-2-7b-hf",
quantization_config=bnb_config,
device_map="auto",
)
# 内存节省:7B 模型从 14GB → 7GB
"""
def issues(self):
"""
INT8 量化的常见问题
"""
return {
"outlier_problem": "某些维度存在异常值,影响整体量化质量",
"activation_quantization": "激活值的动态范围难以估计",
"accuracy_loss": "对大模型可能有 5-10% 的性能损失",
"hardware_support": "需要 INT8 硬件支持(如 Tensor Core)",
}

4. INT4 量化#

4.1 INT4 量化的挑战#

class INT4Challenges:
"""
INT4 量化的挑战
"""
def challenges(self):
"""
INT4 的主要挑战
"""
return {
"precision_loss": "只有 16 个离散值,精度损失严重",
"outlier_sensitivity": "异常值影响更大",
"symmetry_issues": "对称和非对称的选择更敏感",
"per_channel_required": "通常需要 per-channel 量化",
}
def solutions(self):
"""
解决方案
"""
return {
"nf4": "使用专为神经网络设计的 NF4 类型",
"mixed_precision": "异常值用高精度",
"fine_grained": "更细粒度的分组",
"calibration": "更好的校准数据",
}

4.2 NF4 量化(已在 QLoRA 文章中详述)#

class NF4Quantization:
"""
NF4 量化(QLoRA 论文)
核心思想:
1. 神经网络权重近似服从正态分布
2. 量化值应该按正态分布设计
3. 零附近分配更多量化级别
"""
def get_quantile_boundaries(self, num_bits=4):
"""
计算 NF4 分位边界
对于 4-bit,有 16 个量化值
"""
import scipy.stats as stats
import numpy as np
num_values = 2 ** num_bits # 16
# 计算分位点
probs = np.linspace(0, 1, num_values + 1)
# 逆正态 CDF
boundaries = stats.norm.ppf(probs)
# 归一化
max_abs = np.max(np.abs(boundaries))
normalized_boundaries = boundaries / max_abs
return normalized_boundaries

4.3 GPTQ 训练后量化#

class GPTQ:
"""
GPTQ: Generative Pre-trained Transformer Quantization
核心思想:
1. 训练后量化(无需重训练)
2. 使用海森矩阵信息补偿量化误差
3. 逐列量化,带误差反馈
"""
def __init__(self, model, bits=4, block_size=128):
self.model = model
self.bits = bits
self.block_size = block_size
self.quantization_stats = {}
def quantize_model(self):
"""
GPTQ 量化流程
"""
for name, module in self.model.named_modules():
if isinstance(module, nn.Linear):
# 量化该层
quantized_weight, scale, gptq_error = self.quantize_layer(
module.weight.data,
module.in_features,
module.out_features,
)
# 应用量化权重
module.weight.data = quantized_weight
# 记录信息
self.quantization_stats[name] = {
"scale": scale,
"error": gptq_error,
}
def quantize_layer(self, weight, in_features, out_features):
"""
GPTQ 量化单层
关键步骤:
1. 计算该层的海森矩阵 H = (X^T X)
2. 逐列量化,累积误差
3. 用误差反馈修正未量化列
"""
# 获取输入数据(用于校准)
# 假设 self.calibration_data 已准备好
batch_size = min(1024, self.calibration_data.shape[0])
X = self.calibration_data[:batch_size] # (batch, in_features)
# 计算海森矩阵的逆(近似)
H = X.T @ X / batch_size
H_inv = torch.linalg.inv(H + torch.eye(in_features) * 1e-6)
# 权重按列分组
num_groups = in_features // self.block_size
quantized_weights = weight.clone()
errors = torch.zeros_like(weight)
for col_idx in range(in_features):
# 获取该列
w_col = weight[:, col_idx]
# 该列的量化误差
error = errors[:, col_idx]
# 补偿后的权重
w_compensated = w_col + error
# 量化
max_val = torch.max(torch.abs(w_compensated))
scale = max_val / (2 ** (self.bits - 1) - 1)
w_quant = torch.round(w_compensated / scale).clamp(
-(2 ** (self.bits - 1)),
(2 ** (self.bits - 1) - 1)
)
# 反量化
w_dequant = w_quant * scale
# 更新误差
new_error = w_compensated - w_dequant
errors[:, col_idx] = new_error
# 误差反馈到未量化列
if col_idx < in_features - 1:
h_inv_diag = H_inv[col_idx, col_idx]
weight_col_contribution = (H_inv[col_idx, col_idx + 1:] / h_inv_diag).unsqueeze(0)
errors[:, col_idx + 1:] += new_error.unsqueeze(1) * weight_col_contribution
quantized_weights[:, col_idx] = w_quant
return quantized_weights, scale, errors

5. AWQ:激活感知量化#

5.1 AWQ 的核心思想#

class AWQ:
"""
AWQ: Activation-Aware Weight Quantization (Lin et al., 2023)
核心洞察:
量化误差对模型性能的影响不仅取决于权重分布,
还取决于激活值的分布。
关键发现:
- 与输入激活值相乘的权重应该更精确
- 这些"显著权重"需要更高的精度
"""
def __init__(self, model, bits=4):
self.model = model
self.bits = bits
self.scales = {}
def find_important_weights(self, activation_stats):
"""
找出重要的权重
重要权重 = 与大激活值相乘的权重
s_i = |W_i| · |X_i|
其中 W_i 是权重,X_i 是激活值
"""
# activation_stats: 激活值统计 (mean, std)
weight_importance = {}
for name, module in self.model.named_modules():
if isinstance(module, nn.Linear):
# 权重重要性
weight_scale = torch.abs(module.weight.data).mean(dim=1) # per-output channel
# 激活重要性(如果有统计)
if name in activation_stats:
act_scale = activation_stats[name]
else:
act_scale = torch.ones_like(weight_scale)
# 组合重要性
importance = weight_scale * act_scale
weight_importance[name] = importance
return weight_importance

5.2 AWQ 的实现#

def quantize_with_awq(self, weight, activation_scale, name):
"""
AWQ 量化
步骤:
1. 找出显著权重通道
2. 为不同通道分配不同精度
3. 量化并存储混合精度
"""
# Step 1: 计算权重重要性
weight_abs = torch.abs(weight) # (out_features, in_features)
importance = weight_abs * activation_scale.unsqueeze(1) # 广播
# Step 2: 找出 Top-K 显著通道
k = weight.shape[0] // 4 # 25% 用高精度
_, topk_indices = torch.topk(importance.mean(dim=1), k=k)
# Step 3: 分离权重
weight_high = weight.clone()
weight_low = weight.clone()
for idx in topk_indices:
weight_low[idx, :] = 0
weight_high[~torch.isin(torch.arange(weight.shape[0]), topk_indices), :] = 0
# Step 4: 分别量化
# 显著权重用 INT8
quantized_high, scale_high = self.quantize_int8(weight_high)
# 不显著权重用 INT4
quantized_low, scale_low = self.quantize_int4(weight_low)
return {
"quantized_high": quantized_high,
"scale_high": scale_high,
"quantized_low": quantized_low,
"scale_low": scale_low,
"important_channels": topk_indices,
}
def quantize_int8(self, weight):
"""INT8 量化"""
max_val = torch.max(torch.abs(weight[weight != 0])) if weight.sum() != 0 else torch.tensor(1.0)
scale = max_val / 127.0
quantized = torch.round(weight / scale).to(torch.int8)
return quantized, scale
def quantize_int4(self, weight):
"""INT4 量化"""
max_val = torch.max(torch.abs(weight[weight != 0])) if weight.sum() != 0 else torch.tensor(1.0)
scale = max_val / 7.0
quantized = torch.round(weight / scale).to(torch.int4)
return quantized, scale

5.3 AWQ vs GPTQ#

class AWQvsGPTQ:
"""
AWQ vs GPTQ 对比
"""
def compare(self):
"""
性能对比
"""
return {
"accuracy": {
"gptq": "中等精度损失",
"awq": "精度损失更小(保留显著权重)",
},
"speed": {
"gptq": "量化速度较慢(需要海森矩阵)",
"awq": "量化速度较快(只统计激活值)",
},
"memory": {
"gptq": "需要存储量化常数和误差",
"awq": "需要存储通道索引",
},
"hardware_friendly": {
"gptq": "需要特殊解码",
"awq": "更硬件友好",
},
}
def benchmark_results(self):
"""
基准测试结果(来自论文)
"""
return {
"wikitext_perplexity": {
"FP16": 12.5,
"GPTQ-INT4": 14.2,
"AWQ-INT4": 13.1,
"improvement": "AWQ 比 GPTQ 好 0.8 perplexity",
},
}

6. GGUF/llama.cpp 量化格式#

6.1 GGUF 格式概述#

class GGUFFormat:
"""
GGUF (Generic Gradient Unspecified Format)
llama.cpp 使用的量化格式
支持多种量化精度和混合精度
"""
def supported_quant_types(self):
"""
支持的量化类型
"""
return {
"Q4_K_S": {
"name": "Q4_K_S",
"bits": 4,
"description": "4-bit, small (MLP layers)",
"size_factor": 0.5,
},
"Q4_K_M": {
"name": "Q4_K_M",
"bits": 4,
"description": "4-bit, medium",
"size_factor": 0.55,
},
"Q5_K_S": {
"name": "Q5_K_S",
"bits": 5,
"description": "5-bit, small",
"size_factor": 0.6,
},
"Q5_K_M": {
"name": "Q5_K_M",
"bits": 5,
"description": "5-bit, medium",
"size_factor": 0.65,
},
"Q6_K": {
"name": "Q6_K",
"bits": 6,
"description": "6-bit, medium",
"size_factor": 0.75,
},
"Q8_0": {
"name": "Q8_0",
"bits": 8,
"description": "8-bit, almost full precision",
"size_factor": 1.0,
},
"F16": {
"name": "F16",
"bits": 16,
"description": "Half precision",
"size_factor": 2.0,
},
}

6.2 Q4_K_M 量化详解#

class Q4KMQuantization:
"""
Q4_K_M 量化格式详解
K 表示使用了 K-Quant 方案
M 表示中等质量
结构:
- 每个 block 有 32 个权重
- 权重被分成小组
"""
def structure(self):
"""
Q4_K_M 结构
每个 block (32 个权重):
- 缩放因子 (4 bytes, FP16)
- 微缩放因子 (4 bytes, FP16)
- 量化权重 (16 bytes, 32 个 4-bit 值)
总计: 24 bytes / 32 weights = 6 bits/weight
对比:
- FP16: 16 bits/weight
- Q4_K_M: 6 bits/weight (压缩到 37.5%)
"""
return {
"block_size": 32,
"scales": {
"main_scale": "FP16 (2 bytes)",
"mini_scales": "FP16 (2 bytes) per 8 weights",
},
"weights": "4-bit (0.5 bytes) per weight",
"total_bytes_per_block": 24,
"bits_per_weight": 6,
"compression_ratio": "62.5% reduction",
}
def quantize_block(self, weight_block):
"""
量化一个 block
"""
# weight_block: (32,)
# Step 1: 找出最大值确定缩放
max_val = torch.max(torch.abs(weight_block))
main_scale = max_val / 8.0 # 4-bit 范围 [-8, 7]
# Step 2: 第一次量化
q1 = torch.round(weight_block / main_scale).to(torch.int8)
# Step 3: 计算残差
residual = weight_block - q1 * main_scale
# Step 4: 对残差再次量化
# 分成 4 组,每组 8 个值
num_groups = 4
group_size = 8
mini_scales = []
q2_groups = []
for i in range(num_groups):
group = residual[i * group_size:(i + 1) * group_size]
if torch.abs(group).max() > 1e-6:
mini_scale = torch.max(torch.abs(group)) / 8.0
q2 = torch.round(group / mini_scale).to(torch.int4)
else:
mini_scale = torch.tensor(0.0)
q2 = torch.zeros(group_size, dtype=torch.int32)
mini_scales.append(mini_scale)
q2_groups.append(q2)
return {
"q1": q1, # INT8
"main_scale": main_scale,
"q2": q2_groups, # 4 INT4
"mini_scales": mini_scales,
}

6.3 llama.cpp 量化实践#

class LlamaCppQuantization:
"""
llama.cpp 量化实践
"""
def convert_and_quantize(self, model_path, output_path, quant_type="Q4_K_M"):
"""
转换和量化模型
步骤:
1. 转换为 GGUF 格式
2. 应用量化
"""
return f"""
# 使用 llama.cpp 工具
# 1. 安装
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
mkdir build && cd build
cmake .. && cmake --build . --config Release
# 2. 转换模型到 GGUF
python3 -m llama_cpp.llama_cupy_importer \\
--model {model_path} \\
--outfile converted.gguf
# 3. 量化
./quantize converted.gguf {output_path} {quant_type}
# 或者使用 Python
from llama_cpp import Llama
# 自动量化
model = Llama.from_pretrained(
"{model_path}",
quantization="Q4_K_M",
)
"""
def file_size_comparison(self):
"""
文件大小对比
"""
return {
"model": "LLaMA-2 7B",
"fp16": "14.0 GB",
"Q8_0": "7.0 GB (50%)",
"Q6_K": "5.3 GB (38%)",
"Q5_K_M": "4.6 GB (33%)",
"Q4_K_M": "3.9 GB (28%)",
"Q4_K_S": "3.5 GB (25%)",
}

7. 量化性能分析#

7.1 精度损失评估#

class QuantizationPerformance:
"""
量化性能评估
"""
def metrics(self):
"""
常用评估指标
"""
return {
"perplexity": "语言模型困惑度(越低越好)",
"accuracy": "下游任务准确率",
"cosine_similarity": "权重相似度",
"relative_error": "相对误差",
"bit_error_rate": "比特翻转率",
}
def benchmark_results(self):
"""
基准测试结果
"""
return {
"LLaMA-2 7B on WikiText-2": {
"FP16": {"perplexity": 5.9, "memory": "14 GB"},
"INT8": {"perplexity": 6.1, "memory": "7 GB"},
"Q5_K_M": {"perplexity": 6.3, "memory": "4.6 GB"},
"Q4_K_M": {"perplexity": 6.5, "memory": "3.9 GB"},
"Q4_K_S": {"perplexity": 6.8, "memory": "3.5 GB"},
},
}
def task_specific_loss(self):
"""
任务相关的精度损失
"""
return {
"math_reasoning": {
"sensitivity": "高",
"recommended": "Q5_K_M 或更高",
"reason": "数学需要精确计算",
},
"code_generation": {
"sensitivity": "高",
"recommended": "Q5_K_M 或更高",
"reason": "代码语法需要精确",
},
"chat_completion": {
"sensitivity": "中",
"recommended": "Q4_K_M",
"reason": "自然语言容忍度更高",
},
"embedding": {
"sensitivity": "低",
"recommended": "Q4_K_S",
"reason": "相对距离更重要",
},
}

7.2 量化 vs 推理速度#

class QuantizationSpeed:
"""
量化与推理速度
"""
def speed_comparison(self):
"""
速度对比(相对 FP16)
"""
return {
"FP16": {
"speed": "1.0x (基准)",
"notes": "需要大量显存",
},
"INT8": {
"speed": "1.2-1.5x",
"notes": "需要 INT8 硬件支持",
},
"Q8_0": {
"speed": "1.3x",
"notes": "llama.cpp 优化良好",
},
"Q6_K": {
"speed": "1.5x",
"notes": "平衡精度和速度",
},
"Q5_K_M": {
"speed": "1.7x",
"notes": "推荐选择",
},
"Q4_K_M": {
"speed": "1.8x",
"notes": "最流行的选择",
},
}
def bottlenecks(self):
"""
推理瓶颈
"""
return {
"memory_bandwidth": "量化后显存需求降低,带宽成为新瓶颈",
"dequantization": "需要反量化,有计算开销",
"batch_size": "大批量时量化优势更明显",
"sequence_length": "长序列时 KV cache 是瓶颈",
}

8. 量化最佳实践#

8.1 量化方法选择指南#

class QuantizationGuide:
"""
量化方法选择指南
"""
def choose_quantization(self, model_size, hardware, use_case):
"""
选择量化方法
"""
if use_case == "inference_only":
return self.for_inference(model_size, hardware)
elif use_case == "fine_tuning":
return self.for_fine_tuning(model_size)
else:
return self.for_both(model_size, hardware)
def for_inference(self, model_size, hardware):
"""
仅推理场景
"""
if hardware == "consumer_gpu":
return {
"7B": "Q4_K_M (推荐) 或 Q5_K_M (更高精度)",
"13B": "Q4_K_M",
"70B": "Q4_K_0 或 Q5_K_M (多卡)",
}
elif hardware == "server_gpu":
return {
"7B": "Q5_K_M 或 Q6_K",
"13B": "Q5_K_M",
"70B": "Q4_K_M",
}
else:
return "CPU 推理: Q4_K_S 或更低"
def for_fine_tuning(self, model_size):
"""
微调场景
"""
return {
"7B": "QLoRA (Q4_K_M + LoRA)",
"13B": "QLoRA (Q4_K_M + LoRA)",
"70B": "QLoRA (Q4_K_M + LoRA)",
"note": "QLoRA 是微调的最佳选择",
}
def for_both(self, model_size, hardware):
"""
推理和微调
"""
return "先 QLoRA 微调,再量化推理"

8.2 量化流程#

class QuantizationWorkflow:
"""
量化工作流程
"""
def full_workflow(self):
"""
完整量化流程
"""
return """
1. 准备模型和数据
- 加载 FP16 模型
- 准备校准数据集(100-1000 条样本)
2. 收集统计信息
- 前向传播收集激活值统计
- 记录权重分布
3. 选择量化方法
- INT8: bitsandbytes
- GPTQ: 自动量化
- AWQ: 激活感知量化
- GGML: llama.cpp
4. 执行量化
- 按层量化
- 记录缩放因子和零点
5. 验证精度
- perplexity 测试
- 下游任务评估
6. 保存模型
- 保存量化权重
- 保存量化参数
"""

8.3 常见问题与解决#

class QuantizationTroubleshooting:
"""
量化问题排查
"""
def issues_and_solutions(self):
"""
常见问题与解决方案
"""
return {
"severe_accuracy_loss": {
"cause": "量化精度过低或校准数据不足",
"solution": "使用更高精度(Q5/Q6)或增加校准数据",
},
"out_of_memory": {
"cause": "量化格式不匹配硬件",
"solution": "使用更低的量化精度或混合精度",
},
"slow_inference": {
"cause": "量化格式计算不友好",
"solution": "使用 llama.cpp 或 AWQ",
},
"model_corruption": {
"cause": "量化参数计算错误",
"solution": "检查缩放因子和零点计算",
},
}

9. 工具与库#

9.1 量化工具对比#

class QuantizationTools:
"""
量化工具对比
"""
TOOLS = {
"bitsandbytes": {
"language": "Python",
"quant_types": ["INT8", "NF4"],
"use_case": "训练和推理",
"ease_of_use": "高(HuggingFace 集成)",
"hardware": "NVIDIA GPU",
},
"GPTQ": {
"language": "Python",
"quant_types": ["GPTQ (INT4)"],
"use_case": "训练后量化",
"ease_of_use": "中",
"hardware": "NVIDIA GPU",
},
"AWQ": {
"language": "Python",
"quant_types": ["AWQ (混合 INT4)"],
"use_case": "训练后量化",
"ease_of_use": "中",
"hardware": "通用",
},
"llama.cpp": {
"language": "C/C++",
"quant_types": ["Q4_K_S", "Q5_K_M", "Q6_K", "Q8_0"],
"use_case": "推理优化",
"ease_of_use": "中",
"hardware": "通用(CPU/GPU)",
},
"llamafile": {
"language": "C/C++",
"quant_types": ["Q4_K_S", "Q4_K_M", "Q5_K_M"],
"use_case": "一键部署",
"ease_of_use": "高(单文件)",
"hardware": "通用",
},
}

9.2 使用示例#

class ToolExamples:
"""
工具使用示例
"""
def bitsandbytes_example(self):
"""
bitsandbytes 示例
"""
return '''
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
# INT8
bnb_config = BitsAndBytesConfig(load_in_8bit=True)
model = AutoModelForCausalLM.from_pretrained(
"model_name",
quantization_config=bnb_config,
)
# NF4 (QLoRA)
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_use_double_quant=True,
)
'''
def gptq_example(self):
"""
GPTQ 示例
"""
return '''
from auto_gptq import AutoGPTQForCausalLM
# 量化
model = AutoGPTQForCausalLM.from_pretrained("model_name")
model.quantize("calibration_data.jsonl")
model.save_quantized("quantized_model/")
'''
def awq_example(self):
"""
AWQ 示例
"""
return '''
from awq import AutoAWQForCausalLM
# 量化
model = AutoAWQForCausalLM.from_pretrained("model_name")
quant_config = {"zero_point": True, "q_group_size": 128}
model.quantize("calibration_data.jsonl", quant_config=quant_config)
model.save_quantized("quantized_model/")
'''

10. 核心公式汇总#

10.1 对称量化#

xq=round(xs),s=max(x)2b11x_q = \text{round}\left(\frac{x}{s}\right), \quad s = \frac{\max(|x|)}{2^{b-1}-1}

10.2 非对称量化#

xq=round(xs+z),s=max(x)min(x)2b1,z=min(x)sx_q = \text{round}\left(\frac{x}{s} + z\right), \quad s = \frac{\max(x) - \min(x)}{2^b - 1}, \quad z = -\frac{\min(x)}{s}

10.3 GPTQ 误差补偿#

ej=wjw^j,wi=wiHij1Hjj1ej\mathbf{e}_j = \mathbf{w}_j - \hat{\mathbf{w}}_j, \quad \mathbf{w}_i' = \mathbf{w}_i - \frac{H_{ij}^{-1}}{H_{jj}^{-1}} \mathbf{e}_j

10.4 AWQ 权重重要性#

si=WiXis_i = |\mathbf{W}_i| \cdot |\mathbf{X}_i|

11. 总结#

11.1 量化方法对比#

┌─────────────────────────────────────────────────────────────┐
│ 量化方法对比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 方法 精度 速度 显存 适用场景 │
│ ───────────────────────────────────────────────────────── │
│ FP16 基准 1.0x 高 资源充足 │
│ INT8 高 1.2x 中 通用推理 │
│ GPTQ 中高 1.5x 低 4-bit 推理 │
│ AWQ 高 1.6x 低 高精度 4-bit │
│ QLoRA 高 中 极低 微调 + 推理 │
│ GGML/Q4_K_M 中 1.8x 极低 本地推理 │
│ ───────────────────────────────────────────────────────── │
│ │
└─────────────────────────────────────────────────────────────┘

11.2 选择建议#

量化选择建议:
推理场景:
→ 显存充足: INT8 或 Q5_K_M
→ 显存有限: Q4_K_M (最佳平衡)
→ 极致压缩: Q4_K_S 或混合精度
微调场景:
→ 必须使用 QLoRA (NF4 + LoRA)
→ 推荐 r=8-16, alpha=2r
CPU 推理:
→ 使用 llama.cpp
→ 推荐 Q4_K_S 或 Q4_K_M
工具选择:
→ 简单易用: bitsandbytes
→ 最佳精度: AWQ
→ 最佳压缩: llama.cpp GGUF
推荐阅读
  1. LLM.int8()(Frantar et al., 2022)—— INT8 量化
  2. GPTQ(Frantar et al., 2023)—— 训练后量化
  3. AWQ(Lin et al., 2023)—— 激活感知量化
  4. QLoRA(Dettmers et al., 2023)—— 量化微调
  5. llama.cpp—— GGUF 格式实现

参考资料#

  1. Frantar, E., et al. (2022). “LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale.” NeurIPS.
  2. Frantar, E., et al. (2023). “GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers.” ICLR.
  3. Lin, J., et al. (2023). “AWQ: Activation-Aware Weight Quantization for LLM Compression.” ICML Workshop.
  4. Dettmers, T., et al. (2023). “QLoRA: Efficient Finetuning of Quantized LLMs.” NeurIPS.
  5. Sheng, Y., et al. (2023). “QoQ: Progressive Quantization.” ICML.
  6. Yao, J., et al. (2023). “ZeroQuantV2: Zero-Holistic Quantization.” arXiv.
  7. ggerganov/llama.cpp. “LLM inference in C/C++.” GitHub.

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

模型量化与压缩深度解析:INT8/INT4/AWQ/GPTQ 全面指南
https://aiattnstudio.link/posts/quantization/
作者
Federico
发布于
2026-07-16
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author

Federico

AI Research Lab

Hello, I'm Federico.

关于实验室 / About
公告

欢迎来到Federico的个人博客

分类
标签
站点统计
57文章
7分类
404标签