实战Transformers模型量化Facebook OPT

基于上篇文章的理论知识,本文主要讲述了实战Transformers模型量化:介绍Facebook OPT模型的量化过程和相关技术。

Transformers 模型量化技术:GPTQ

Frantar等人发表了论文 GPTQ:Accurate Post-Training Quantization for Generative Pre-trained Transformers。作者详细介绍了一种训练后量化算法,适用于所有通用的预训练 Transformer模型,同时只有微小的性能下降。GPTQ算法需要通过对量化模型进行推理来校准模型的量化权重。其呈现的效果可以表达成Hugging Face + AutoGPTQ。

使用 GPTQ 量化模型

为了使用 auto-gptq 库量化一个模型,需要向量化器传递一个数据集。通常有两种方式构造数据集:

  • 量化器支持的默认数据集(包括[‘wikitext2’,‘c4’,‘c4-new’,‘ptb’,‘ptb-new’])
  • 一个字符串列表(这些字符串将被用作数据集)
    先安装好项目所依赖的包,如requirements。
torch>=2.1.2==2.3.0.dev20240116+cu121
transformers==4.37.2
ffmpeg==1.4
ffmpeg-python==0.2.0
timm==0.9.12
datasets==2.16.1
evaluate==0.4.1
scikit-learn==1.3.2
pandas==2.1.1
peft==0.7.1
accelerate==0.26.1
autoawq==0.2.2
optimum==1.17.0
auto-gptq==0.6.0
bitsandbytes>0.39.0==0.41.3.post2
jiwer==3.0.3
soundfile>=0.12.1==0.12.1
librosa==0.10.1
gradio==4.13.0
trl

然后进行加载requirements.txt内所有依赖包。

pip install -r requirements.txt

运行出来的界面如下图所示:
安装依赖包运行界面图

使用 GPTQ 算法支持的默认数据集来量化

对此,现使用"wikitext2"数据集将模型量化为4位精度,且支持的精度有[2, 4, 6, 8]。

from transformers import AutoModelForCausalLM, AutoTokenizer, GPTQConfig
import torch

model_id = "facebook/opt-2.7b"

quantization_config = GPTQConfig(
     bits=4, # 量化精度
     group_size=128,
     dataset="c4",
     desc_act=False,
)

运行界面图

tokenizer =AutoTokenizer.from_pretrained(model_id)
quant_model = AutoModelForCausalLM.from_pretrained(model_id, quantization_config=quantization_config, device_map='auto') 

运行参数界面
关于device_map参数,大家可以浏览huggingface的官方原文解释。
地址:https://huggingface.co/docs/transformers/v4.36.1/en/quantization
huggingface界面
上图为huggingface官方解释界面。

实测GPU显存占用:量化模型(峰值超过7GB)

运行图
由于电脑配置较低导致没有运行出来,笔者暂截取Github上的示例图。

检查量化模型正确性

通过检查线性层的属性来确保模型已正确量化,它们应该包含qweight和qzeros属性,这些属性应该是torch.int32数据类型。更简单地说,GPTQ简单的方法能够检查量化模型是否正确,是否有错误等,但该方法无法衡量模型的参数值被量化得非常好。即能够运行出来的模型到底有没有达到比较好的指标是无法被检查出来的。

quant_model.model.decoder.layers[0].self_attn.q_proj.__dict__

运行界面图
运行的界面图

使用GPU加载模型并生成文本

tokenizer = AutoTokenizer.from_pretrained(model_name_or_path)

text = "Merry Christmas! I'm glad to"
inputs = tokenizer(text, return_tensors="pt").to(0)

out = quant_model.generate(**inputs, max_new_tokens=64)
print(tokenizer.decode(out[0], skip_special_tokens=True))

运行界面图

使用自定义数据集量化模型

下面演示通过传递自定义数据集来量化一个模型。通过字符串列表来自定义一个数据集,建议样本数不少于128(样本数太少会影响模型性能)

from transformers import AutoModelForCausalLM, GPTQConfig, AutoTokenizer

model_name_or_path = "facebook/opt-2.7b"
custom_dataset = ["auto-gptq is an easy-to-use model quantization library with user-friendly apis, based on GPTQ algorithm."]

custom_quantization_config = GPTQConfig(
    bits=4,
    group_size=128,
    desc_act=False,
    dataset=custom_dataset
)

custom_quant_model = AutoModelForCausalLM.from_pretrained(model_name_or_path,
                                                          quantization_config=custom_quantization_config,
                                                          torch_dtype=torch.float16,
                                                          device_map="auto")

