Skip to content

🛠️ PEFT 微调实战

从零到一跑通第一个 LoRA 微调——代码直接抄走能用

环境准备

bash
pip install transformers peft accelerate datasets bitsandbytes

核心库:

作用
transformers加载模型和 tokenizer
peftLoRA 实现
accelerate分布式训练
datasets数据加载
bitsandbytes4-bit 量化(QLoRA 用)

完整代码:QLoRA 微调 Qwen2.5-7B

以下代码在 16GB 显存显卡上可运行,微调 7B 模型。

python
import torch
from transformers import (
    AutoModelForCausalLM,
    AutoTokenizer,
    TrainingArguments,
    Trainer,
    DataCollatorForSeq2Seq,
)
from peft import LoraConfig, get_peft_model, TaskType
from datasets import Dataset
import json

# ========== 1. 加载模型(4-bit 量化) ==========
model_name = "Qwen/Qwen2.5-7B-Instruct"

model = AutoModelForCausalLM.from_pretrained(
    model_name,
    torch_dtype=torch.float16,
    device_map="auto",
    load_in_4bit=True,  # QLoRA 关键
    bnb_4bit_compute_dtype=torch.float16,
)
tokenizer = AutoTokenizer.from_pretrained(model_name)

# ========== 2. 配置 LoRA ==========
lora_config = LoraConfig(
    task_type=TaskType.CAUSAL_LM,
    r=8,               # rank,越大效果越好但越慢
    lora_alpha=16,     # 缩放系数,一般设 r 的 2 倍
    lora_dropout=0.1,  # dropout 防过拟合
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],  # Qwen 的注意力层
)

model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# 输出:trainable params: 8,388,608 || all params: 7,623,000,000 || trainable%: 0.11%

# ========== 3. 加载数据 ==========
def load_dataset(path):
    with open(path) as f:
        data = json.load(f)

    texts = []
    for item in data:
        # 拼接成 ChatML 格式
        text = f"<|im_start|>system\n你是合同审查助手<|im_end|>\n"
        text += f"<|im_start|>user\n{item['instruction']}\n{item['input']}<|im_end|>\n"
        text += f"<|im_start|>assistant\n{item['output']}<|im_end|>"
        texts.append(text)

    return Dataset.from_dict({"text": texts})

dataset = load_dataset("contract_data.json")

# ========== 4. Tokenize ==========
def tokenize(examples):
    result = tokenizer(
        examples["text"],
        truncation=True,
        max_length=2048,
        padding=False,
    )
    result["labels"] = result["input_ids"].copy()
    return result

dataset = dataset.map(tokenize, batched=True)

# ========== 5. 训练配置 ==========
training_args = TrainingArguments(
    output_dir="./qwen-lora-contract",
    per_device_train_batch_size=2,   # 显存不够就改成 1
    gradient_accumulation_steps=4,   # 4 步累加 = 等效 batch_size=8
    num_train_epochs=3,
    learning_rate=2e-4,
    fp16=True,
    logging_steps=10,
    save_steps=100,
    save_total_limit=2,
    warmup_ratio=0.05,
    lr_scheduler_type="cosine",
)

# ========== 6. 开始训练 ==========
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=dataset,
    data_collator=DataCollatorForSeq2Seq(tokenizer=tokenizer, padding=True),
)

trainer.train()

# ========== 7. 保存 LoRA 权重 ==========
model.save_pretrained("./qwen-lora-contract-final")
tokenizer.save_pretrained("./qwen-lora-contract-final")

显存不够怎么办

问题解法
OOM at batch_size=2改成 1
OOM at batch_size=1开 gradient_checkpointing
还是 OOM换 QLoRA(4-bit),或减小 max_length
还是 OOM换更小的模型(1.5B 或 3B)
python
# 开梯度检查点(省显存,稍慢)
model.gradient_checkpointing_enable()

# 减小序列长度
max_length=1024  # 从 2048 降到 1024

加载微调后的模型

python
from peft import PeftModel

# 加载基础模型
base_model = AutoModelForCausalLM.from_pretrained(
    "Qwen/Qwen2.5-7B-Instruct",
    torch_dtype=torch.float16,
    device_map="auto",
)

# 加载 LoRA 权重
model = PeftModel.from_pretrained(base_model, "./qwen-lora-contract-final")

# 推理
inputs = tokenizer("审查这份合同:...", return_tensors="pt").to("cuda")
outputs = model.generate(**inputs, max_new_tokens=512)
print(tokenizer.decode(outputs[0]))

合并 LoRA 到基座模型

想把 LoRA 权重合并到原模型(方便 Ollama/vLLM 部署):

python
merged_model = model.merge_and_unload()
merged_model.save_pretrained("./qwen-contract-merged")
tokenizer.save_pretrained("./qwen-contract-merged")

训练技巧

  • 学习率别太大:LoRA 推荐 1e-4 到 5e-4,全参数 1e-5 到 5e-5
  • epoch 别太多:3-5 个 epoch 够了,多了过拟合
  • 监控 loss:训练 loss 降、验证 loss 也降 → 正常;验证 loss 涨了 → 过拟合,停
  • 小步快跑:先用 200 条数据跑通流程,再放全量数据

🎯 本章要点

  • QLoRA = 4-bit 量化 + LoRA,16GB 显存就能微调 7B 模型
  • 完整流程:加载→配 LoRA→加载数据→tokenize→训练→保存→加载推理
  • OOM 四步解法:减 batch→开 checkpoint→降 max_length→换小模型
  • LoRA 权重只有几 MB,合并后才能给 Ollama/vLLM 部署
加载练习题中...

有问题或补充?欢迎留言