PEFT documentation
量化
量化
量化使用更少的位来表示数据,是一种减少内存占用并加速推理的实用技术,尤其适用于大型语言模型(LLM)。量化模型有几种方法,包括:
- 使用 AWQ 算法优化哪些模型权重需要被量化
- 使用 GPTQ 算法对权重矩阵的每一行进行独立量化
- 使用 bitsandbytes 库将模型量化为 8 位和 4 位精度
- 使用 AQLM 算法将量化精度低至 2 位
然而,模型量化后通常不会针对下游任务进行进一步训练,因为权重和激活值的较低精度可能导致训练不稳定。但由于 PEFT 方法仅添加额外的可训练参数,因此您可以在量化模型之上使用 PEFT 适配器进行训练!将量化与 PEFT 结合使用,是即使在单块 GPU 上训练最大规模模型的一个良好策略。例如,QLoRA 是一种将模型量化至 4-bit 然后使用 LoRA 进行训练的方法。该方法允许您在单块 48GB 的 GPU 上微调一个 65B 参数的模型!
在本指南中,您将了解如何将模型量化为 4 位,并使用 LoRA 对其进行训练。
量化模型
bitsandbytes 是一个量化库,支持与 Transformers 集成。通过该集成,你可以将模型量化为 8 位或 4 位,并通过配置 BitsAndBytesConfig 类启用许多其他选项。例如,你可以:
- 在加载模型时,设置
load_in_4bit=True可将模型量化为 4 位 - 将
bnb_4bit_quant_type="nf4"设置为使用一种特殊的 4 位数据类型,用于从正态分布初始化的权重 - 设置
bnb_4bit_use_double_quant=True以使用嵌套量化方案对已量化的权重进行量化 - 将
bnb_4bit_compute_dtype=torch.bfloat16设置为 bfloat16 以进行更快的计算
import torch
from transformers import BitsAndBytesConfig
config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_use_double_quant=True,
bnb_4bit_compute_dtype=torch.bfloat16,
)将 config 传递给 from_pretrained 方法。
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained("mistralai/Mistral-7B-v0.1", quantization_config=config)接下来,您应该调用 prepare_model_for_kbit_training() 函数,对量化后的模型进行训练前的预处理。
from peft import prepare_model_for_kbit_training
model = prepare_model_for_kbit_training(model)现在量化模型已准备就绪,让我们设置一个配置。
LoraConfig
使用以下参数(或自行选择)创建一个 LoraConfig:
from peft import LoraConfig
config = LoraConfig(
r=16,
lora_alpha=8,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)然后使用 get_peft_model() 函数,从量化后的模型和配置创建一个 PeftModel。
from peft import get_peft_model
model = get_peft_model(model, config)您已准备好使用您偏好的任何训练方法开始训练!
LoftQ 初始化
LoftQ 通过初始化 LoRA 权重来最小化量化误差,从而在训练量化模型时提升性能。要开始使用,请遵循这些说明。
LoftQ 仅应用于线性层,包括通过 target_parameters 指定的层。不支持嵌入层和卷积层,这些层将使用默认的 LoRA 初始化。nn.MultiheadAttention 层不受支持,并会引发错误。
通常,为了让 LoftQ 发挥最佳效果,建议尽可能多地使用 LoRA 来针对更多层,因为未被针对的层无法应用 LoftQ。这意味着传入 LoraConfig(..., target_modules="all-linear") 很可能能获得最佳结果。此外,在使用 4bit 量化时,应在量化配置中使用 nf4 作为量化类型,即 BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4")。
通常,为了让 LoftQ 发挥最佳效果,建议尽可能多地使用 LoRA 来针对更多层,因为未被针对的层无法应用 LoftQ。这意味着传入 LoraConfig(..., target_modules="all-linear") 很可能能获得最佳结果。此外,在使用 4bit 量化时,应在量化配置中使用 nf4 作为量化类型,即 BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4")。
目前,应用 LoftQ 有两种预期方式:
- 加载未量化的基础模型,应用 LoftQ 并设置目标量化级别(例如,对于 nf4 使用
bits=4),然后保存生成的适配器。接着,使用例如BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4")对基础模型进行量化,并在其上加载经过 LoftQ 初始化的适配器 - 加载量化后的基础模型,使用 LoRA(不使用 loftq)初始化一个 PEFT 模型,并使用
replace_lora_weights_loftq通过从权重文件中流式传输非量化基础模型的参考权重来应用 LoftQ 初始化(将在下一节中进一步说明)
使用 replace_lora_weights_loftq 进行即时 LoftQ 应用
一种更简单但功能更有限的应用 LoftQ 初始化的方法是使用便捷函数 replace_lora_weights_loftq。该函数以量化后的 PEFT 模型作为输入,并将 LoRA 权重就地替换为经过 LoftQ 初始化的对应权重。
from peft import replace_lora_weights_loftq
from transformers import BitsAndBytesConfig
bnb_config = BitsAndBytesConfig(load_in_4bit=True, ...)
base_model = AutoModelForCausalLM.from_pretrained(..., quantization_config=bnb_config)
# note: don't pass init_lora_weights="loftq" or loftq_config!
lora_config = LoraConfig(task_type="CAUSAL_LM")
peft_model = get_peft_model(base_model, lora_config)
replace_lora_weights_loftq(peft_model)replace_lora_weights_loftq 还允许你传入一个 callback 参数,以便更精细地控制哪些层需要修改、哪些不需要,这在实践中往往能显著提升效果。要查看更详细的示例,请参阅 此 notebook。
replace_lora_weights_loftq 仅实现了 LoftQ 的一个迭代步骤。这意味着只更新 LoRA 权重,而不是迭代地更新 LoRA 权重和量化后的基础模型权重。这可能会导致性能降低,但优点在于我们可以直接使用从基础模型派生的原始量化权重,而无需额外保留一份修改后的量化权重副本。这种权衡是否值得取决于具体的使用场景。
目前,replace_lora_weights_loftq 还存在以下额外限制:
- 模型文件必须存储为
safetensors文件。 - 仅支持 bitsandbytes 4 位量化。
QLoRA 风格的训练
QLoRA 会在 Transformer 架构的所有线性层中添加可训练权重。由于这些线性层的属性名称在不同架构中可能有所不同,请将 target_modules 设置为 "all-linear",以便为所有线性层添加 LoRA:
config = LoraConfig(target_modules="all-linear", ...)GPTQ 量化
您可以在 GPT-QModel 和 Transformers 的 GPTQ 文档中了解更多关于基于 GPTQ 的 [2, 3, 4, 8] 位量化的信息。PEFT 通过 GPT-QModel 支持 GPTQ 后训练。
# GPT-QModel install
pip install "gptqmodel>=7.0.0"from transformers import AutoModelForCausalLM, AutoTokenizer, GPTQConfig
model_id = "facebook/opt-125m"
tokenizer = AutoTokenizer.from_pretrained(model_id)
gptq_config = GPTQConfig(bits=4, group_size=128, dataset="wikitext2", tokenizer=tokenizer)
quantized_model = AutoModelForCausalLM.from_pretrained(model_id, device_map="auto", quantization_config=gptq_config)
# save quantized model
quantized_model.save_pretrained("./opt-125m-gptq")
tokenizer.save_pretrained("./opt-125m-gptq")量化完成后,您可以使用 PEFT API 对 GPTQ 模型进行后训练。
AQLM 量化
语言模型的加性量化(AQLM)是一种大语言模型压缩方法。它同时对多个权重进行量化,并利用权重之间的相互依赖性。AQLM 将 8 到 16 个权重的组表示为多个向量码之和。这使得它能够将模型压缩至低至 2-bit,且精度损失相当低。
由于 AQLM 量化过程计算开销较大,建议使用预量化模型。部分可用模型的列表可在官方 aqlm 仓库中找到。
这些模型支持 LoRA 适配器调优。若要调优量化模型,您需要安装 aqlm 推理库:pip install aqlm>=1.0.2。微调后的 LoRA 适配器应单独保存,因为无法将其与 AQLM 量化权重合并。
quantized_model = AutoModelForCausalLM.from_pretrained(
"BlackSamorez/Mixtral-8x7b-AQLM-2Bit-1x16-hf-test-dispatch",
dtype="auto", device_map="auto", low_cpu_mem_usage=True,
)
peft_config = LoraConfig(...)
quantized_model = get_peft_model(quantized_model, peft_config)您可以参考 Google Colab 示例,以了解 AQLM+LoRA 微调的概述。
EETQ 量化
您还可以对 EETQ 量化模型执行 LoRA 微调。EETQ 包提供了一种简单且高效的方式来执行 8 位量化,据称其速度比 LLM.int8() 算法更快。首先,请确保您拥有与 EETQ 兼容的 transformers 版本(例如,通过从最新的 pypi 或从源代码安装)。
import torch
from transformers import EetqConfig
config = EetqConfig("int8")将 config 传递给 from_pretrained 方法。
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained("mistralai/Mistral-7B-v0.1", quantization_config=config)并创建一个 LoraConfig,然后将其传递给 get_peft_model:
from peft import LoraConfig, get_peft_model
config = LoraConfig(
r=16,
lora_alpha=8,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)
model = get_peft_model(model, config)HQQ 量化
使用大型机器学习模型半二次量化(HQQ)进行量化的模型支持 LoRA 适配器调优。若要调优量化后的模型,您需要使用 pip install hqq 安装 hqq 库。
from hqq.engine.hf import HQQModelForCausalLM
device = torch.accelerator.current_accelerator().type if hasattr(torch, "accelerator") else "cuda"
quantized_model = HQQModelForCausalLM.from_quantized(save_dir_or_hfhub, device=device)
peft_config = LoraConfig(...)
quantized_model = get_peft_model(quantized_model, peft_config)或者使用与 HQQ 兼容的 transformers 版本(例如,通过从最新的 pypi 或从源代码安装)。
from transformers import HqqConfig, AutoModelForCausalLM
quant_config = HqqConfig(nbits=4, group_size=64)
quantized_model = AutoModelForCausalLM.from_pretrained(save_dir_or_hfhub, device_map=device_map, quantization_config=quant_config)
peft_config = LoraConfig(...)
quantized_model = get_peft_model(quantized_model, peft_config)torchao(PyTorch 架构优化)
PEFT 支持使用 torchao(“ao”)进行 int8 量化的模型。
from peft import LoraConfig, get_peft_model
from transformers import AutoModelForCausalLM, TorchAoConfig
from torchao.quantization import Int8WeightOnlyConfig
model_id = ...
quantization_config = TorchAoConfig(quant_type=Int8WeightOnlyConfig())
base_model = AutoModelForCausalLM.from_pretrained(model_id, quantization_config=quantization_config)
peft_config = LoraConfig(...)
model = get_peft_model(base_model, peft_config)注意事项:
- 请使用最新版本的 torchao(>= v0.4.0)和 transformers(> 4.42)。
- 目前仅支持线性层。
quant_type = "int4_weight_only"目前不受支持。NF4目前尚未在 Transformers 中实现,因此也不受支持。- 目前,DoRA 仅支持
quant_type = "int8_weight_only"。 - 在使用 LoRA 时,torchao 获得了显式支持。然而,当 torchao 对某一层进行量化时,其类不会改变,只有底层张量的类型会发生变化。因此,除 LoRA 之外的其他 PEFT 方法通常也能与 torchao 配合使用,即使它们并未被显式支持。但请注意,合并操作仅在 LoRA 且
quant_type = "int8_weight_only"时才能正确工作。如果您使用其他 PEFT 方法或数据类型,合并操作很可能会引发错误;即使没有报错,结果也仍然是不正确的。
INC 量化
Intel Neural Compressor (INC) 支持针对多种设备进行模型量化,包括 Intel Gaudi 加速器(也称为 HPU 设备)。您可以对使用 INC 量化的模型执行 LoRA 微调。若要在 PyTorch 模型中使用 INC,请通过以下命令安装该库:pip install neural-compressor[pt]。要将模型量化为 HPU 设备的 FP8 精度,可以使用以下单步量化工作流:
import torch
from neural_compressor.torch.quantization import FP8Config, convert, finalize_calibration, prepare
quant_configs = {
...
}
config = FP8Config(**quant_configs)将 config 传递给 prepare 方法,运行推理以收集校准统计数据,然后调用 finalize_calibration 和 convert 方法将模型量化为 FP8 精度:
model = prepare(model, config)
# Run inference to collect calibration statistics
...
# Finalize calibration and convert the model to FP8 precision
finalize_calibration(model)
model = convert(model)
# Load PEFT LoRA adapter as usual
...以下示例演示了如何在 HPU 设备上将 PEFT LoRA 适配器加载到 INC 量化的 FLUX 文本生成图像模型中,示例代码见此处。
注意事项:
merge()和unmerge()方法目前不支持 INC 量化模型。- 目前,在加载 PEFT 适配器时,仅支持 Linear INC 量化层。
其他支持的 PEFT 方法
除了 LoRA,以下 PEFT 方法也支持量化:
- VeRA(支持 bitsandbytes 量化)
- AdaLoRA(同时支持 bitsandbytes 和 GPTQ 量化)
- (IA)³(支持 bitsandbytes 量化)
Transformer Engine (TE) LoRA
PEFT 支持在 NVIDIA Transformer Engine 层(te.pytorch.Linear、te.pytorch.LayerNormLinear 和 te.pytorch.LayerNormMLP)之上使用 LoRA 适配器。TE 层利用 FP8 和融合内核来加速 Transformer 训练,而附加 LoRA 适配器则允许您对 TE 加速的模型进行参数高效微调。
from peft import LoraConfig, get_peft_model
config = LoraConfig(
r=8,
lora_alpha=16,
target_modules=["q_proj", "v_proj"],
task_type="TOKEN_CLS",
)
model = get_peft_model(te_model, config)安装 Transformer Engine 后,PEFT 会自动将匹配的层分发到 TeLinear 适配器——无需额外配置。
注意事项
merge()和unmerge()目前尚不支持 TE 层。- DoRA 不支持 TE 层。
完整的 ESM2 令牌分类示例位于 examples/lora_finetuning_transformer_engine/ 目录下。
将适配器合并到量化权重中
对于任何支持该功能的量化方法,merge_adapter() 会对基础权重进行反量化,加上适配器权重,然后再次量化结果。unmerge_adapter() 执行相反的操作,因此它不会恢复原始的量化权重,并且反复的合并与取消合并循环可能会累积舍入误差。要恢复原始权重,请重新加载基础模型。要在不合并的情况下在基础模型和适配器之间切换,请使用 disable_adapter() 上下文管理器。
后续步骤
如果您对量化有更多兴趣,以下内容可能对您有所帮助:
- 了解更多关于 QLoRA 的详细信息,并查看 Making LLMs even more accessible with bitsandbytes, 4-bit quantization and QLoRA 博客文章中关于其影响的基准测试。
- 有关不同量化方案的更多信息,请参阅 Transformers 的 Quantization 指南。