运行界面图

相比使用默认数据集,未经精心准备的自定义数据集会明显降低模型性能。

text = "Merry Christmas! I'm glad to"
inputs = tokenizer(text, return_tensors="pt").to(0)

out = custom_quant_model.generate(**inputs, max_new_tokens=64)
print(tokenizer.decode(out[0], skip_special_tokens=True))

示例图

Transformers 模型量化技术:AWQ

在AWQ:Activation-aware Weight Quantization for LLM Compression and Acceleration论文中,作者介绍了一种激活感知权重量化算法,可以用于压缩任何基于 Transformer 的语言模型,同时只有微小的性能下降。AWQ可以看成transformers + quantization综合表达的效果。

量化前模型测试文本生成任务

from transformers import pipeline

model_path = "facebook/opt-125m"

# 使用 GPU 加载原始的 OPT-125m 模型
generator = pipeline('text-generation',
                     model=model_path,
                     device=0,
                     do_sample=True,
                     num_return_sequences=3)

运行界面图

实测GPU显存占用:加载OPT-125m模型后

界面图

示例对比

generator("The woman worked as a")

运行结果
示例1 运行结果

generator("The man worked as a")

运行结果
示例2 运行结果

使用 AutoAWQ 量化模型

下面我们以 facebook opt-125m 模型为例,使用 AutoAWQ 库实现的 AWQ 算法实现模型量化。

from awq import AutoAWQForCausalLM
from transformers import AutoTokenizer


quant_path = "models/opt-125m-awq"
quant_config = {"zero_point": True, "q_group_size": 128, "w_bit": 4, "version": "GEMM"}

# 加载模型
model = AutoAWQForCausalLM.from_pretrained(model_path, device_map="cuda")
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)

图

# 量化模型
model.quantize(tokenizer, quant_config=quant_config)

实测GPU显存使用:量化模型时峰值达到将近 4GB。

quant_config

界面

Transformers兼容性配置

from transformers import AwqConfig, AutoConfig

# 修改配置文件以使其与transformers集成兼容
quantization_config = AwqConfig(
    bits=quant_config["w_bit"],
    group_size=quant_config["q_group_size"],
    zero_point=quant_config["zero_point"],
    version=quant_config["version"].lower(),
).to_dict()

# 预训练的transformers模型存储在model属性中,需要传递一个字典
model.model.config.quantization_config = quantization_config
# 保存模型权重

model.save_quantized(quant_path)
tokenizer.save_pretrained(quant_path)  # 保存分词器

界面图

Transformers 量化技术 BitsAndBytes

BitsAndBytes
bitsandbytes是将模型量化为8位和4位的最简单选择。

  • 8位量化将fp16中的异常值与int8中的非异常值相乘,将非异常值转换回fp16,然后将它们相加以返回fp16中的权重。这减少了异常值对模型性能产生的降级效果。
  • 4位量化进一步压缩了模型,并且通常与QLoRA一起用于微调量化LLM(低精度语言模型)。

异常值: 是指大于某个阈值的隐藏状态值,这些值是以fp16进行计算的。虽然这些值通常服从正态分布([-3.5, 3.5]),但对于大型模型来说,该分布可能会有很大差异([-60, 6]或[6, 60])。8位量化适用于约为5左右的数值,但超过此范围后将导致显著性能损失。一个好的默认阈值是6,但对于不稳定的模型(小型模型或微调)可能需要更低的阈值。

在 Transformers 中使用参数量化

使用 Transformers 库的 model.from_pretrained()方法中的load_in_8bit或load_in_4bit参数,便可以对模型进行量化。只要模型支持使用Accelerate加载并包含torch.nn.Linear层,这几乎适用于任何模态的任何模型。

from transformers import AutoModelForCausalLM
model_id = "facebook/opt-2.7b"
model_4bit = AutoModelForCausalLM.from_pretrained(model_id,
                                                  device_map="auto",
                                                  load_in_4bit=True)
model_4bit

示例图

# 获取当前模型占用的 GPU显存(差值为预留给 PyTorch 的显存)
memory_footprint_bytes = model_4bit.get_memory_footprint()
memory_footprint_mib = memory_footprint_bytes / (1024 ** 2)  # 转换为 MiB

print(f"{memory_footprint_mib:.2f}MiB")

运行结果为:1457.52MiB,
而在显存中占用的初始结果为1774MiB。

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained(model_id
                                         )
