PEFT库介绍
PEFT(Parameter-Efficient Fine-Tuning,参数高效微调)是由 Hugging Face 开源的一个高效微调库,旨在通过少量可训练参数实现对大型预训练模型的快速适应,从而显著降低计算和存储成本。
核心功能与优势
-
多种高效微调方法:
- LoRA(Low-Rank Adaptation):通过引入低秩矩阵来微调模型权重,仅需训练少量参数即可达到与全参数微调相当的性能。
- Prefix Tuning:通过优化输入提示(prompt)来调整模型,不改变模型权重,适应性强。
- Prompt Tuning:通过优化连续提示来微调模型,适用于零样本和少样本学习。
- BitFit:仅对模型的偏置项进行微调,极大地减少了需要优化的参数数量。
-
显著降低资源消耗:
- PEFT 方法仅需训练模型中极小部分参数(如 0.1%),显著降低了计算和存储成本。
- 例如,使用 LoRA 微调一个 3B 参数的模型,仅需 14.4GB GPU 内存,而全参数微调则需要 47.14GB。
-
与 Hugging Face 生态系统无缝集成:
- PEFT 与 Transformers 和 Accelerate 库深度集成,支持多种模型(如 BERT、GPT、T5)和任务(如序列分类、因果语言建模、图像生成等)。
-
应用场景:
- 大语言模型微调:在消费级硬件上高效微调大型语言模型。
- 图像生成:与 Diffusers 集成,用于微调 Stable Diffusion 等模型。
- 强化学习:与 TRL 库结合,用于基于人类反馈的强化学习(RLHF)。
- 多任务学习:支持多任务提示调优,使模型能够适应多个下游任务。
安装与使用
-
安装:
- 使用 PyPI 安装:
pip install peft
- 使用源码安装(获取最新功能):
pip install git+https://github.com/huggingface/peft
- 本地克隆并安装可编辑版本:
git clone https://github.com/huggingface/peft cd peft pip install -e .
- 使用 PyPI 安装:
-
快速入门:
- 以下是一个使用 LoRA 微调的示例代码:
from transformers import AutoModelForSequenceClassification, AutoTokenizer,DataCollatorWithPadding
from peft import get_peft_model, LoraConfig, TaskType
from transformers import Trainer, TrainingArguments
model_name = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_name)
tokenized_dataset = ...#根据自己的数据类型进行加载,使用datasets.load_dataset()函数
model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=2)
lora_config = LoraConfig(
task_type=TaskType.SEQ_CLS,
inference_mode=False,
r=4,
lora_alpha=16,
lora_dropout=0.1
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
args = TrainingArguments(
output_dir=config.save_model,
per_device_train_batch_size=config.train_batch_size,
gradient_accumulation_steps=config.accumulation_batch_size,#梯度累计更新,降低显存使用的方法
logging_steps=50,
remove_unused_columns=True,#可以保留多余的参数,Tranier默认tokenized_dataset处理后的字典只支持input_ids,attention_mask,labels三个参数,如果想要传递其他参数的话需要设置为False
num_train_epochs=config.max_epoch,
learning_rate=config.learning_rate,
evaluation_strategy="epoch",
save_strategy="epoch",
load_best_model_at_end=True,
gradient_checkpointing=True # 确保不使用缓存
)
trainer = Trainer(
model=model,
args=args,
train_dataset=tokenized_dataset['train'],
eval_dataset=tokenized_dataset['test'],
compute_metrics=compute_metrics,
data_collator=DataCollatorWithPadding(tokenizer=tokenizer, padding=True,return_tensors="pt")
)
trainer.train()
源码解析
- 点击get_peft_model()函数,就可以看见源码了:
- 上面执行一个条件判断,判断该模型是不是提示学习方法类型的模型,该该方法包括P_Tuning,PromptTuning,Prefix_Tuning等。然后根据任务类型执行相应的PEFTModel装配步骤,比如我当前正在执行序列分类任务。则进入PeftModelForSequenceClassification模型,但是最终都会先进入PeftModel进行初始化。
- 查看PeftModel装配对应模块的方法:
- 可以在PeftModel里面看到这两段核心逻辑,其中先判断是否为prompt_learning方法,在进行各种的可学习参数注入。我们首先查看lora类型的该如何注入,由于lora是非prompt_learning方法,所以走下面的else分支。
- PEFT_TYPE_TO_MODEL_MAPPING里面装载了许多模型参数高效微调的方法模型,可以在你的IDE工具的外部库 /site-packages/peft/tuners中看见。我们使用lora现在加载了LoraModel进来,进入一个重要的分支:
self.base_model = cls(model, {adapter_name: peft_config}, adapter_name)
,这里会进行lora模型和我们原来模型的装配。
- LoraModel装配模型:
- 我们刚刚点击进入到cls(model, {adapter_name: peft_config}, adapter_name)里面,这里面就是对应类型微调方法的实现模型,我们这里是LoraModel。这里进去LoraModel的init方法,我们点击init方法,就可以看到LoraModel的初始化过程了,如上图。接下来我们进入最核心的 self.inject_adapter(self.model, adapter_name, low_cpu_mem_usage=low_cpu_mem_usage)。
- 查看模型的装配过程。
- 首先,会执行检查过程,查看当前key是否为我们在LoraConfig中设置的target_module中的匹配,如果符合则会进入到
self._create_and_replace(peft_config, adapter_name, target, target_name, parent, current_key=key)
- 在_create_and_replace方法中,首先会创建一个Lora对象:
- 我们可以看到,返回的对象是原来线性层(base_layer,即符合规则的q_proj)和对应的lora模块组合,这个模块将会在
self._replace_module(parent, target_name, new_module, target)
中调用setattr(parent, child_name, new_module)方法替换掉原来的线性层(q_proj对应的线性层)。 - 其余模块的替换也是一样的逻辑,然后全部替换完毕后会返回一个新的由Lora替换的模型回去,至此Peft替换Lora部分的源码解读完毕。另外的关于Prompt_learning分支的代码如果有需要的我也会在后续更新,如果对你有帮助的话请记得点击一个收藏。
总结
PEFT 为大型模型的高效微调提供了强大的工具集,不仅降低了微调成本,还保持了优异的性能。它适用于多种任务和模型,是研究人员和工程师在资源受限环境中微调大型模型的理想选择。