LoRA 微调实战:从数据清洗到参数选择的完整记录
单卡 24G 把 7B 模型微调出可用效果的全过程。含数据格式、超参选择、显存对照表和效果评估方法。
微调最花时间的部分不是训练,是准备数据。我这次 3 天的活里,2.5 天在洗数据、调格式、抽检效果,真正跑训练的只有几个小时。
这篇按实际顺序记录一遍,从数据格式开始。
数据格式:先定好再动手
LLaMA-Factory 和大多数框架都吃 Alpaca 或 ShareGPT 格式。我用 Alpaca,因为它最直白:
[
{
"instruction": "把下面这段技术文档改写成给非技术人员看的说明",
"input": "KV Cache 是 Transformer 推理时缓存键值对以复用计算结果的技术……",
"output": "大模型每次说话都要重新算一遍之前所有内容,很慢。KV Cache 就是把算过的存起来,下次直接用。"
}
]
有个容易忽略的点:input 为空时不要留 "",直接省略这个字段。某些版本的模板会把空 input 也拼进 prompt,多出一段奇怪的换行,进而影响训练。
数据清洗,我实际做的六件事
- 去重(精确 + 近似)。用 SimHash 做近似去重,阈值设 0.9。这一步删掉了 12% 的数据
- 长度过滤。丢掉 output 短于 20 字或长于 2000 字的样本。太短的学不到东西,太长的多半是复制粘贴的原始文档
- 格式校验。检查是否所有样本都能被 chat template 正确渲染,报错的直接丢
- 语言一致性。如果 instruct 是中文而 output 是英文,多半是抓取错误
- 去除模板化开头。删掉大量以「当然可以!」「这是一个很好的问题」开头的回答 —— 这正是你不想让模型学会的东西
- 人工抽检 100 条。这一步不能省,我每次都靠它发现新问题
关键超参怎么选
这是我试出来的最优区间,附上理由:
| 参数 | 推荐值 | 理由 |
|---|---|---|
lora_rank | 16 | 8 太弱学不动风格,32 在 3k 数据上开始过拟合 |
lora_alpha | 32 | 惯例取 rank 的 2 倍 |
lora_dropout | 0.05 | 小数据集需要一点正则 |
learning_rate | 2e-4 | LoRA 的标准区间,1e-4~3e-4 |
lr_scheduler | cosine | 比 linear 收敛更稳 |
warmup_ratio | 0.03 | 避免开头几步把权重带偏 |
num_epochs | 3 | 超过 3 轮基本就是背数据 |
cutoff_len | 2048 | 覆盖 95% 的样本长度 |
batch_size × grad_accum | 2 × 8 | 等效 batch 16,24G 显存够用 |
目标模块的选择比很多人想的更重要:
# 只挂 attention(省显存,效果一般)
target_modules = ["q_proj", "v_proj"]
# 全挂(推荐,显存多占一点但效果明显更好)
target_modules = ["q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj"]
我一开始只挂 q_proj/v_proj,训练损失降得很好看,但实际推理时风格迁移很弱。改成全挂之后,同样数据量的效果提升很明显。多出来的显存开销大概 1.5 GB,值得。
显存对照表
单卡 4090 24 GB,7B 模型,BF16 基座 + LoRA:
| 配置 | 显存占用 | 能否跑 |
|---|---|---|
| 全参微调 | > 80 GB | ✗ |
| LoRA + batch 1 × accum 16 | 14.2 GB | ✓ 很宽裕 |
| LoRA + batch 2 × accum 8 | 17.8 GB | ✓ 推荐 |
| LoRA + batch 4 × accum 4 | 23.1 GB | △ 接近上限 |
| QLoRA(4-bit 基座)+ batch 4 | 11.6 GB | ✓ 最省,但慢 40% |
QLoRA 省显存但慢,只有在显存真的不够时才用。24 GB 卡跑 7B 的 LoRA 是完全够的,没必要上 QLoRA。
训练命令
llamafactory-cli train \
--model_name_or_path Qwen/Qwen2.5-7B-Instruct \
--stage sft \
--do_train \
--dataset my_dataset \
--template qwen \
--finetuning_type lora \
--lora_rank 16 \
--lora_alpha 32 \
--lora_target all \
--cutoff_len 2048 \
--per_device_train_batch_size 2 \
--gradient_accumulation_steps 8 \
--learning_rate 2e-4 \
--lr_scheduler_type cosine \
--warmup_ratio 0.03 \
--num_train_epochs 3 \
--bf16 true \
--gradient_checkpointing true \
--output_dir ./out \
--logging_steps 10 \
--save_steps 200
--gradient_checkpointing true 用时间换显存,能省 30% 左右,长序列时几乎是必开项。
怎么判断有没有效
损失曲线只能说明模型在拟合训练集,不能说明它变好了。我固定用三个办法:
第一,留出集上的对照。 从数据里切 5% 出来不参与训练,训练完对比基座模型和微调模型在留出集上的表现。如果微调后在留出集上反而变差,就是过拟合。
第二,固定 prompt 的人工盲测。 准备 20 个典型请求,把两个模型的输出随机打乱顺序,自己盲评。这一步最花时间但最能说明问题。
第三,回归测试。 挑 10 个和微调目标无关的通用问题(比如简单数学、常识问答),确认模型没有因为这些数据而「变傻」。
最后
如果只记一句话:先把数据弄干净,再考虑调参。 参数带来的差异通常在 10% 量级,数据质量带来的差异是数倍。
配置、清洗脚本和一个 3k 条的示例数据集都在下载区。
资源下载
3 个入口- 夸克网盘同上,备用链接提取码5x9t
链接若失效,欢迎发邮件告诉我,我会尽快重新上传。