text = "Merry Christmas! I'm glad to"
inputs = tokenizer(text, return_tensors="pt").to(0)

out = model_4bit.generate(**inputs, max_new_tokens=64)
print(tokenizer.decode(out[0], skip_special_tokens=True))

运行界面图

使用 NF4 精度加载模型


from transformers import BitsAndBytesConfig

nf4_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
)

model_nf4 = AutoModelForCausalLM.from_pretrained(model_id, quantization_config=nf4_config)
# 获取当前模型占用的 GPU显存(差值为预留给 PyTorch 的显存)
memory_footprint_bytes = model_nf4.get_memory_footprint()
memory_footprint_mib = memory_footprint_bytes / (1024 ** 2)  # 转换为 MiB

print(f"{memory_footprint_mib:.2f}MiB")

运行结果为:1457.52MiB
而在显存中占用的初始结果为3414MiB。

使用双量化加载模型

double_quant_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_use_double_quant=True,
)

model_double_quant = AutoModelForCausalLM.from_pretrained(model_id, quantization_config=double_quant_config)
# 获取当前模型占用的 GPU显存(差值为预留给 PyTorch 的显存)
memory_footprint_bytes = model_double_quant.get_memory_footprint()
memory_footprint_mib = memory_footprint_bytes / (1024 ** 2)  # 转换为 MiB

print(f"{memory_footprint_mib:.2f}MiB")

运行结果为:1457.52MiB,
而在显存中占用的初始结果为:4910MiB

使用 QLoRA 所有量化技术加载模型

import torch

qlora_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_use_double_quant=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16
)

model_qlora = AutoModelForCausalLM.from_pretrained(model_id, quantization_config=qlora_config)
# 获取当前模型占用的 GPU显存(差值为预留给 PyTorch 的显存)
memory_footprint_bytes = model_qlora.get_memory_footprint()
memory_footprint_mib = memory_footprint_bytes / (1024 ** 2)  # 转换为 MiB

print(f"{memory_footprint_mib:.2f}MiB")

运行结果为:1457.52MiB。

