人工智能实战:LoRA 微调后效果不升反降?从数据清洗到训练参数的完整排查方案
一、问题场景:训练跑通了,但模型变笨了
很多团队在做大模型应用时,都会遇到这样的问题:
text
通用模型能用,但不够贴合业务。
于是开始尝试 LoRA 微调。
第一版训练通常很顺利:
text
数据准备好了
训练脚本跑起来了
loss 也下降了
模型也成功保存了
但一测试发现:
text
1. 模型回答变啰嗦
2. 原本会的问题不会了
3. 业务问题没有明显提升
4. 输出格式更不稳定
5. 有些答案明显过拟合训练样本
这就是 LoRA 微调里非常常见的问题:
text
训练成功 ≠ 效果变好。
这篇文章解决的问题是:
text
LoRA 微调后效果不升反降,如何从数据、参数、评测、合并、推理五个方面系统排查。
二、LoRA 适合解决什么问题?
LoRA 不是万能的。
适合:
text
1. 固定输出风格
2. 特定格式生成
3. 领域术语表达
4. 指令遵循增强
5. 小范围业务适配
不适合:
text
1. 给模型注入大量新知识
2. 替代 RAG
3. 修复基础推理能力
4. 让小模型突然具备大模型能力
如果你的目标是:
text
让模型记住公司所有制度
更适合:
text
RAG
如果你的目标是:
text
让模型按照固定格式输出审批意见
LoRA 更合适。
三、问题根因:大多数 LoRA 失败不是算法问题
LoRA 效果差,常见原因是:
text
1. 数据质量差
2. 数据量太少或太重复
3. 指令格式不统一
4. 学习率过高
5. 训练轮数过多
6. 没有评测集
7. 推理时 prompt 和训练格式不一致
其中最常见的是:
text
数据问题。
不是代码问题。
四、准备训练数据
推荐 JSONL 格式:
json
{"instruction": "请根据用户问题生成客服回复", "input": "用户说退款不到账怎么办?", "output": "您好,退款到账时间通常为1-3个工作日,请您先确认原支付渠道。"}
{"instruction": "请根据用户问题生成客服回复", "input": "用户说订单物流不更新怎么办?", "output": "您好,物流信息可能存在延迟,建议您等待24小时后再次查看。"}
每条样本必须包含:
text
instruction
input
output
不要混乱。
五、数据清洗脚本
python
import json
def clean_jsonl(input_path: str, output_path: str):
seen = set()
cleaned = []
with open(input_path, "r", encoding="utf-8") as f:
for line in f:
item = json.loads(line)
instruction = item.get("instruction", "").strip()
user_input = item.get("input", "").strip()
output = item.get("output", "").strip()
if not instruction or not output:
continue
key = instruction + user_input + output
if key in seen:
continue
seen.add(key)
if len(output) < 5:
continue
cleaned.append({
"instruction": instruction,
"input": user_input,
"output": output
})
with open(output_path, "w", encoding="utf-8") as f:
for item in cleaned:
f.write(json.dumps(item, ensure_ascii=False) + "\n")
print("cleaned:", len(cleaned))
clean_jsonl("train_raw.jsonl", "train_clean.jsonl")
清洗重点:
text
1. 去空样本
2. 去重复样本
3. 去过短输出
4. 统一字段格式
六、训练前必须拆分评测集
错误做法:
text
所有数据都拿去训练
正确做法:
text
train 90%
eval 10%
示例:
python
import json
import random
def split_dataset(path: str):
with open(path, "r", encoding="utf-8") as f:
data = [json.loads(line) for line in f]
random.shuffle(data)
split = int(len(data) * 0.9)
train = data[:split]
eval_data = data[split:]
for name, items in [("train.jsonl", train), ("eval.jsonl", eval_data)]:
with open(name, "w", encoding="utf-8") as f:
for item in items:
f.write(json.dumps(item, ensure_ascii=False) + "\n")
print("train:", len(train), "eval:", len(eval_data))
split_dataset("train_clean.jsonl")
七、训练参数建议
LoRA 常见参数:
text
r:8 / 16 / 32
lora_alpha:16 / 32
learning_rate:1e-4 ~ 2e-4
epochs:2~3
batch_size:根据显存
保守起步:
text
r=8
lora_alpha=16
learning_rate=1e-4
epochs=2
如果数据很少,不要训练太多轮。
否则很容易过拟合。
八、使用 PEFT 加载 LoRA
安装:
bash
pip install peft transformers accelerate
示例:
python
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import LoraConfig, get_peft_model
model_name = "Qwen/Qwen2.5-0.5B-Instruct"
tokenizer = AutoTokenizer.from_pretrained(
model_name,
trust_remote_code=True
)
model = AutoModelForCausalLM.from_pretrained(
model_name,
device_map="auto",
trust_remote_code=True
)
config = LoraConfig(
r=8,
lora_alpha=16,
target_modules=["q_proj", "v_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)
model = get_peft_model(model, config)
model.print_trainable_parameters()
九、训练格式必须和推理格式一致
训练时格式:
text
### 指令:
{instruction}
### 输入:
{input}
### 回答:
{output}
那么推理时也要保持:
python
def build_prompt(instruction: str, user_input: str):
return f"""
### 指令:
{instruction}
### 输入:
{user_input}
### 回答:
"""
如果训练和推理格式不一致,效果会明显下降。
十、评测不能只看 loss
loss 下降不代表业务效果提升。
必须准备固定测试问题:
python
eval_questions = [
"用户说退款一直没到账怎么办?",
"用户投诉物流不更新怎么回复?",
"用户要求人工客服怎么处理?"
]
对比:
text
base model 输出
LoRA model 输出
评估维度:
text
1. 是否符合业务语气
2. 是否包含关键信息
3. 是否编造规则
4. 是否格式稳定
5. 是否比原模型更好
十一、推理加载 LoRA
python
from transformers import AutoTokenizer, AutoModelForCausalLM
from peft import PeftModel
base_model = "Qwen/Qwen2.5-0.5B-Instruct"
lora_path = "./lora-output"
tokenizer = AutoTokenizer.from_pretrained(base_model, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
base_model,
device_map="auto",
trust_remote_code=True
)
model = PeftModel.from_pretrained(model, lora_path)
model.eval()
推理:
python
import torch
prompt = build_prompt(
"请根据用户问题生成客服回复",
"用户说退款一直没到账怎么办?"
)
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
with torch.inference_mode():
outputs = model.generate(
**inputs,
max_new_tokens=256,
temperature=0.3
)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
十二、踩坑记录
坑 1:用低质量数据训练
LoRA 会放大数据问题。
垃圾数据越多,模型越不稳定。
坑 2:训练轮数过多
数据量少时,epochs 太多会过拟合。
表现为:
text
模型总是输出训练集里的固定句式。
坑 3:学习率过高
学习率过高会破坏原模型能力。
建议从:
text
1e-4
开始。
坑 4:target_modules 随便填
不同模型结构不同。
不要照抄别人的 target_modules。
坑 5:不做基线对比
必须比较:
text
base model vs LoRA model
否则你不知道是否真的提升。
十三、适合收藏的 LoRA 排查 Checklist
text
目标:
[ ] 是否明确 LoRA 要解决的问题
[ ] 是否不把 LoRA 当知识库
[ ] 是否适合用微调解决
数据:
[ ] 是否去重
[ ] 是否清洗
[ ] 是否统一格式
[ ] 是否拆分评测集
[ ] 是否覆盖真实业务问题
训练:
[ ] 学习率是否合理
[ ] epoch 是否过多
[ ] r / alpha 是否保守
[ ] target_modules 是否正确
评测:
[ ] 是否和 base model 对比
[ ] 是否有固定测试集
[ ] 是否人工评估业务效果
[ ] 是否检查幻觉和格式
上线:
[ ] 推理 prompt 是否和训练一致
[ ] 是否保留回滚方案
[ ] 是否灰度测试
十四、经验总结
LoRA 微调不是"把数据扔进去训练一下"。
真正决定效果的是:
text
1. 数据质量
2. 任务边界
3. 训练格式
4. 参数控制
5. 评测体系
一句话总结:
text
LoRA 不是让模型学更多知识,而是让模型更稳定地执行某类任务。
十五、优化建议
后续可以继续做:
text
1. 增加高质量人工标注数据
2. 构建自动评测集
3. 对 badcase 做定向补充
4. 尝试不同 r 和 learning_rate
5. 使用 DPO 优化偏好输出
6. 将 LoRA 与 RAG 结合
7. 灰度上线对比真实用户反馈
最后一句经验:
text
微调最怕的不是训练失败,而是训练成功后你以为它变好了。