AI 模型量化压缩技术教程
SEO 元信息
- 名称:AI模型量化压缩技术教程
- 描述:零基础AI模型量化压缩技术教程,涵盖量化基础原理、PTQ/QAT、GPTQ/AWQ/GGUF量化实战、性能评估、大模型4bit量化部署等核心技能,适合AI工程师系统学习。
- 关键词:模型量化, GPTQ, AWQ, GGUF, 大模型压缩
- 长尾关键词:大模型量化压缩教程, GPTQ量化实战教程, AWQ模型量化入门, GGUF格式量化部署教程
目录
- 为什么需要模型量化
- 量化基础概念与数据类型
- 量化原理与数学基础
- 训练后量化 PTQ vs 量化感知训练 QAT
- GPTQ 量化详解与实战
- AWQ 量化技术
- GGUF 格式与 llama.cpp 量化
- SmoothQuant 平滑量化
- 量化模型性能评估
- 量化工具对比与选型
- 实战:将 70B 模型量化到 4bit 并部署
- 总结与展望
1. 为什么需要模型量化
随着大语言模型(LLM)参数量从数十亿飙升到数千亿,模型的存储和推理成本成为落地应用的核心瓶颈。以 Llama-2-70B 为例,原始 FP16 权重约占 140GB 显存,单张消费级显卡根本无法加载,更不用说实时推理。
模型量化(Quantization)通过降低权重和激活值的数值精度来压缩模型体积、加速推理、减少显存占用,是当前最主流的大模型压缩手段。
量化的核心收益
| 指标 | FP16 原始模型 | INT4 量化模型 | 改善幅度 |
|---|---|---|---|
| 模型体积 | 140 GB | 35 GB | 4× |
| 推理显存 | 160 GB | 40 GB | 4× |
| 推理速度 | 基准 | 1.5-3× 提升 | 显著 |
| 精度损失 | 基准 | < 2% | 可接受 |
量化不是简单的"丢精度",而是一门融合了线性代数、信息论和硬件架构的系统工程。
2. 量化基础概念与数据类型
2.1 常见数值精度
FP32(单精度浮点):32 位,1 位符号 + 8 位指数 + 23 位尾数。训练时代的默认精度,范围大、精度高,但存储和计算成本最高。
FP16(半精度浮点):16 位,1 位符号 + 5 位指数 + 10 位尾数。推理常用的精度,显存减半,但指数范围较小,容易溢出。
BF16(Brain Float 16):16 位,1 位符号 + 8 位指数 + 7 位尾数。Google 提出,保留了 FP32 的指数范围,牺牲尾数精度。A100/H100 等新一代 GPU 原生支持。
INT8(8 位整数):范围 -128 到 127(有符号)或 0 到 255(无符号)。量化推理的基础精度,精度损失可控。
INT4(4 位整数):范围 -8 到 7(有符号)。极致压缩精度,大模型量化的前沿方向,需要更精细的量化策略。
NF4(Normal Float 4):QLoRA 提出的特殊 4 位格式,专为正态分布权重设计,量化误差比 INT4 更小。
2.2 位宽对比
精度 位宽 模型体积(70B) 显存需求 精度损失
─────────────────────────────────────────────────────
FP32 32 280 GB 320+ GB 无
FP16 16 140 GB 160 GB 极小
BF16 16 140 GB 160 GB 极小
INT8 8 70 GB 80 GB 小
INT4 4 35 GB 40 GB 中等
INT3 3 26 GB 30 GB 较大
INT2 2 18 GB 20 GB 大
3. 量化原理与数学基础
3.1 线性量化(Uniform Quantization)
线性量化是最基础的量化方式,将连续浮点数映射到离散整数。
对称量化公式:
q = round(x / s)
x̂ = q × s
其中 s 是缩放因子(scale),q 是量化后的整数值。
计算缩放因子:
s = max(|x|) / (2^(b-1) - 1)
其中 b 是目标位宽。
非对称量化公式:
q = round((x - z) / s)
x̂ = q × s + z
其中 z 是零点偏移(zero-point),能更好地适应非对称分布的权重。
3.2 分组量化(Group Quantization)
实际量化时,并非对整个张量使用同一个缩放因子,而是将权重分成若干小组,每组独立量化。
import torch
def group_quantize(weight, group_size=128, bits=4):
"""分组对称量化"""
# 将权重 reshape 为 [N, group_size]
orig_shape = weight.shape
weight = weight.reshape(-1, group_size)
# 计算每组的缩放因子
max_val = weight.abs().max(dim=1, keepdim=True).values
scale = max_val / (2**(bits-1) - 1)
scale = scale.clamp(min=1e-10)
# 量化
q_weight = torch.round(weight / scale).clamp(
-(2**(bits-1)), 2**(bits-1) - 1
).to(torch.int8)
# 反量化
dequant_weight = q_weight * scale
return dequant_weight.reshape(orig_shape), scale, q_weight
分组大小的选择:
- group_size=128:精度和效率的最佳平衡,业界主流选择
- group_size=64:精度更高,但量化元数据开销增加
- group_size=-1(不分组):整列/整层共用一个 scale,最省内存但精度最差
3.3 量化误差分析
量化引入的误差主要来自舍入误差和截断误差:
MSE = (1/N) × Σ(x - x̂)²
降低误差的方法:
- 校准数据:用真实数据统计激活值分布
- 逐通道量化:每个输出通道独立量化
- 混合精度:敏感层保持高精度,非敏感层激进量化
4. 训练后量化 PTQ vs 量化感知训练 QAT
4.1 训练后量化(Post-Training Quantization, PTQ)
PTQ 在模型训练完成后直接对权重进行量化,不需要重新训练,是大模型量化的主流方案。
优点:
- 无需训练数据或仅需少量校准数据(128-1024 条)
- 速度快,几小时即可完成 70B 模型量化
- 不需要 GPU 集群
缺点:
- 低位宽(4bit 以下)精度损失较大
- 对激活值中的离群值(outlier)敏感
适用场景:INT8 推理、4bit 部署、资源受限的边缘设备
4.2 量化感知训练(Quantization-Aware Training, QAT)
QAT 在训练过程中模拟量化操作,让模型"适应"低精度表示。
核心思想:在前向传播中插入伪量化节点(fake quantization),在反向传播中使用直通估计器(Straight-Through Estimator, STE)传递梯度。
import torch
import torch.nn as nn
class FakeQuantize(torch.autograd.Function):
@staticmethod
def forward(ctx, x, scale, zero_point, qmin, qmax):
x_q = torch.round(x / scale + zero_point)
x_q = torch.clamp(x_q, qmin, qmax)
x_dq = (x_q - zero_point) * scale
return x_dq
@staticmethod
def backward(ctx, grad_output):
# STE: 梯度直接传递
return grad_output, None, None, None, None
class QATLinear(nn.Module):
def __init__(self, in_features, out_features, bits=4):
super().__init__()
self.linear = nn.Linear(in_features, out_features)
self.bits = bits
self.qmin = -(2**(bits-1))
self.qmax = 2**(bits-1) - 1
def forward(self, x):
# 计算权重的 scale
w_scale = self.linear.weight.abs().max() / self.qmax
# 伪量化权重
w_q = FakeQuantize.apply(
self.linear.weight, w_scale, 0, self.qmin, self.qmax
)
return torch.nn.functional.linear(x, w_q, self.linear.bias)
优点:精度通常优于 PTQ,尤其在极低位宽(2-3bit)时优势明显。
缺点:需要完整训练流程、大量计算资源、训练时间长。
适用场景:对精度要求极高的生产环境、2-3bit 极致压缩。
4.3 PTQ vs QAT 对比
| 维度 | PTQ | QAT |
|---|---|---|
| 训练数据需求 | 少量校准数据(128-1024条) | 完整训练数据集 |
| 计算成本 | 低(小时级) | 高(天级,需GPU集群) |
| INT8 精度 | 优秀(损失<1%) | 优秀 |
| INT4 精度 | 良好(损失1-3%) | 优秀(损失<1%) |
| INT2 精度 | 差 | 可接受 |
| 工具复杂度 | 简单 | 复杂 |
| 典型方案 | GPTQ, AWQ, GGUF | QLoRA, BitNet |
5. GPTQ 量化详解与实战
5.1 GPTQ 原理
GPTQ(GPT-Quantization)基于 OBQ(Optimal Brain Quantization) 框架,核心思想是:逐列量化权重矩阵,每量化一列后,用 Hessian 矩阵的逆来补偿剩余列的误差。
数学表达:
δF = (w_q - w)² / [2 · H⁻¹[i,i]]
更新剩余权重:
w' = w - (w_q - w) · H⁻¹[:,i] / H⁻¹[i,i]
其中 H = 2X^TX 是 Hessian 矩阵(由校准数据的激活值计算得到)。
GPTQ 的关键创新:
- 将 OBQ 的逐行量化改为逐列量化,大幅提升速度
- 使用 Cholesky 分解加速 Hessian 逆矩阵计算
- 支持分组量化(group_size=128)兼顾精度和速度
5.2 GPTQ 实战代码
# 安装依赖
pip install auto-gptq transformers accelerate
from transformers import AutoTokenizer
from auto_gptq import AutoGPTQForCausalLM, BaseQuantizeConfig
# 1. 配置量化参数
quantize_config = BaseQuantizeConfig(
bits=4, # 量化位宽
group_size=128, # 分组大小
damp_percent=0.01, # Hessian 矩阵阻尼系数
desc_act=True, # 按激活值重要性排序量化(精度更高)
sym=False, # 非对称量化
)
# 2. 加载模型和分词器
model_id = "meta-llama/Llama-2-7b-hf"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoGPTQForCausalLM.from_pretrained(
model_id, quantize_config
)
# 3. 准备校准数据
calibration_data = [
"The quick brown fox jumps over the lazy dog.",
"Machine learning is transforming industries worldwide.",
# ... 建议 128-1024 条代表性文本
]
calibration_dataset = [
tokenizer(text, return_tensors="pt", max_length=512,
truncation=True, padding=False)
for text in calibration_data
]
# 4. 执行量化
model.quantize(calibration_dataset)
# 5. 保存量化模型
model.save_quantized("./llama2-7b-gptq-4bit")
tokenizer.save_pretrained("./llama2-7b-gptq-4bit")
# 6. 加载并推理
model = AutoGPTQForCausalLM.from_quantized(
"./llama2-7b-gptq-4bit", device="cuda:0"
)
inputs = tokenizer("Hello, how are you?", return_tensors="pt").to("cuda:0")
outputs = model.generate(**inputs, max_new_tokens=50)
print(tokenizer.decode(outputs[0]))
5.3 GPTQ 量化参数调优
desc_act=True vs False:
True:按激活值方差排序,先量化重要性低的列,精度更高,但推理时需要重排序,速度略慢False:按原始列顺序量化,速度更快,精度稍低
group_size 选择:
- 128:推荐默认值
- 64:精度提升约 0.5%,但元数据开销翻倍
- 32:边际收益递减
6. AWQ 量化技术
6.1 AWQ 原理
AWQ(Activation-aware Weight Quantization)的核心洞察是:权重中只有 1% 左右的"显著通道"(salient channels)对模型精度至关重要,这些通道对应激活值中的离群值。
AWQ 不直接混合精度,而是通过逐通道缩放(per-channel scaling)来保护显著通道:
对于权重 W 和激活值 X:
1. 计算每个通道的激活值统计量 s = mean(|X|)
2. 对权重进行缩放:W' = W × diag(s^α),α ∈ [0, 1]
3. 对激活值进行反缩放:X' = X × diag(s^(-α))
4. 量化 W'(缩放后量化误差更均匀)
缩放因子 α 通过网格搜索在校准数据上自动优化。
AWQ 的优势:
- 不需要反向传播或二阶信息
- 比 GPTQ 更快(量化速度快 10×)
- 在小模型上精度与 GPTQ 相当,大模型上通常更优
6.2 AWQ 实战代码
pip install autoawq transformers
from awq import AutoAWQForCausalLM
from transformers import AutoTokenizer
# 1. 加载模型
model_id = "meta-llama/Llama-2-7b-hf"
model = AutoAWQForCausalLM.from_pretrained(model_id)
tokenizer = AutoTokenizer.from_pretrained(model_id)
# 2. 配置量化参数
quant_config = {
"zero_point": True, # 非对称量化
"q_group_size": 128, # 分组大小
"w_bit": 4, # 量化位宽
"version": "GEMM", # GEMM 内核(速度更快)或 GEMV(精度更高)
}
# 3. 准备校准数据
calibration_data = [
"人工智能正在改变世界的方方面面。",
"Quantization reduces model size significantly.",
# ... 128-512 条代表性文本
]
# 4. 执行量化
model.quantize(
tokenizer,
quant_config=quant_config,
calib_data=calibration_data,
)
# 5. 保存
model.save_quantized("./llama2-7b-awq-4bit")
tokenizer.save_pretrained("./llama2-7b-awq-4bit")
# 6. 加载推理
model = AutoAWQForCausalLM.from_quantized(
"./llama2-7b-awq-4bit", fuse_layers=True
)
6.3 AWQ vs GPTQ 对比
| 维度 | GPTQ | AWQ |
|---|---|---|
| 量化速度 | 较慢(需 Hessian 计算) | 快 10× |
| 推理速度 | 快 | 快(GEMM 内核优化) |
| INT4 精度 | 优秀 | 优秀(大模型更优) |
| 校准数据需求 | 128-1024 条 | 128-512 条 |
| 生态支持 | 成熟,HuggingFace 广泛支持 | 快速增长 |
| 硬件兼容 | NVIDIA GPU | NVIDIA GPU |
7. GGUF 格式与 llama.cpp 量化
7.1 GGUF 格式概述
GGUF(GGML Unified Format)是 llama.cpp 项目使用的模型格式,专为 CPU/混合推理 优化。与 GPTQ/AWQ 依赖 CUDA 不同,GGUF 模型可以在纯 CPU、Apple Silicon、甚至手机上运行。
GGUF 的特点:
- 单文件自包含(包含元数据、分词器、权重)
- 支持多种量化方案(Q2_K 到 Q8_0)
- 跨平台:Linux/macOS/Windows/Android
- 支持 mmap 加载,内存映射友好
7.2 GGUF 量化方案详解
GGUF 提供丰富的量化类型,命名规则如下:
Q{位宽}_{变体}
常见类型:
Q4_0 : 4-bit,旧版,每组共享一个 FP16 scale
Q4_1 : 4-bit,每组共享 scale + min
Q4_K_M : 4-bit K-Quant 中等(推荐!精度和速度的最佳平衡)
Q4_K_S : 4-bit K-Quant 小(更激进,体积更小)
Q5_K_M : 5-bit K-Quant 中等(精度更高)
Q5_0 : 5-bit 旧版
Q6_K : 6-bit K-Quant
Q8_0 : 8-bit(最高精度量化)
Q2_K : 2-bit(极致压缩,精度损失大)
IQ4_XS : 4-bit iMatrix(基于重要性矩阵的自适应量化)
K-Quant 是 llama.cpp 的重要改进:对不同层使用不同位宽(重要层用更多位),比统一位宽精度更好。
7.3 GGUF 量化实战
# 1. 克隆 llama.cpp
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
make -j$(nproc)
# 2. 转换 HuggingFace 模型为 GGUF
python3 convert_hf_to_gguf.py \
/path/to/llama2-7b \
--outfile llama2-7b-f16.gguf \
--outtype f16
# 3. 量化为 Q4_K_M
./llama-quantize \
llama2-7b-f16.gguf \
llama2-7b-Q4_K_M.gguf \
Q4_K_M
# 4. 推理测试
./llama-cli \
-m llama2-7b-Q4_K_M.gguf \
-p "Once upon a time" \
-n 128 \
--temp 0.7
# 5. 启动 API 服务
./llama-server \
-m llama2-7b-Q4_K_M.gguf \
--host 0.0.0.0 \
--port 8080 \
-ngl 9999 # GPU offload 层数,9999 表示全部
7.4 GGUF 量化方案性能对比
以 Llama-2-7B 为例(WikiText-2 困惑度):
量化方案 模型大小 困惑度 推理速度(tok/s)
────────────────────────────────────────────────
F16 13.5 GB 5.47 30 (CPU)
Q8_0 7.2 GB 5.48 55 (CPU)
Q6_K 5.5 GB 5.52 70 (CPU)
Q5_K_M 4.8 GB 5.58 80 (CPU)
Q4_K_M 4.1 GB 5.68 90 (CPU)
Q4_0 3.8 GB 5.91 95 (CPU)
Q3_K_M 3.3 GB 6.15 100 (CPU)
Q2_K 2.7 GB 8.11 110 (CPU)
推荐选择:
- Q4_K_M:最佳平衡点,适合大多数场景
- Q5_K_M:对精度敏感的场景
- Q6_K:需要接近原始精度的场景
8. SmoothQuant 平滑量化
8.1 问题背景
Transformer 模型的激活值存在严重的**离群值(outlier)**问题——少数通道的激活值比其他通道大 100× 以上。这导致激活值的量化非常困难,因为量化范围被极端值主导,正常值的有效精度被大幅压缩。
8.2 SmoothQuant 原理
SmoothQuant 的核心思想是将量化难度从激活值"迁移"到权重:
Y = X · W = (X · diag(s)^(-1)) · (diag(s) · W) = X̂ · Ŵ
通过逐通道缩放因子 s,将激活值中的离群值"平滑"到权重上(权重分布通常更均匀,更容易量化)。
import torch
def smooth_activation_weight(activation, weight, alpha=0.5):
"""
SmoothQuant: 平滑激活值和权重
Args:
activation: [batch, seq_len, hidden_dim] 激活值统计量(abs max)
weight: [out_dim, hidden_dim] 权重矩阵
alpha: 平滑系数,0-1 之间。越大,越多难度转移到权重
"""
# 计算每个通道的激活值范围
activation_scale = activation.abs().max(dim=0).values
# 计算每个通道的权重范围
weight_scale = weight.abs().max(dim=0).values
# 计算平滑因子
s = (activation_scale.pow(alpha) / weight_scale.pow(1 - alpha)).clamp(min=1e-5)
# 对激活值除以 s,对权重乘以 s
activation_smoothed = activation / s.unsqueeze(0).unsqueeze(0)
weight_smoothed = weight * s.unsqueeze(0)
return activation_smoothed, weight_smoothed, s
8.3 α 参数选择
- α = 0.5:平衡点,适合大多数模型
- α = 0.75:更多难度转移到权重,适合激活值离群值特别严重的情况
- α = 0.25:更多难度留在激活值,适合权重分布不均的情况
SmoothQuant 通常与 INT8 量化配合使用,可以实现权重和激活值同时 INT8 量化(W8A8),相比只量化权重(W8A16)能获得更大的推理加速。
9. 量化模型性能评估
9.1 评估维度
量化模型的评估需要从多个维度综合考量:
1. 精度评估
- 困惑度(Perplexity):语言模型的标准指标,越低越好
- 下游任务准确率:MMLU、HellaSwag、ARC 等基准测试
- 生成质量:人工评测或 LLM-as-Judge
2. 效率评估
- 推理速度:tokens/s(吞吐量)和首 token 延迟(TTFT)
- 显存占用:峰值 GPU 内存
- 模型体积:磁盘存储大小
3. 稳定性评估
- 长文本生成质量:量化后是否出现重复或退化
- 多轮对话一致性:上下文理解能力是否下降
- 边界情况:特殊字符、多语言输入的表现
9.2 评估代码示例
import torch
import time
from transformers import AutoTokenizer, AutoModelForCausalLM
def evaluate_quantized_model(model_path, eval_prompts, device="cuda:0"):
"""量化模型综合评估"""
tokenizer = AutoTokenizer.from_pretrained(model_path)
model = AutoModelForCausalLM.from_pretrained(
model_path,
torch_dtype=torch.float16,
device_map=device
)
results = {
"total_tokens": 0,
"total_time": 0,
"outputs": []
}
for prompt in eval_prompts:
inputs = tokenizer(prompt, return_tensors="pt").to(device)
# 预热
with torch.no_grad():
_ = model.generate(**inputs, max_new_tokens=10)
# 计时
torch.cuda.synchronize()
start = time.time()
with torch.no_grad():
output = model.generate(
**inputs, max_new_tokens=256,
do_sample=False
)
torch.cuda.synchronize()
elapsed = time.time() - start
new_tokens = output.shape[1] - inputs["input_ids"].shape[1]
results["total_tokens"] += new_tokens
results["total_time"] += elapsed
results["outputs"].append({
"prompt": prompt,
"response": tokenizer.decode(output[0], skip_special_tokens=True),
"tokens": new_tokens,
"time": elapsed,
"tokens_per_sec": new_tokens / elapsed
})
# 汇总统计
avg_tps = results["total_tokens"] / results["total_time"]
peak_mem = torch.cuda.max_memory_allocated(device) / 1024**3
print(f"平均推理速度: {avg_tps:.1f} tokens/s")
print(f"峰值显存: {peak_mem:.2f} GB")
return results
# 使用示例
eval_prompts = [
"Explain quantum computing in simple terms.",
"Write a Python function to sort a list using quicksort.",
"What are the key differences between TCP and UDP?",
]
results = evaluate_quantized_model("./llama2-7b-Q4_K_M", eval_prompts)
9.3 常用基准测试
| 基准 | 测试内容 | 指标 |
|---|---|---|
| WikiText-2 | 语言建模 | 困惑度 |
| MMLU | 多领域知识 | 准确率 |
| HellaSwag | 常识推理 | 准确率 |
| ARC | 科学问答 | 准确率 |
| GSM8K | 数学推理 | 准确率 |
| HumanEval | 代码生成 | Pass@1 |
| MT-Bench | 对话质量 | 1-10 评分 |
10. 量化工具对比与选型
10.1 主流量化工具
| 工具 | 量化方法 | 位宽 | 推理后端 | 适用场景 |
|---|---|---|---|---|
| AutoGPTQ | GPTQ (PTQ) | 2/3/4/8-bit | GPU (CUDA) | 通用 GPU 推理 |
| AutoAWQ | AWQ (PTQ) | 4-bit | GPU (CUDA) | 大规模 GPU 部署 |
| llama.cpp | GGUF 多种 | 2-8 bit | CPU/GPU/混合 | 边缘部署、跨平台 |
| bitsandbytes | NF4/INT8 | 4/8-bit | GPU (CUDA) | 训练时量化(QLoRA) |
| Intel Neural Compressor | 多种 | INT8/INT4 | CPU (Intel) | Intel 硬件优化 |
| ONNX Runtime | 多种 | INT8 | CPU/GPU/NPU | 生产环境多硬件 |
| vLLM | GPTQ/AWQ/FP8 | 4/8-bit | GPU (CUDA) | 高吞吐推理服务 |
| TensorRT-LLM | 多种 | FP8/INT4/INT8 | GPU (NVIDIA) | 极致推理性能 |
10.2 选型决策树
你的部署环境是什么?
├── 消费级 GPU (RTX 3090/4090, 24GB)
│ ├── 需要最高推理速度 → AWQ + vLLM
│ └── 需要最高精度 → GPTQ (desc_act=True)
├── 服务器 GPU (A100/H100, 80GB)
│ ├── 高吞吐服务 → AWQ/TensorRT-LLM
│ └── 多模型部署 → GPTQ/AWQ 4bit
├── Apple Silicon (M1/M2/M3)
│ └── GGUF (Q4_K_M) + llama.cpp / Ollama
├── 纯 CPU 环境
│ └── GGUF (Q4_0/Q4_K_M) + llama.cpp
└── 边缘设备 (手机/嵌入式)
└── GGUF (Q4_0) + llama.cpp (轻量构建)
11. 实战:将 70B 模型量化到 4bit 并部署
11.1 环境准备
# 硬件需求
# - 量化过程:至少 1× A100 80GB(或 2× A6000 48GB)
# - 推理运行:1× RTX 4090 24GB + 64GB 内存(CPU offload)
# - 或纯 CPU + 128GB 内存
# 创建环境
conda create -n quant python=3.10 -y
conda activate quant
# 安装依赖
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121
pip install transformers accelerate autoawq auto-gptq
pip install datasets # 校准数据
11.2 步骤一:准备校准数据
from datasets import load_dataset
# 使用 C4 数据集的子集作为校准数据
dataset = load_dataset("allenai/c4", data_files="en/c4-train.00000-of-01024.json.gz",
split="train", streaming=True)
calibration_texts = []
for i, sample in enumerate(dataset):
if i >= 512:
break
# 截取前 2048 个字符
calibration_texts.append(sample["text"][:2048])
print(f"准备了 {len(calibration_texts)} 条校准数据")
# 保存以复用
with open("calibration_data.txt", "w") as f:
for text in calibration_texts:
f.write(text.replace("\n", " ") + "\n")
11.3 步骤二:使用 AWQ 量化
from awq import AutoAWQForCausalLM
from transformers import AutoTokenizer
import torch
model_id = "meta-llama/Llama-2-70b-hf"
# 加载模型(需要足够显存或使用 device_map="auto" 分布到多卡)
model = AutoAWQForCausalLM.from_pretrained(
model_id,
torch_dtype=torch.float16,
device_map="auto", # 自动分配到多张 GPU
safetensors=True
)
tokenizer = AutoTokenizer.from_pretrained(model_id)
# 量化配置
quant_config = {
"zero_point": True,
"q_group_size": 128,
"w_bit": 4,
"version": "GEMM",
}
# 加载校准数据
with open("calibration_data.txt") as f:
calibration_data = [line.strip() for line in f if line.strip()]
# 执行量化(约需 2-4 小时)
print("开始量化...")
model.quantize(
tokenizer,
quant_config=quant_config,
calib_data=calibration_data[:256], # 256 条足够
)
print("量化完成!")
# 保存
output_dir = "./Llama-2-70b-AWQ-4bit"
model.save_quantized(output_dir)
tokenizer.save_pretrained(output_dir)
# 检查模型大小
import os
total_size = sum(
os.path.getsize(os.path.join(output_dir, f))
for f in os.listdir(output_dir)
)
print(f"量化后模型大小: {total_size / 1024**3:.2f} GB")
# 预期输出约 35-38 GB
11.4 步骤三:使用 vLLM 部署推理服务
from vllm import LLM, SamplingParams
# 加载量化模型
llm = LLM(
model="./Llama-2-70b-AWQ-4bit",
quantization="awq",
dtype="float16",
max_model_len=4096,
gpu_memory_utilization=0.92,
tensor_parallel_size=1, # 单卡 4090 需要 CPU offload
)
# 推理测试
sampling_params = SamplingParams(
temperature=0.7,
top_p=0.9,
max_tokens=512,
)
prompts = [
"Explain the theory of relativity in simple terms.",
"Write a Python function to find the longest palindrome substring.",
"What are the main challenges in building AGI?",
]
outputs = llm.generate(prompts, sampling_params)
for output in outputs:
print(f"Prompt: {output.prompt[:50]}...")
print(f"Response: {output.outputs[0].text[:200]}...")
print(f"Tokens/s: {len(output.outputs[0].token_ids) / output.metrics.finished_time:.1f}")
print("---")
11.5 步骤四:备选方案——GGUF 量化(CPU/混合推理)
# 使用 llama.cpp 将 70B 量化为 GGUF
cd llama.cpp
# 转换为 GGUF(需要约 160GB 内存/磁盘空间)
python3 convert_hf_to_gguf.py \
./Llama-2-70b-AWQ-4bit \
--outfile llama2-70b-f16.gguf \
--outtype f16
# 量化为 Q4_K_M(约需 1-2 小时)
./llama-quantize \
llama2-70b-f16.gguf \
llama2-70b-Q4_K_M.gguf \
Q4_K_M
# 验证文件大小
ls -lh llama2-70b-Q4_K_M.gguf
# 预期约 38-42 GB
# 在 RTX 4090 + 64GB 内存上推理(部分层 offload 到 GPU)
./llama-server \
-m llama2-70b-Q4_K_M.gguf \
--host 0.0.0.0 \
--port 8080 \
-ngl 40 \ # offload 40 层到 GPU(4090 24GB 可容纳)
-c 4096 \ # 上下文长度
--mlock # 锁定内存,防止 swap
11.6 步骤五:Ollama 一键部署
# 最简单的部署方式:使用 Ollama
# 创建 Modelfile
cat > Modelfile << 'EOF'
FROM ./llama2-70b-Q4_K_M.gguf
PARAMETER temperature 0.7
PARAMETER top_p 0.9
PARAMETER num_ctx 4096
TEMPLATE """{{ .System }}
{{ .Prompt }}"""
SYSTEM "You are a helpful AI assistant."
EOF
# 创建并运行
ollama create llama2-70b-custom -f Modelfile
ollama run llama2-70b-custom
11.7 步骤六:性能评估
import requests
import time
import json
def benchmark_api(url, prompt, num_runs=5):
"""评测推理 API 性能"""
latencies = []
token_counts = []
for _ in range(num_runs):
start = time.time()
response = requests.post(url, json={
"model": "llama2-70b",
"prompt": prompt,
"max_tokens": 256,
"temperature": 0.7,
"stream": False
})
elapsed = time.time() - start
data = response.json()
tokens = len(data["choices"][0]["text"].split())
latencies.append(elapsed)
token_counts.append(tokens)
avg_latency = sum(latencies) / len(latencies)
avg_tps = sum(token_counts) / sum(latencies)
print(f"平均延迟: {avg_latency:.2f}s")
print(f"平均吞吐: {avg_tps:.1f} tokens/s")
return avg_latency, avg_tps
# 测试不同并发下的性能
for concurrency in [1, 4, 8]:
print(f"\n并发数: {concurrency}")
benchmark_api(
"http://localhost:8080/v1/completions",
"Explain machine learning to a 10-year-old."
)
12. 总结与展望
量化技术选型速查表
| 场景 | 推荐方案 | 位宽 | 预期效果 |
|---|---|---|---|
| GPU 服务器高吞吐 | AWQ + vLLM | 4-bit | 3-4× 压缩,速度提升 |
| 消费级 GPU 部署 | GPTQ/AWQ | 4-bit | 70B→单卡可加载 |
| Apple Silicon | GGUF + Ollama | Q4_K_M | 原生 Metal 加速 |
| 纯 CPU 部署 | GGUF + llama.cpp | Q4_0 | 无 GPU 依赖 |
| 训练微调 | bitsandbytes QLoRA | NF4 | 4bit 训练,FP16 效果 |
| 极致精度 | FP8 (H100) | 8-bit | 几乎无损 |
| 边缘设备 | GGUF | Q4_0/Q3_K | 手机可运行 |
未来趋势
- FP8 原生支持:H100/H200 原生 FP8,量化精度损失趋近于零
- 1-bit LLM(BitNet):微软 BitNet 证明 1.58-bit 训练可行,模型压缩 10×+
- 混合精度自动搜索:自动为每层选择最优位宽
- 硬件协同设计:量化格式与芯片架构深度绑定
- 量化 + 蒸馏 + 剪枝:多种压缩技术联合使用
参考资源
- GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers (Frantar et al., 2022)
- AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration (Lin et al., 2023)
- SmoothQuant: Accurate and Efficient Post-Training Quantization for Large Language Models (Xiao et al., 2022)
- llama.cpp: https://github.com/ggerganov/llama.cpp
- AutoGPTQ: https://github.com/AutoGPTQ/AutoGPTQ
- AutoAWQ: https://github.com/casper-hansen/AutoAWQ