3. LoRA微调BERT用于情感分类

LoRA微调BERT用于情感分类 学习卡片

任务目标

在IMDb电影评论数据集上,使用LoRA对BERT-base进行情感二分类微调,并与全量微调(Full Fine-tuning)的准确率进行对比。本案例聚焦于验证LoRA在参数量大幅减少的情况下能否达到接近全量微调的效果。

环境与依赖

需要安装以下核心库(Python 3.9+):

pip install transformers datasets peft accelerate scikit-learn torch

实现代码

以下代码完整实现了数据加载、LoRA配置、训练与评估对比。使用IMDb数据集的2,000条子集(训练1,000/测试1,000)以保证在消费级GPU上可快速运行;全量微调使用相同子集和训练轮次。

import torch
from datasets import load_dataset
from transformers import (
    AutoTokenizer, AutoModelForSequenceClassification,
    TrainingArguments, Trainer, DataCollatorWithPadding
)
from peft import LoraConfig, get_peft_model, TaskType
from sklearn.metrics import accuracy_score
import numpy as np

# ---------- 1. 加载数据与tokenizer ----------
dataset = load_dataset("imdb", split="train[:1000]")
test_dataset = load_dataset("imdb", split="test[:1000]")

tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")

def preprocess(examples):
    return tokenizer(examples["text"], truncation=True, max_length=256)

train_data = dataset.map(preprocess, batched=True, remove_columns=["text"])
test_data = test_dataset.map(preprocess, batched=True, remove_columns=["text"])
train_data = train_data.rename_column("label", "labels")
test_data = test_data.rename_column("label", "labels")
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)

# ---------- 2. 定义训练与评估函数 ----------
def compute_metrics(eval_pred):
    preds, labels = eval_pred
    preds = np.argmax(preds, axis=1)
    return {"accuracy": accuracy_score(labels, preds)}

def train_model(model, run_name):
    args = TrainingArguments(
        output_dir=f"./{run_name}",
        learning_rate=2e-5,
        per_device_train_batch_size=16,
        per_device_eval_batch_size=32,
        num_train_epochs=2,
        evaluation_strategy="epoch",
        save_strategy="no",
        logging_dir=f"./{run_name}_logs",
        report_to=[],
        disable_tqdm=True,
    )
    trainer = Trainer(
        model=model,
        args=args,
        train_dataset=train_data,
        eval_dataset=test_data,
        tokenizer=tokenizer,
        data_collator=data_collator,
        compute_metrics=compute_metrics,
    )
    trainer.train()
    return trainer.evaluate()

# ---------- 3. LoRA微调 ----------
base_model = AutoModelForSequenceClassification.from_pretrained(
    "bert-base-uncased", num_labels=2
)
lora_config = LoraConfig(
    task_type=TaskType.SEQ_CLS,
    r=8,
    lora_alpha=16,
    lora_dropout=0.1,
    target_modules=["query", "value"],
)
lora_model = get_peft_model(base_model, lora_config)
print(f"LoRA可训练参数量: {lora_model.num_parameters(only_trainable=True):,}")

lora_metrics = train_model(lora_model, "bert_lora_imdb")
print(f"LoRA评估结果: {lora_metrics}")

# ---------- 4. 全量微调(相同数据与超参数) ----------
full_model = AutoModelForSequenceClassification.from_pretrained(
    "bert-base-uncased", num_labels=2
)
print(f"全量微调参数量: {full_model.num_parameters():,}")

full_metrics = train_model(full_model, "bert_full_imdb")
print(f"全量微调评估结果: {full_metrics}")

# ---------- 5. 对比输出 ----------
print("\n===== 准确率对比 =====")
print(f"LoRA 准确率: {lora_metrics['eval_accuracy']:.4f}")
print(f"Full 准确率: {full_metrics['eval_accuracy']:.4f}")

运行方式

将上述代码保存为 lora_vs_full_imdb.py,直接执行:

python lora_vs_full_imdb.py

首次运行会自动下载BERT-base模型与IMDb数据集(约500MB缓存)。若显存不足(<8GB),可将 per_device_train_batch_size 降至8。

验证方法与判定标准

代码运行结束后,控制台会依次输出两个模型的评估指标。判定实验有效的标准:

  • LoRA可训练参数量约为0.3M(对比全量109M,减少约99.7%),该数值由代码自动打印。
  • LoRA与全量微调均完成2个epoch训练,无报错中断。
  • 对比输出中两个准确率均为0.8~0.9之间的合理数值(IMDb二分类任务在此子集规模下的典型范围),且LoRA准确率与全量微调差距在0.05以内,即证明LoRA在显著减少可训练参数的同时保持了接近全量微调的性能。

注意:由于使用2,000条子集且仅训练2轮,准确率会低于完整数据集上的报告值(约0.93),这是预期现象,本案例核心在于对比两种微调方式的相对差距。

常见问题

  • 显存溢出:降低batch size至8或4,或使用 max_length=128 减少序列长度。
  • 训练速度过慢:确保使用GPU(nvidia-smi 确认),CPU训练需数小时。
  • 结果波动:由于子集随机采样,两次运行准确率可能有±0.02波动,属正常现象。