LoRA 微调 Qwen3.5-9B:四轮实战参数调优攻略
基于真实训练日志提炼的"决策树"式调参指南
一、四轮实验全景总览
| 轮次 | 学习率 | Epochs | 数据量/份 | 梯度裁剪 | Warmup | 最终 Loss | 最佳 Loss | Token Acc | 结论 |
|---|---|---|---|---|---|---|---|---|---|
| R1 | 1e-4 | 1 | #100 | ❌ | 0.05 | 1.38 | 1.22 | 67% | ❌ 梯度爆炸 |
| R2 | 2e-5 | 3 | #100 | ✅ 1.0 | 0.1 | 1.33 | 1.19 | 68% | ⚠️ 学不动 |
| R3 | 5e-5 | 3 | #200 | ✅ 1.0 | 0.1 | 1.14 | 0.897 | 73.6% | ✅ 最优 |
| R4 | 3.5e-5 | 4 | #200 | ✅ 1.0 | 0.1 | 1.14 | 0.911 | 73.6% | ➖ 边际递减 |
二、逐轮深度拆解:每轮改了什么?为什么改?结果如何?
🔴 第一轮:基线测试(踩坑)
训练命令关键参数:
--learning_rate 1e-4
--num_train_epochs 1
--dataset #100
--warmup_ratio 0.05
# 无 gradient_clipping
日志核心数据:
| Step | Loss | grad_norm | Token Acc | 学习率 |
|---|---|---|---|---|
| 1 | 1.23 | NaN | 68.1% | 1e-4 |
| 5 | 1.47 | NaN | 65.4% | 8.83e-5 |
| 10 | 1.54 | 1.91 | 64.1% | 5e-5 |
| 15 | 1.33 | 0.67 | 66.1% | 1.17e-5 |
| 19 | 1.38 | - | 67.0% | - |
❌ 问题诊断:
-
梯度爆炸:Step 1 和 Step 5 的
grad_norm: NaN,原因是学习率 1e-4 对 LoRA 来说太高 + 没有梯度裁剪保护,FP16 精度下数值溢出。 -
严重欠拟合:只跑了 1 个 Epoch(19 步),模型刚看完一遍数据就停了,Loss 停留在 1.38 高位,Token Acc 只有 67%。
-
预热不足:
warmup_ratio=0.05只有约 1 步预热,完全不足以让模型平稳起步。
✅ 学到的教训:
-
必须加
--gradient_clipping 1.0(护身符) -
至少跑 3 个 Epoch(1 轮纯属浪费钱)
-
提高
--warmup_ratio到 0.1
🟡 第二轮:矫枉过正(保守治疗)
训练命令关键修改:
--learning_rate 2e-5 # 从 1e-4 降到 1/5
--num_train_epochs 3 # 从 1 升到 3
--warmup_ratio 0.1 # 从 0.05 升到 0.1
--gradient_clipping 1.0 # 新增!
--torch_dtype bfloat16 # 从 float16 改为 bfloat16
日志核心数据:
| Step | Loss | grad_norm | Token Acc | 学习率 |
|---|---|---|---|---|
| 1 | 1.23 | 1.42 ✅ | 68.1% | 3.33e-6 |
| 20 | 1.27 | 0.69 | 67.5% | 1.65e-5 |
| 30 | 1.32 | 0.64 | 65.9% | 1.09e-5 |
| 40 | 1.24 | 0.71 | 67.5% | 5e-6 |
| 57 | 1.33 | - | 65.1% | - |
⚠️ 问题诊断:
-
梯度爆炸已彻底治愈:Step 1 的
grad_norm从NaN变成了健康的1.42,证明梯度裁剪 + 降学习率 + 预热组合拳有效。 -
学习率太低:2e-5 虽然安全,但模型"学不动"。Loss 在 1.2~1.3 区间徘徊了整整 3 个 Epoch,就是跌不破 1.2。
-
数据量太少:#100 每份只有 300 条总数据,模型 3 轮看完也没学到足够丰富的表达。
✅ 学到的教训:
-
梯度裁剪治好了 NaN,但学习率需要往上提
-
数据量要加大:#100 → #200 起步
🟢 第三轮:黄金交点(最优突破)
训练命令关键修改:
--learning_rate 5e-5 # 从 2e-5 升到 5e-5
--num_train_epochs 3 # 保持 3
--dataset #200 # 从 #100 升到 #200
--gradient_clipping 1.0 # 保留
--warmup_ratio 0.1 # 保留
日志核心数据:
| Step | Loss | grad_norm | Token Acc | 学习率 | 备注 |
|---|---|---|---|---|---|
| 1 | 1.50 | 1.60 ✅ | 63.2% | 4.17e-6 | 起步平稳 |
| 30 | 1.12 | 0.60 | 69.2% | 4.63e-5 | 首次跌破 1.2 |
| 60 | 1.04 | 0.69 | 70.4% | 2.73e-5 | 持续下降 |
| 105 | 0.897 | 0.85 | 73.6% | 9.5e-7 | 全局最优! |
| 114 | 1.14 | - | 72.7% | - | 最终 |
✅ 成功诊断:
-
学习率 5e-5 是 LoRA 的"甜点区":既不会爆炸(有梯度裁剪兜底),又能让模型有足够的"动力"去优化。
-
数据量 #200(总计 600 条)刚好让模型吃饱:3 个 Epoch 的步数从 57 步增加到 114 步,给了模型充分的迭代空间。
-
最佳 Checkpoint 出现在总步数的 92% 处(105/114):说明模型在第 3 轮中后期达到最优,最后几步略有波动(学习率衰减到接近 0)。
-
Token Acc 首次突破 70%,达到 73.6%:这是从"及格"到"优良"的分水岭。
✅ 策略启示:
-
3 个 Epoch + 5e-5 + #200 = 黄金组合
-
不要只看最终 Checkpoint,去日志里找 Loss 最低的那一行
🔵 第四轮:探索极限(边际递减)
训练命令关键修改:
--learning_rate 3.5e-5 # 从 5e-5 降到 3.5e-5
--num_train_epochs 4 # 从 3 升到 4
# 其他全部保持不变
日志核心数据:
| Step | Loss | grad_norm | Token Acc | 学习率 | 备注 |
|---|---|---|---|---|---|
| 105 | 0.911 | 0.91 | 73.3% | 9.34e-6 | 全局最优(接近 R3 的 0.897) |
| 152 | 1.14 | - | 73.6% | - | 最终 |
➖ 问题诊断:
-
边际收益递减:多跑 1 个 Epoch(+38 步)只把最终 Acc 从 72.7% 提到了 73.6%,但 Loss 没改善。
-
学习率 3.5e-5 偏保守:虽然全程稳定,但收敛速度比 5e-5 慢,到 Step 105 才达到 R3 Step 105 的水平(0.911 vs 0.897),略逊一筹。
-
4 个 Epoch 没有额外收益:模型在 600 条数据上的"学习能力"已经饱和,再多的 Epoch 也只是在重复记忆。
✅ 策略启示:
-
3 个 Epoch 是最优性价比,4 个不划算(耗时 +33%,收益几乎为 0)
-
学习率 5e-5 优于 3.5e-5:收敛更快,最优 Loss 更低
三、核心结论:参数决策树
🎯 学习率选择决策树
训练起步
│
├─ 加上 gradient_clipping=1.0 + warmup_ratio=0.1
│
├─ 从 5e-5 开始(LoRA 黄金起点)
│
├─ 观察 Step 1~5 的 grad_norm
│ ├─ 如果是 NaN → 降到 3e-5,重跑
│ └─ 如果是正常数字(0.5~2.0)→ 继续
│
└─ 观察 Epoch 1 结束时(总步数/3)的 Loss
├─ 如果 Loss < 1.2 → ✅ 保持 5e-5,完美
├─ 如果 Loss > 1.3 → 学习率太低,提到 8e-5
└─ 如果 Loss 震荡剧烈 → 学习率太高,降到 3e-5
🎯 Epoch 数量选择决策树
训练启动(learning_rate=5e-5,数据量#200)
│
├─ 观察第 3 轮中后期(约总步数 70%~80%)的 Loss
│ ├─ 如果 Loss 仍在明显下降(每 5 步降 > 0.05)→ 需要第 4 轮
│ └─ 如果 Loss 已趋平或开始反弹 → 第 3 轮就是最优
│
└─ 你的数据证明:3 轮刚好,4 轮边际递减
🎯 数据量选择决策树
数据量决定模型能学多“宽”
│
├─ #100(300 条总数据)→ 只能死记硬背,Acc ≤ 68%
├─ #200(600 条总数据)→ 学会基础表达,Acc 73%~75% ✅ 你在这里
├─ #500(1500 条总数据)→ 泛化能力增强,Acc 有望 78%~80%
└─ 注意:数据量增加 → 步数增加 → 训练时间线性增长
四、"三步调参法"------小白直接套用
第一步:定底盘(不管什么模型都加上)
--gradient_clipping 1.0
--warmup_ratio 0.1
--torch_dtype bfloat16 # 或 float16
--target_modules all-linear
--gradient_accumulation_steps 16
第二步:定起点(推荐起始值)
--learning_rate 5e-5
--num_train_epochs 3
--dataset #200
第三步:看日志调优
| 日志现象 | 对策 |
|---|---|
| Step 1 的 grad_norm = NaN | 降到 learning_rate=3e-5 或确认 gradient_clipping=1.0 已加 |
| Epoch 1 结束 Loss > 1.3 | 升 learning_rate 到 8e-5 |
| Epoch 2 结束 Loss > 1.0 | 升数据量到 #500 |
| Epoch 3 结束 Loss < 0.6 | 过拟合了!降 Epoch 到 2 |
| 最佳 Loss 出现在 70%~80% 处 | 就用那个 Checkpoint,不要用最后一步 |
五、最终验收标准
基于实际的四轮数据,可以得出"合格/优良/优秀"三级标准:
| 等级 | Loss(最低点) | Token Acc | 对话表现 |
|---|---|---|---|
| 合格 | ≤ 1.2 | ≥ 68% | 能正确自我介绍,偶尔语序颠倒 |
| 优良 | ≤ 1.0 | ≥ 72% | 流畅自我介绍,通用问答逻辑通顺 |
| 优秀 | ≤ 0.8 | ≥ 78% | 表达自然,能处理多种问法变体 |