跳到正文

LoRA 微调实战:从数据清洗到参数选择的完整记录

单卡 24G 把 7B 模型微调出可用效果的全过程。含数据格式、超参选择、显存对照表和效果评估方法。

1168 字约 3 分钟直达下载 ↓
本文目录(7)

微调最花时间的部分不是训练,是准备数据。我这次 3 天的活里,2.5 天在洗数据、调格式、抽检效果,真正跑训练的只有几个小时。

这篇按实际顺序记录一遍,从数据格式开始。

数据格式:先定好再动手

LLaMA-Factory 和大多数框架都吃 Alpaca 或 ShareGPT 格式。我用 Alpaca,因为它最直白:

[
  {
    "instruction": "把下面这段技术文档改写成给非技术人员看的说明",
    "input": "KV Cache 是 Transformer 推理时缓存键值对以复用计算结果的技术……",
    "output": "大模型每次说话都要重新算一遍之前所有内容,很慢。KV Cache 就是把算过的存起来,下次直接用。"
  }
]

有个容易忽略的点:input 为空时不要留 "",直接省略这个字段。某些版本的模板会把空 input 也拼进 prompt,多出一段奇怪的换行,进而影响训练。

数据清洗,我实际做的六件事

  1. 去重(精确 + 近似)。用 SimHash 做近似去重,阈值设 0.9。这一步删掉了 12% 的数据
  2. 长度过滤。丢掉 output 短于 20 字或长于 2000 字的样本。太短的学不到东西,太长的多半是复制粘贴的原始文档
  3. 格式校验。检查是否所有样本都能被 chat template 正确渲染,报错的直接丢
  4. 语言一致性。如果 instruct 是中文而 output 是英文,多半是抓取错误
  5. 去除模板化开头。删掉大量以「当然可以!」「这是一个很好的问题」开头的回答 —— 这正是你不想让模型学会的东西
  6. 人工抽检 100 条。这一步不能省,我每次都靠它发现新问题

关键超参怎么选

这是我试出来的最优区间,附上理由:

参数推荐值理由
lora_rank168 太弱学不动风格,32 在 3k 数据上开始过拟合
lora_alpha32惯例取 rank 的 2 倍
lora_dropout0.05小数据集需要一点正则
learning_rate2e-4LoRA 的标准区间,1e-4~3e-4
lr_schedulercosine比 linear 收敛更稳
warmup_ratio0.03避免开头几步把权重带偏
num_epochs3超过 3 轮基本就是背数据
cutoff_len2048覆盖 95% 的样本长度
batch_size × grad_accum2 × 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,值得。

LoRA 结构示意:低秩矩阵挂在原始权重旁
LoRA 结构示意:低秩矩阵挂在原始权重旁

显存对照表

单卡 4090 24 GB,7B 模型,BF16 基座 + LoRA:

配置显存占用能否跑
全参微调> 80 GB✗
LoRA + batch 1 × accum 1614.2 GB✓ 很宽裕
LoRA + batch 2 × accum 817.8 GB✓ 推荐
LoRA + batch 4 × accum 423.1 GB△ 接近上限
QLoRA(4-bit 基座)+ batch 411.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 个入口

链接若失效,欢迎发邮件告诉我,我会尽快重新上传。