AI模型量化压缩技术教程

AI 模型量化压缩技术教程

SEO 元信息

  • 名称:AI模型量化压缩技术教程
  • 描述:零基础AI模型量化压缩技术教程,涵盖量化基础原理、PTQ/QAT、GPTQ/AWQ/GGUF量化实战、性能评估、大模型4bit量化部署等核心技能,适合AI工程师系统学习。
  • 关键词:模型量化, GPTQ, AWQ, GGUF, 大模型压缩
  • 长尾关键词:大模型量化压缩教程, GPTQ量化实战教程, AWQ模型量化入门, GGUF格式量化部署教程

目录

  1. 为什么需要模型量化
  2. 量化基础概念与数据类型
  3. 量化原理与数学基础
  4. 训练后量化 PTQ vs 量化感知训练 QAT
  5. GPTQ 量化详解与实战
  6. AWQ 量化技术
  7. GGUF 格式与 llama.cpp 量化
  8. SmoothQuant 平滑量化
  9. 量化模型性能评估
  10. 量化工具对比与选型
  11. 实战:将 70B 模型量化到 4bit 并部署
  12. 总结与展望

1. 为什么需要模型量化

随着大语言模型(LLM)参数量从数十亿飙升到数千亿,模型的存储和推理成本成为落地应用的核心瓶颈。以 Llama-2-70B 为例,原始 FP16 权重约占 140GB 显存,单张消费级显卡根本无法加载,更不用说实时推理。

模型量化(Quantization)通过降低权重和激活值的数值精度来压缩模型体积、加速推理、减少显存占用,是当前最主流的大模型压缩手段。

量化的核心收益

指标 FP16 原始模型 INT4 量化模型 改善幅度
模型体积 140 GB 35 GB
推理显存 160 GB 40 GB
推理速度 基准 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 手机可运行

未来趋势

  1. FP8 原生支持:H100/H200 原生 FP8,量化精度损失趋近于零
  2. 1-bit LLM(BitNet):微软 BitNet 证明 1.58-bit 训练可行,模型压缩 10×+
  3. 混合精度自动搜索:自动为每层选择最优位宽
  4. 硬件协同设计:量化格式与芯片架构深度绑定
  5. 量化 + 蒸馏 + 剪枝:多种压缩技术联合使用

参考资源

内容声明

本文内容为AI技术学习教程,仅供学习参考。如涉及技术问题,欢迎通过 xurj005@163.com 与我们交流。

目录