<think>嗯,用户之前问了关于模型剪枝后再微调时被移除参数的问题,现在又转向了AWQ量化。我需要先理解AWQ是什么,以及用户可能的需求。AWQ全称是Activation-aware Weight Quantization,是一种量化方法,主要用于在保持模型性能的同时减少模型大小和计算开销。用户可能是做模型优化的,或者对模型部署感兴趣,尤其是在资源受限的环境中,比如移动设备或嵌入式系统。 用户之前的问题涉及剪枝后的参数恢复,现在转向量化,可能是在探索不同的模型压缩技术。需要明确AWQ与其他量化方法(如INT8、二值化)的区别,以及它的优势。另外,用户可能想知道AWQ的具体实现原理,应用场景,以及实际使用中的注意事项,比如是否需要微调,对准确率的影响等。 在回答时,需要结构化,先介绍AWQ的基本概念,然后核心思想,实现步骤,优缺点,应用场景,最后可能给出代码示例或使用建议。同时,用户之前的问题有系统级指令,要求数学公式用$...$,独立公式用$$...$$,所以需要注意格式正确,用中文回答,结构清晰,逐步引导用户理解。 另外,用户可能对量化技术不太熟悉,所以需要避免过于专业的术语,或者适当解释术语。例如,解释量化是什么,激活值的重要性,以及为什么AWQ在激活值的指导下能更好地保留关键权重。同时,比较与其他方法的区别,比如传统量化可能均匀量化所有权重,而AWQ根据激活值的分布调整量化间隔,更智能。 还要注意用户可能的深层需求,比如如何在实践中应用AWQ,是否有现成的工具支持,例如AutoGPTQ库的使用,或者是否需要自行实现。给出实际代码示例会帮助用户理解如何操作。另外,可能存在的挑战,如如何选择group size,如何处理不同层的敏感性差异,是否需要微调步骤,这些都需要在回答中涉及。 总结,回答需要覆盖:AWQ的定义、核心思想、步骤、优缺点、应用场景、实现示例、注意事项。同时保持结构清晰,逐步深入,确保用户能跟随思路,解决问题。</think>好的!**AWQ量化(Activation-aware Weight Quantization)** 是一种针对大语言模型(LLM)的高效量化方法,其核心思想是通过分析激活值的分布动态调整权重量化策略,从而在低比特(如4-bit)下最大限度保留模型性能。以下是逐步解析: --- ### **1. 量化是什么?** 量化是将模型权重从高精度(如FP32)压缩到低精度(如INT4)的过程,目的是: - **减少内存占用**(例如:FP32 → INT4,模型大小缩小8倍) - **加速推理**(低精度计算更快,硬件更易优化) **传统量化痛点**:直接对权重均匀量化会严重损害模型性能,尤其是对关键权重(例如:大值权重)的误差敏感。 --- ### **2. AWQ的核心思想** AWQ通过**分析激活值的分布**,识别权重的重要性差异,动态调整量化策略: - **关键权重保留更高精度**:对激活值较大的权重分配更精细的量化间隔。 - **非关键权重允许更低精度**:对激活值较小的权重使用更粗粒度量化。 #### **数学表达** 若权重为 $W$,激活值为 $X$,则量化后的权重 $\tilde{W}$ 可表示为: $$ \tilde{W} = \text{Quantize}(W \cdot s), \quad s = f(X) $$ 其中 $s$ 是基于激活值 $X$ 动态生成的缩放因子。 --- ### **3. AWQ的实现步骤** #### **Step 1: 分析激活值分布** - 在少量校准数据上运行模型,收集各层激活值的统计特性(如均值、方差、极值)。 - **目标**:识别哪些权重对激活值的贡献更大。 #### **Step 2: 动态缩放权重** - 根据激活值分布生成**每组的缩放因子**(例如:以通道或块为单位分组)。 - **关键权重**:放大其数值,避免量化时丢失信息。 - **非关键权重**:缩小其数值,允许更大量化误差。 #### **Step 3: 低比特量化** - 对缩放后的权重进行分组量化(如4-bit),并存储缩放因子和零点(zero-point)。 - 常用格式:INT4 + 分组缩放因子(如每128个权重共享一个缩放因子)。 #### **Step 4: 推理时反量化** - 在计算时动态将低比特权重还原为高精度: $$ W_{\text{dequant}} = \tilde{W} \cdot s^{-1} $$ --- ### **4. 为什么AWQ优于传统量化?** | 方法 | 量化策略 | 关键权重处理 | 性能保留 | |---------------|----------------|--------------|----------| | 均匀量化 | 所有权重同等精度 | 误差敏感 | 差 | | 逐层缩放量化 | 单层统一缩放 | 部分优化 | 中 | | **AWQ** | **按激活值动态分组** | **精准保护** | **优** | **优势**: - **无需重训练**:仅需校准数据,适合黑盒模型- **硬件友好**:分组量化兼容高效计算(如GPU Tensor Core)。 - **通用性强**:适用于多种模型架构(如LLaMA、GPT、ViT)。 --- ### **5. 实际应用与代码示例** #### **使用AutoGPTQ库实现AWQ** ```python from transformers import AutoModelForCausalLM, AutoTokenizer from auto_gptq import AutoGPTQForCausalLM, BaseQuantizeConfig # 加载原始模型 model_name = "facebook/opt-125m" tokenizer = AutoTokenizer.from_pretrained(model_name) model = AutoModelForCausalLM.from_pretrained(model_name) # 配置AWQ量化参数 quantize_config = BaseQuantizeConfig( bits=4, # 4-bit量化 group_size=128, # 每128个权重共享一个缩放因子 desc_act=True, # 基于激活值动态调整 ) # 量化并保存 quant_model = AutoGPTQForCausalLM.from_pretrained( model_name, quantize_config=quantize_config, calibration_data=calibration_dataset # 提供校准数据 ) quant_model.save_quantized("./opt-125m-awq-4bit") ``` #### **关键参数** - `bits`:量化位数(通常4-bit)。 - `group_size`:缩放因子分组大小(越小精度越高,计算开销越大)。 - `desc_act`:是否启用激活值感知的动态缩放。 --- ### **6. 注意事项** 1. **校准数据选择**:需与任务相关(例如:通用文本选择多样化句子)。 2. **组大小权衡**:`group_size=128` 平衡精度与速度,可尝试64或256。 3. **硬件兼容性**:确保推理框架支持低比特计算(如TensorRT-LLM、vLLM)。 4. **性能评估**:量化后需验证任务指标(如困惑度、准确率)。 --- ### **总结** - **AWQ量化本质**:通过激活值动态识别关键权重,针对性保护其量化精度。 - **适用场景**:大模型边缘部署、低内存设备推理、实时应用。 - **效果**:在4-bit量化下,LLM的困惑度(PPL)损失可控制在1%以内。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Whitney_mao

您的认可和鼓励是持续分享的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值