LLM 课程文档

LoRA (低秩自适应)

Hugging Face's logo
加入 Hugging Face 社区

并获得增强的文档体验

开始使用

Ask a Question Open In Colab

LoRA (低秩自适应)

微调大型语言模型是一个资源密集的过程。LoRA 是一种允许我们用少量参数微调大型语言模型的技术。它的工作原理是向注意力权重添加和优化较小的矩阵,通常将可训练参数减少约 90%。

理解 LoRA

LoRA(低秩自适应)是一种参数高效的微调技术,它冻结预训练模型权重,并将可训练的秩分解矩阵注入到模型的层中。LoRA 不是在微调期间训练所有模型参数,而是通过低秩分解将权重更新分解为较小的矩阵,从而在保持模型性能的同时,显著减少可训练参数的数量。例如,当应用于 GPT-3 175B 时,与完全微调相比,LoRA 将可训练参数减少了 10,000 倍,GPU 内存需求减少了 3 倍。您可以在 LoRA 论文中阅读更多关于 LoRA 的信息。

LoRA 通过向 Transformer 层添加成对的秩分解矩阵来工作,通常侧重于注意力权重。在推理期间,这些适配器权重可以与基础模型合并,从而不会产生额外的延迟开销。LoRA 特别适用于将大型语言模型适应于特定任务或领域,同时保持资源需求可管理。

LoRA 的主要优势

  1. 内存效率:

    • 只有适配器参数存储在 GPU 内存中
    • 基础模型权重保持冻结,并且可以以较低精度加载
    • 使在消费级 GPU 上微调大型模型成为可能
  2. 训练特性:

    • 原生 PEFT/LoRA 集成,设置最少
    • 支持 QLoRA(量化 LoRA),以获得更好的内存效率
  3. 适配器管理:

    • 检查点期间的适配器权重保存
    • 将适配器合并回基础模型的功能

使用 PEFT 加载 LoRA 适配器

PEFT 是一个库,它为加载和管理 PEFT 方法(包括 LoRA)提供了统一的接口。它允许您轻松加载和切换不同的 PEFT 方法,从而更容易地实验不同的微调技术。

适配器可以使用 load_adapter() 加载到预训练模型上,这对于尝试权重未合并的不同适配器非常有用。使用 set_adapter() 函数设置活动适配器权重。要返回基础模型,您可以使用 unload() 来卸载所有 LoRA 模块。这使得在不同的特定于任务的权重之间切换变得容易。

from peft import PeftModel, PeftConfig

config = PeftConfig.from_pretrained("ybelkada/opt-350m-lora")
model = AutoModelForCausalLM.from_pretrained(config.base_model_name_or_path)
lora_model = PeftModel.from_pretrained(model, "ybelkada/opt-350m-lora")

lora_load_adapter

使用 trl 和 SFTTrainer 以及 LoRA 微调 LLM

trl 中的 SFTTrainer 通过 PEFT 库提供与 LoRA 适配器的集成。这意味着我们可以像使用 SFT 一样微调模型,但使用 LoRA 来减少我们需要训练的参数数量。

我们将在示例中使用 PEFT 中的 LoRAConfig 类。设置只需要几个配置步骤

  1. 定义 LoRA 配置(秩、alpha、dropout)
  2. 使用 PEFT 配置创建 SFTTrainer
  3. 训练并保存适配器权重

LoRA 配置

让我们了解一下 LoRA 配置和关键参数。

参数 描述
r(秩) 用于权重更新的低秩矩阵的维度。通常在 4-32 之间。较低的值提供更高的压缩率,但表达能力可能较差。
lora_alpha LoRA 层的缩放因子,通常设置为秩值的 2 倍。较高的值会导致更强的适应效果。
lora_dropout LoRA 层的 Dropout 概率,通常为 0.05-0.1。较高的值有助于防止训练期间的过拟合。
bias 控制偏置项的训练。选项为“none”、“all”或“lora_only”。“none”对于内存效率最常见。
target_modules 指定将 LoRA 应用于哪些模型模块。可以是“all-linear”或特定模块,如“q_proj,v_proj”。更多模块可以实现更大的适应性,但会增加内存使用量。
在实施 PEFT 方法时,从较小的秩值 (4-8) 开始 LoRA 并监控训练损失。使用验证集来防止过拟合,并在可能的情况下将结果与完全微调基线进行比较。不同方法的有效性可能因任务而异,因此实验是关键。

