原文出处:LoRA and PEFT: Efficient Fine-Tuning 原作者:Hugging Face · 许可证:Apache-2.0 License 中文译本由诸葛AI学院整理,仅供学习参考,版权归原作者与 Hugging Face 所有。
LoRA 与 PEFT:高效微调
参数高效微调(Parameter-Efficient Fine-Tuning,PEFT)让你只训练少量新增参数就能适配大模型,基座模型的权重保持冻结。用得最多的 PEFT 方法是 LoRA(Low-Rank Adaptation,低秩适配),它把可训练的低秩更新注入线性层。通常这能把可训练参数减少约 90%,同时保住模型性能。
什么时候用 PEFT
- 算力或内存预算有限
- 需要把同一个基座模型快速适配到多个任务或领域
- 想要快速迭代、产出文件小(适配器权重通常只有几 MB)
理解 LoRA
LoRA 已经成为采用最广的 PEFT 方法。它的做法是往注意力(attention)权重上加小的秩分解矩阵,可训练参数一般能减少 90% 左右。
LoRA 是一种参数高效的微调技术:冻结预训练模型的权重,往模型各层注入可训练的秩分解矩阵。微调时不是训练全部模型参数,LoRA 通过低秩分解把权重更新拆成更小的矩阵,大幅减少可训练参数的数量,同时维持模型性能。举例来说,把 LoRA 用在 GPT-3 175B 上,相比全参数微调,可训练参数减少了一万倍,GPU 内存需求降低了 3 倍。更多细节可以读 LoRA 论文。
LoRA 的工作方式是给 transformer 层加上成对的秩分解矩阵,主要作用于注意力权重。推理时,这些适配器权重可以和基座模型合并,不会带来额外的延迟开销。把大语言模型适配到特定任务或领域时,LoRA 特别好用,资源需求也控制得住。
加载 LoRA 适配器
用 load_adapter() 可以把适配器加载到一个预训练模型上,适合尝试多个权重未合并的适配器。再用 set_adapter() 指定哪套适配器权重生效;想回到基座模型,可以用 unload() 把所有 LoRA 模块卸掉。这样在不同任务权重之间切换很方便。
```python from transformers import AutoModelForCausalLM from peft import PeftModel
base_model = AutoModelForCausalLM.from_pretrained("
合并 LoRA 适配器
用 LoRA 训练完之后,你可能想把适配器权重合并回基座模型,部署起来更省事。合并后得到一个权重整合的单一模型,推理时不用再单独加载适配器。
合并过程要注意内存管理和精度。基座模型和适配器权重要同时加载,确保 GPU/CPU 内存够用;在 transformers 里用 device_map="auto" 可以自动分配内存。全程保持一致的精度(比如 float16),与训练时使用的精度对齐,并把合并后的模型按部署时的格式保存。上线之前,务必把合并模型和"基座 + 适配器"版本的输出与性能指标做对比,验证无误再部署。
适配器也适合在不同任务或领域之间切换:基座模型和适配器权重分开加载,就能在不同任务权重之间快速换。
[!TIP] 落地 PEFT 方法时,LoRA 的秩(rank)从小值起步(4-8),盯住训练损失。用验证集防止过拟合,条件允许的话,拿全参数微调当基线来对比。不同方法在各任务上的效果会有差别,动手实验是关键。
OLoRA
OLoRA 用 QR 分解来初始化 LoRA 适配器。OLoRA 按模型基座权重的 QR 分解因子对权重做一次变换,也就是说,在正式训练之前就先把权重改掉。这样做能明显改善稳定性、加快收敛,最终拿到更好的性能。
用 TRL 搭配 PEFT
PEFT 方法可以和 TRL(Transformers Reinforcement Learning,transformers 强化学习库)结合,做高效微调。这个组合对 RLHF(Reinforcement Learning from Human Feedback,基于人类反馈的强化学习)特别有用,因为内存需求降低了。
```python from peft import LoraConfig from trl import SFTTrainer
用 PEFT 配置加载模型
lora_config = LoraConfig( r=16, lora_alpha=32, lora_dropout=0.05, bias="none", task_type="CAUSAL_LM" )
trainer = SFTTrainer( model="your-model-name", train_dataset=dataset["train"] peft_config=lora_config ) ```
合并的最小实现
训练好一个 LoRA 适配器后,你可以把适配器权重合并回基座模型。做法如下:
```python import torch from transformers import AutoModelForCausalLM from peft import PeftModel
1. 加载基座模型
base_model = AutoModelForCausalLM.from_pretrained( "base_model_name", dtype=torch.bfloat16, device_map="auto" )
2. 加载带适配器的 PEFT 模型
peft_model = PeftModel.from_pretrained( base_model, "path/to/adapter", dtype=torch.bfloat16 )
3. 把适配器权重合并进基座模型
try: merged_model = peft_model.merge_and_unload() except RuntimeError as e: print(f"Merging failed: {e}") # 改用兜底方案,或做内存优化
4. 保存合并后的模型
merged_model.save_pretrained("path/to/save/merged_model") ```
如果保存出来的模型大小对不上,确认你是否把分词器(tokenizer)也一起保存了:
```python
模型和分词器都要保存
tokenizer = AutoTokenizer.from_pretrained("base_model_name") merged_model.save_pretrained("path/to/save/merged_model") tokenizer.save_pretrained("path/to/save/merged_model") ```
TRL + LoRA 快速上手
TRL 的 SFTTrainer 原生集成了 PEFT。定义一个 LoraConfig,传给 trainer,训练时就只更新适配器权重。
```python from peft import LoraConfig from trl import SFTTrainer, SFTConfig
1) 配置 LoRA
peft_config = LoraConfig( r=8, lora_alpha=16, lora_dropout=0.05, bias="none", task_type="CAUSAL_LM", )
2) 创建 trainer(示例)
trainer = SFTTrainer( model=model, args=SFTConfig(output_dir="lora-adapter", num_train_epochs=1, per_device_train_batch_size=2, packing=True), train_dataset=dataset["train"], peft_config=peft_config, ) trainer.train() ```
训练完成后,两条路任选:
- 推理时把适配器和基座模型一起加载;
- 或者把适配器合并进基座模型,简化部署。