3. LoRA秩的选择
任务目标
在GLUE基准的MRPC(微软研究释义语料库)文本对匹配任务上,使用BERT-base作为底座模型,分别以LoRA秩 r=1、4、8、16 进行微调,通过验证集准确率与F1分数对比,确定当前任务下的最佳秩配置。
环境与依赖
需要安装以下核心库(Python 3.9+):
pip install transformers==4.40.0 datasets==2.19.0 peft==0.10.0 torch==2.2.0 evaluate==0.4.1
完整实现代码
以下代码完整实现了4种秩配置的训练与评估闭环。使用MRPC数据集的1,000条训练样本和验证集全量(408条)进行快速对比,确保在合理时间内完成实验。
import torch
from datasets import load_dataset
from transformers import (
AutoTokenizer, AutoModelForSequenceClassification,
TrainingArguments, Trainer, DataCollatorWithPadding
)
from peft import LoraConfig, get_peft_model, TaskType
import evaluate
import numpy as np
# ========== 1. 加载数据与预处理 ==========
dataset = load_dataset("glue", "mrpc")
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
def preprocess(examples):
return tokenizer(examples["sentence1"], examples["sentence2"],
truncation=True, max_length=128)
# 使用1000条训练样本加速实验
train_data = dataset["train"].select(range(1000)).map(preprocess, batched=True)
eval_data = dataset["validation"].map(preprocess, batched=True)
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
# ========== 2. 评估指标 ==========
accuracy_metric = evaluate.load("accuracy")
f1_metric = evaluate.load("f1")
def compute_metrics(eval_pred):
logits, labels = eval_pred
predictions = np.argmax(logits, axis=-1)
acc = accuracy_metric.compute(predictions=predictions, references=labels)["accuracy"]
f1 = f1_metric.compute(predictions=predictions, references=labels)["f1"]
return {"accuracy": acc, "f1": f1}
# ========== 3. 训练不同秩的LoRA ==========
def train_and_evaluate(r):
model = AutoModelForSequenceClassification.from_pretrained(
"bert-base-uncased", num_labels=2
)
# 冻结全部基座参数
for param in model.parameters():
param.requires_grad = False
lora_config = LoraConfig(
task_type=TaskType.SEQ_CLS,
r=r,
lora_alpha=2 * r, # 缩放因子随秩线性调整
lora_dropout=0.1,
target_modules=["query", "value"] # 仅注入Q和V矩阵
)
peft_model = get_peft_model(model, lora_config)
peft_model.print_trainable_parameters()
training_args = TrainingArguments(
output_dir=f"./lora_r_{r}",
learning_rate=3e-4,
per_device_train_batch_size=32,
per_device_eval_batch_size=32,
num_train_epochs=3,
evaluation_strategy="epoch",
save_strategy="no",
logging_dir=f"./logs_r_{r}",
remove_unused_columns=False,
report_to=[],
)
trainer = Trainer(
model=peft_model,
args=training_args,
train_dataset=train_data,
eval_dataset=eval_data,
data_collator=data_collator,
compute_metrics=compute_metrics,
)
trainer.train()
eval_result = trainer.evaluate()
return eval_result
# ========== 4. 执行对比实验 ==========
if __name__ == "__main__":
results = {}
for r in [1, 4, 8, 16]:
print(f"\n===== 训练 LoRA r={r} =====")
result = train_and_evaluate(r)
results[r] = {
"accuracy": result["eval_accuracy"],
"f1": result["eval_f1"]
}
print(f"r={r} -> Accuracy: {result['eval_accuracy']:.4f}, F1: {result['eval_f1']:.4f}")
# 输出汇总
print("\n===== 结果汇总 =====")
for r, metrics in results.items():
print(f"r={r:2d} | Acc={metrics['accuracy']:.4f} | F1={metrics['f1']:.4f}")
# 确定最佳秩
best_r = max(results, key=lambda r: results[r]["f1"])
print(f"\n最佳秩: r={best_r} (基于F1分数)")
运行方式
将上述代码保存为 lora_rank_search.py,在具备GPU(建议显存≥8GB)的环境中执行:
python lora_rank_search.py
首次运行会自动下载BERT-base模型与MRPC数据集。训练4个模型(每个约3分钟,取决于GPU性能),最终在终端输出各秩的验证集指标。
验证方法与判定标准
- 可训练参数对比:程序会打印每个秩的LoRA可训练参数量。r=1时约0.3M参数,r=16时约1.2M参数,均远小于全量微调的110M参数,验证LoRA的高效性。
- 性能趋势观察:MRPC验证集上,通常r=8或r=16能取得最佳F1分数(约85-88%),r=1可能欠拟合(F1约80-83%)。若r=4与r=8差距小于0.5%,说明低秩已足够捕获任务特征。
- 判定最佳秩:以F1分数为主要指标(MRPC类别不平衡),选择F1最高的秩作为最佳配置。若两个秩的F1差异小于0.3%,则选择更小的秩(参数效率更高)。
预期输出示例
===== 结果汇总 =====
r= 1 | Acc=0.7892 | F1=0.8341
r= 4 | Acc=0.8235 | F1=0.8612
r= 8 | Acc=0.8382 | F1=0.8725
r=16 | Acc=0.8407 | F1=0.8749
最佳秩: r=16 (基于F1分数)
以上数值仅为基于模型容量的合理示例,实际结果会因随机种子和训练环境略有波动。核心观察点在于:随着秩增大,性能先快速提升后趋于平缓,r=8与r=16的差距通常小于r=1与r=4的差距。
关键结论与使用建议
- 秩的选择规律:低秩(r=1-4)适合简单任务或数据量小的场景;中等秩(r=8)是多数文本分类任务的稳妥起点;高秩(r=16)仅在任务复杂度高时带来边际收益。
- 参数效率考量:r=16的可训练参数量约为r=1的16倍,但性能提升通常不超过3-5%。若部署资源紧张,优先选择性能下降在0.5%以内的最小秩。
- 超参数联动:本实验将
lora_alpha设为2*r,保持缩放比例一致。若调整alpha值,最佳秩可能偏移,需重新对比。