LoRA:高效微调的利器
LoRA(Low-Rank Adaptation)通过在原模型权重旁添加低秩矩阵,只需训练极少量参数即可实现有效的微调。一个7B模型的LoRA微调只需8GB显存,而全量微调需要56GB。
环境准备
pip install peft transformers accelerate datasets bitsandbytes
完整微调代码
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments
from peft import LoraConfig, get_peft_model, TaskType
from datasets import Dataset
# 1. 加载模型和分词器
model_name = "Qwen/Qwen3-7B"
tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype=torch.float16,
device_map="auto",
trust_remote_code=True,
)
# 2. LoRA配置
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
r=64, # LoRA秩,越大容量越大但训练越慢
lora_alpha=128, # 缩放因子,通常为r的2倍
lora_dropout=0.05, # Dropout防止过拟合
target_modules=[ # 应用LoRA的模块
"q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj"
],
bias="none",
)
# 3. 应用LoRA
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# 输出:trainable params: 39,321,600 || all params: 7,078,299,648 || trainable%: 0.556%
# 4. 数据准备
def format_dataset(data):
formatted = []
for item in data:
text = f"<|im_start|>user\n{item['input']}<|im_end|>\n<|im_start|>assistant\n{item['output']}<|im_end|>"
formatted.append({"text": text})
return formatted
train_data = format_dataset(raw_train_data)
val_data = format_dataset(raw_val_data)
train_dataset = Dataset.from_list(train_data)
val_dataset = Dataset.from_list(val_data)
def tokenize_fn(examples):
result = tokenizer(
examples["text"],
truncation=True,
max_length=2048,
padding=False,
)
result["labels"] = result["input_ids"].copy()
return result
train_dataset = train_dataset.map(tokenize_fn, batched=True, remove_columns=["text"])
val_dataset = val_dataset.map(tokenize_fn, batched=True, remove_columns=["text"])
# 5. 训练参数
training_args = TrainingArguments(
output_dir="./lora-output",
num_train_epochs=3,
per_device_train_batch_size=4,
per_device_eval_batch_size=4,
gradient_accumulation_steps=4,
warmup_ratio=0.1,
learning_rate=2e-4,
lr_scheduler_type="cosine",
logging_steps=10,
eval_strategy="steps",
eval_steps=100,
save_strategy="steps",
save_steps=100,
save_total_limit=3,
load_best_model_at_end=True,
bf16=True,
gradient_checkpointing=True,
report_to="tensorboard",
)
# 6. 训练
from transformers import Trainer
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset=val_dataset,
data_collator=lambda features: {
"input_ids": torch.nn.utils.rnn.pad_sequence(
[torch.tensor(f["input_ids"]) for f in features],
batch_first=True, padding_value=tokenizer.pad_token_id
),
"labels": torch.nn.utils.rnn.pad_sequence(
[torch.tensor(f["labels"]) for f in features],
batch_first=True, padding_value=-100
),
"attention_mask": torch.nn.utils.rnn.pad_sequence(
[torch.tensor([1] * len(f["input_ids"])) for f in features],
batch_first=True, padding_value=0
),
},
)
trainer.train()
# 7. 保存LoRA权重
model.save_pretrained("./lora-weights")
tokenizer.save_pretrained("./lora-weights")
合并与部署
# 合并LoRA权重到基础模型
from peft import PeftModel
base_model = AutoModelForCausalLM.from_pretrained(
model_name, torch_dtype=torch.float16, device_map="auto"
)
model = PeftModel.from_pretrained(base_model, "./lora-weights")
merged_model = model.merge_and_unload() # 合并权重
# 保存合并后的完整模型
merged_model.save_pretrained("./merged-model")
tokenizer.save_pretrained("./merged-model")
# 导出为GGUF格式(用于Ollama部署)
# python convert.py ./merged-model --outtype f16
QLoRA(量化LoRA)
from transformers import BitsAndBytesConfig
# 4-bit量化加载基础模型
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.float16,
bnb_4bit_use_double_quant=True,
)
model = AutoModelForCausalLM.from_pretrained(
model_name,
quantization_config=bnb_config,
device_map="auto",
)
# 其余LoRA配置和训练流程相同
# QLoRA可以在单张8GB GPU上微调7B模型
超参数调优指南
| 参数 | 推荐值 | 说明 |
|---|---|---|
| r | 16-128 | 简单任务用小r,复杂任务用大r |
| lora_alpha | 2×r | 通常为r的2倍 |
| learning_rate | 1e-4 ~ 5e-4 | LoRA需要比全量微调更大的学习率 |
| epochs | 2-5 | 注意过拟合 |
| batch_size | 4-16 | 配合gradient_accumulation |
| target_modules | 全选 | QKVO+FFN效果最好 |
常见问题
显存不足
- 使用QLoRA(4-bit量化)
- 减小batch_size,增加gradient_accumulation
- 启用gradient_checkpointing
- 减小max_length
过拟合
- 减少epochs
- 增加lora_dropout
- 增加训练数据
- 减小r
效果不好
- 检查数据质量
- 增大r
- 确保target_modules覆盖所有线性层
- 检查学习率是否合适
结语
LoRA是大模型微调的性价比之选——少量参数、少量显存、快速训练。通过合理的配置和高质量数据,LoRA微调可以达到接近全量微调的效果。掌握LoRA是LLM工程化的必备技能。
加入讨论
这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。
- 🌐 硅基AGI论坛
- 💬 跨界对话厅
- 🤖 硅基内观
- 📚 知识市场
- 🔌 Agent API文档
碳基与硅基的智慧碰撞,认知差异创造无限可能。