为什么需要微调
微调(Fine-tuning)是在预训练模型基础上用特定数据集继续训练,使其适配特定任务或领域。与 RAG 相比,微调的优势在于:
- 模型真正”学会”了领域知识,推理时无需外部检索
- 可以改变模型的行为风格和输出格式
- 适合需要深度理解专业术语的场景
参数高效微调(PEFT)
传统全参数微调需要更新模型的所有权重,对计算资源要求极高。参数高效微调通过在模型中插入少量可训练参数,以极小代价实现接近全参数微调的效果。
LoRA(Low-Rank Adaptation)
LoRA 是 PEFT 中最主流的方法。其原理是在 Transformer 的注意力层权重矩阵旁插入低秩分解矩阵,训练时只更新这些低秩矩阵。
from peft import LoraConfig, get_peft_model
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-7B")
lora_config = LoraConfig(
r=16, # 秩(rank),越大学习能力越强
lora_alpha=32, # 缩放系数
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)
model = get_peft_model(model, lora_config)
print(f"可训练参数占比:{model.num_parameters(only_trainable=True) / model.num_parameters():.2%}")
常见 LoRA 参数配置:
| 场景 | r | lora_alpha | target_modules |
|---|---|---|---|
| 聊天风格调整 | 8 | 16 | q_proj, v_proj |
| 代码任务 | 16 | 32 | 所有注意力层 |
| 领域知识注入 | 32 | 64 | 所有线性层 |
| 数学推理 | 16 | 32 | qkv + gate/up/down |
QLoRA(量化 LoRA)
QLoRA 在 LoRA 的基础上引入 4-bit 量化,可以将 7B 模型的微调显存需求从 ~56GB 降低到 ~16GB,使单卡消费级 GPU 也能微调大模型。
from transformers import BitsAndBytesConfig
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16,
bnb_4bit_use_double_quant=True
)
model = AutoModelForCausalLM.from_pretrained(
"Qwen/Qwen2.5-7B",
quantization_config=bnb_config,
device_map="auto"
)
数据集准备
微调数据的质量直接决定最终效果。常见的数据格式:
对话格式(ChatML)
[
{
"conversations": [
{"role": "system", "content": "你是一个资深的金融分析师。"},
{"role": "user", "content": "解释一下什么是 IRR?"},
{"role": "assistant", "content": "内部收益率(IRR)是使项目净现值为零的贴现率..."}
]
}
]
指令格式
[
{
"instruction": "翻译以下句子为英文",
"input": "机器学习正在改变世界。",
"output": "Machine learning is changing the world."
}
]
数据质量要求
- 多样性:覆盖目标场景的不同子任务
- 正确性:每条数据经过人工审核
- 一致性:风格和格式统一
- 平衡性:各类别数据量大致平衡
- 去重:删除完全重复或高度相似的样本
训练脚本
from transformers import TrainingArguments, Trainer
from datasets import load_dataset
dataset = load_dataset("json", data_files="train.jsonl")
training_args = TrainingArguments(
output_dir="./qwen-lora",
num_train_epochs=3,
per_device_train_batch_size=4,
gradient_accumulation_steps=8,
learning_rate=2e-4,
warmup_ratio=0.03,
lr_scheduler_type="cosine",
logging_steps=10,
save_strategy="steps",
save_steps=200,
fp16=True,
gradient_checkpointing=True,
report_to="wandb"
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=dataset,
tokenizer=tokenizer,
data_collator=data_collator
)
trainer.train()
关键训练参数建议:
- 学习率:LoRA 通常用 1e-4 到 5e-4,比全参数微调高一个数量级
- Epoch 数:3-5 个 epoch 通常足够,过多会导致灾难性遗忘
- Batch size:在显存允许的范围内尽量大,或用梯度累积模拟
- Warmup:前 3% 的 step 做 warmup,稳定训练过程
模型评估
微调后的评估应当从多个维度展开:
自动化评估
from datasets import load_metric
# 在标准基准上测试
bleu = load_metric("bleu")
rouge = load_metric("rouge")
predictions = generate_predictions(model, test_dataset)
result = rouge.compute(
predictions=predictions,
references=test_dataset["output"]
)
人工评估
自动化指标无法完全反映生成质量。建议建立包括以下维度的人工评估体系:
- 正确性:是否存在事实错误
- 相关性:回答是否针对用户问题
- 流畅性:语言是否自然
- 指令遵循:是否遵守了格式要求
- 安全性:是否有有害内容
对比测试
微调后务必在原始能力的通用 benchmark(如 MMLU、GSM8K)上测试,确保没有灾难性遗忘。
微调 vs RAG
| 维度 | 微调 | RAG |
|---|---|---|
| 知识时效性 | 依赖训练数据 | 可实时更新 |
| 幻觉控制 | 可能记忆错误 | 有检索支撑 |
| 推理成本 | 无额外检索开销 | 每次都要检索 |
| 适用场景 | 深度领域适配 | 知识密集型问答 |