【Smol Course】3-参数高效微调
wbfwonderful Lv4

Parameter-Efficient Fine-Tuning (PEFT)

随着语言模型变得越来越大,传统的微调变得越来越具有挑战性。即使是 1.7B 参数模型的全面微调也需要大量的 GPU 内存,使存储单独的模型副本变得昂贵,并且有可能灾难性地忘记模型的原始功能。参数有效微调(PEFT)方法通过仅修改模型参数的一小部分而保持大部分模型不变来解决这些挑战。

传统的微调在训练过程中更新所有的模型参数,这对于大型模型来说是不切实际的。PEFT 方法引入了使用较少可训练参数(通常小于原始模型大小的 1%)来调整模型的方法。可训练参数的大幅减少使:

  • 在 GPU 内存有限的消费者硬件上进行微调
  • 有效地存储多个特定于任务的适应性
  • 在低数据场景下更好的泛化
  • 更快的训练和迭代周期

LoRA (Low-Rank Adaptation)

LoRA 已成为最广泛采用的 PEFT 方法。它的工作原理是将小秩分解矩阵添加到注意力权重中,通常会将可训练参数减少约 90%。在推理期间,这些适配器权重可以与基本模型合并,从而不会产生额外的延迟开销。LoRA 对于使大型语言模型适应特定的任务或领域,同时保持资源需求可管理特别有用。

Loading LoRA Adapters

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

1
2
3
4
5
6
from transformers import AutoModelForCausalLM
from peft import PeftModel

base_model = AutoModelForCausalLM.from_pretrained("<base_model_name>")
peft_model_id = "<peft_adapter_id>"
model = PeftModel.from_pretrained(base_model, peft_model_id)

Merging LoRA Adapters

在使用 LoRA 进行训练之后,您可能希望将适配器权重合并回基本模型中,以便于部署。这将创建具有组合权重的单个模型,从而消除了在推理期间单独加载适配器的需要。

合并过程需要注意内存管理和精度。由于您需要同时加载基本模型和适配器权重,因此请确保有足够的 GPU/CPU 内存可用。在 transformer 中使用 device_map=”auto” 将有助于自动内存管理。在整个过程中保持一致的精度(例如,float16),匹配训练期间使用的精度,并以相同的格式保存合并模型以供部署。在部署之前,始终通过将其输出和性能指标与基于适配器的版本进行比较来验证合并模型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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
try:
merged_model = peft_model.merge_and_unload()
except RuntimeError as e:
print(f"Merging failed: {e}")
# Implement fallback strategy or memory optimization

# 4. Save the merged model
merged_model.save_pretrained("path/to/save/merged_model")

注意,PEFT 保存的模型只保存 adapter(如 LoRA 权重)部分,但是,AutoPeftModelForCausalLM 会:

  • 自动加载 base model(如 LLaMA、Qwen、GPT-NeoX):通过在 adapter config 中保存的 base model 路径(或你传的路径);
  • 然后在此基础上加载 adapter 权重;
  • 得到一个完整的、可运行的 PEFT 模型。
  • 换句话说,传入的 args.output_dir 实际上是包含:
    • adapter 权重
    • adapter 配置
    • 一个可解析的 base_model_name_or_path 字段(指向原始模型)

Using TRL with PEFT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from peft import LoraConfig
from transformers import AutoModelForCausalLM

# Load model with PEFT config
lora_config = LoraConfig(
r=16,
lora_alpha=32,
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)

# Load model on specific device
model = AutoModelForCausalLM.from_pretrained(
"your-model-name",
load_in_8bit=True, # Optional: use 8-bit precision
device_map="auto",
peft_config=lora_config
)

Practice

Parameters

定义 PEFT 参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
)

将 PEFT 参数添加到 SFTTrainer 中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
max_seq_length = 1512  # max sequence length for model and packing of the dataset

# 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
tokenizer=tokenizer,
packing=True, # Enable input packing for efficiency
dataset_kwargs={
"add_special_tokens": False, # Special tokens handled by template
"append_concat_token": False, # No additional separator needed
},
)

Load adapters from the Hub

  • 首先加载的是 PeftConfig
  • 然后基于 PeftConfig 加载 base model
  • 最后加载带 LoRA 的模型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import torch
import transformers
from datasets import load_dataset
from peft import LoraConfig, get_peft_model
from peft import PeftModel, PeftConfig
from transformers import AutoModelForCausalLM, AutoTokenizer

peft_model_id = "ybelkada/opt-6.7b-lora"
config = PeftConfig.from_pretrained(peft_model_id)
model = AutoModelForCausalLM.from_pretrained(
config.base_model_name_or_path,
return_dict=True,
load_in_8bit=True,
device_map="auto",
)
tokenizer = AutoTokenizer.from_pretrained(config.base_model_name_or_path)

# Load the Lora model
model = PeftModel.from_pretrained(model, peft_model_id)

Prompt Tuning

提示调优是一种参数高效的方法,它修改输入表示,而不是模型权重。与更新所有模型参数的传统微调不同,提示调优在保持基本模型不变的同时添加并优化了一小组可训练的令牌。提示调优将可训练的连续向量(软提示)添加到输入文本中。

这些软提示是模型嵌入空间中的连续向量,在训练过程中得到优化。与使用自然语言标记的传统离散提示不同,软提示没有固有的含义,而是通过梯度下降学习从冻结模型中引出所需的行为。该技术对于多任务场景特别有效,因为每个任务只需要存储一个小的提示向量(通常是几百个参数),而不是一个完整的模型副本。这种方法不仅保持了最小的内存占用,而且还支持快速的任务切换。

在训练过程中,只更新提示参数,而基本模型保持冻结。这种集中的方法使用标准的训练目标,但需要仔细注意提示符号的学习率和梯度行为。

Implementation with PEFT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from peft import PromptTuningConfig, TaskType, get_peft_model
from transformers import AutoModelForCausalLM, AutoTokenizer

# Load base model
model = AutoModelForCausalLM.from_pretrained("your-base-model")
tokenizer = AutoTokenizer.from_pretrained("your-base-model")

# Configure prompt tuning
peft_config = PromptTuningConfig(
task_type=TaskType.CAUSAL_LM,
num_virtual_tokens=8, # Number of trainable tokens
prompt_tuning_init="TEXT", # Initialize from text
prompt_tuning_init_text="Classify if this text is positive or negative:",
tokenizer_name_or_path="your-base-model",
)

# Create prompt-tunable model
model = get_peft_model(model, peft_config)

Comparison

Method Parameters Memory Task Switching
Prompt Tuning Very Low Minimal Easy
LoRA Low Low Requires Loading
Full Fine-tuning High High New Model Copy

提示调优在以下几个场景中表现出色:

  • 多任务部署
  • 资源受限的环境
  • 快速任务适应
  • 隐私的应用程序