PEFT documentation

量化

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v0.21.0).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

量化

量化使用更少的位来表示数据,是一种减少内存占用并加速推理的实用技术,尤其适用于大型语言模型(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 有两种预期方式:

  1. 加载未量化的基础模型,应用 LoftQ 并设置目标量化级别(例如,对于 nf4 使用 bits=4),然后保存生成的适配器。接着,使用例如 BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4") 对基础模型进行量化,并在其上加载经过 LoftQ 初始化的适配器
  2. 加载量化后的基础模型,使用 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() 上下文管理器。

后续步骤

如果您对量化有更多兴趣,以下内容可能对您有所帮助:

Update on GitHub