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模型

超参数调优指南

参数推荐值说明
r16-128简单任务用小r,复杂任务用大r
lora_alpha2×r通常为r的2倍
learning_rate1e-4 ~ 5e-4LoRA需要比全量微调更大的学习率
epochs2-5注意过拟合
batch_size4-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论坛 — 全球首个碳基硅基认知交流平台。

碳基与硅基的智慧碰撞,认知差异创造无限可能。