将 TRL 与 PEFT 结合使用

PEFT 方法可以与 TRL 结合使用进行微调,以减少内存需求。我们可以在加载模型时将 LoraConfig 传递给模型。

from peft import LoraConfig

# TODO: Configure LoRA parameters
# r: rank dimension for LoRA update matrices (smaller = more compression)
rank_dimension = 6
# lora_alpha: scaling factor for LoRA layers (higher = stronger adaptation)
lora_alpha = 8
# lora_dropout: dropout probability for LoRA layers (helps prevent overfitting)
lora_dropout = 0.05

peft_config = LoraConfig(
    r=rank_dimension,  # Rank dimension - typically between 4-32
    lora_alpha=lora_alpha,  # LoRA scaling factor - typically 2x rank
    lora_dropout=lora_dropout,  # Dropout probability for LoRA layers
    bias="none",  # Bias type for LoRA. the corresponding biases will be updated during training.
    target_modules="all-linear",  # Which modules to apply LoRA to
    task_type="CAUSAL_LM",  # Task type for model architecture
)

上面,我们使用了 device_map="auto" 来自动将模型分配到正确的设备。您也可以使用 device_map={"": device_index} 将模型手动分配到特定设备。

我们还需要使用 LoRA 配置定义 SFTTrainer

# Create SFTTrainer with LoRA configuration
trainer = SFTTrainer(
    model=model,
    args=args,
    train_dataset=dataset["train"],
    peft_config=peft_config,  # LoRA configuration
    max_seq_length=max_seq_length,  # Maximum sequence length
    processing_class=tokenizer,
)

✏️ 试一试! 基于上一节中微调的模型进行构建,但使用 LoRA 对其进行微调。使用 HuggingFaceTB/smoltalk 数据集微调 deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B 模型,使用我们上面定义的 LoRA 配置。

合并 LoRA 适配器

在使用 LoRA 进行训练后,您可能希望将适配器权重合并回基础模型,以便更轻松地部署。这将创建一个包含组合权重的单个模型,从而无需在推理期间单独加载适配器。

合并过程需要注意内存管理和精度。由于您需要同时加载基础模型和适配器权重,请确保有足够的 GPU/CPU 内存可用。在 transformers 中使用 device_map="auto" 将根据您的硬件找到模型的正确设备。

在整个过程中保持一致的精度(例如,float16),使其与训练期间使用的精度相匹配,并以相同的格式保存合并后的模型以进行部署。

合并实现

在训练 LoRA 适配器后,您可以将适配器权重合并回基础模型。以下是操作方法

import torch
from transformers import AutoModelForCausalLM
from peft import PeftModel

# 1. Load the base model
base_model = AutoModelForCausalLM.from_pretrained(
    "base_model_name", torch_dtype=torch.float16, device_map="auto"
)

# 2. Load the PEFT model with adapter
peft_model = PeftModel.from_pretrained(
    base_model, "path/to/adapter", torch_dtype=torch.float16
)

# 3. Merge adapter weights with base model
merged_model = peft_model.merge_and_unload()

如果您在保存的模型中遇到大小差异,请确保您也保存了分词器

# Save both model and tokenizer
tokenizer = AutoTokenizer.from_pretrained("base_model_name")
merged_model.save_pretrained("path/to/save/merged_model")
tokenizer.save_pretrained("path/to/save/merged_model")

✏️ 试一试! 将适配器权重合并回基础模型。使用 HuggingFaceTB/smoltalk 数据集微调 deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B 模型,使用我们上面定义的 LoRA 配置。

资源

< > 在 GitHub 上更新