2.2 训练循环里的超参数与踩坑:学习率、轮次、批次到底怎么设


文档摘要

2.2 训练循环里的超参数与踩坑:学习率、轮次、批次到底怎么设 读者读完这一节,应该建立一套「先保守后微调」的超参决策框架:知道每个旋钮的默认值、调节方向和常见翻车点,而不是一上来就把 rank 拉满、学习率调到最小,然后看着不动的 loss 怀疑人生。 2.2.1 先建立心智模型:LoRA 只有「少量参数在动」 为什么 LoRA 的学习率能比全量微调大一个数量级?因为地貌不同。全量微调要挪动整座山(所有参数一起更新),步子一大就塌方,所以学习率通常在 。LoRA 只微调一座小土丘(适配器里的低秩矩阵),优化面平坦得多,步子可以大些,常用 。 记住这个类比,下面所有超参的调节方向就都有了直觉依据:你在调一个很小的目标,所以可以更激进,但小目标也更容易被一步带崩。 2.2.

2.2 训练循环里的超参数与踩坑:学习率、轮次、批次到底怎么设

读者读完这一节,应该建立一套**「先保守后微调」的超参决策框架**:知道每个旋钮的默认值、调节方向和常见翻车点,而不是一上来就把 rank 拉满、学习率调到最小,然后看着不动的 loss 怀疑人生。

2.2.1 先建立心智模型:LoRA 只有「少量参数在动」

为什么 LoRA 的学习率能比全量微调大一个数量级?因为地貌不同。全量微调要挪动整座山(所有参数一起更新),步子一大就塌方,所以学习率通常在 1e-5 ~ 2e-5。LoRA 只微调一座小土丘(适配器里的低秩矩阵),优化面平坦得多,步子可以大些,常用 1e-4 ~ 3e-4

记住这个类比,下面所有超参的调节方向就都有了直觉依据:你在调一个很小的目标,所以可以更激进,但小目标也更容易被一步带崩

```mermaid graph LR A[学习率过小] --> B[loss 纹丝不动
假装在学] C[学习率适中] --> D[loss 平滑下降
健康] E[学习率过大] --> F[loss 爆炸 NaN
训练崩] ```

2.2.2 学习率:最该先调的旋钮

学习率是我建议第一个动手的参数,因为它对结果的影响最直接、最可见。

  • 稳健默认2e-4。这是 LoRA 的平衡点,多数任务开箱即用。
  • 与 rank 的关系:rank 越大(适配器容量越大),可稍微降低学习率,避免大容量下的更新过冲;rank 小则可维持偏大。
  • 调度与预热:用 warmup_ratio=0.03~0.1 让开头几步别太猛;调度器常用 cosinelinear
  • 关闭远程日志先report_to="none",本地看 logging 最直观,等调稳了再接可视化工具。

下面是「学习率诊断表」,建议你贴在屏幕上:

现象 可能原因 对策
loss 直接变 NaN/Inf 学习率过大,或量化 dtype 不对 降到 1e-4,确认 bnb_4bit_compute_dtype=bf16
loss 一直线、不降 学习率过小,或适配器没接上 提到 2e-4;查 print_trainable_parameters 占比
loss 降但生成乱码 数据/掩码问题,非纯学习率 回到 2.1.4 检查 labels 掩码
早期震荡剧烈 warmup 太短 / batch 太小 warmup_ratio,或加大等效 batch

2.2.3 训练轮次(epoch):不是越多越好

很多人的直觉是「多训几轮更准」,在 LoRA 上这常常适得其反。LoRA 在小数据集上 1~3 个 epoch 通常就够,因为可训练参数少,模型很快就能「记住」适配器该做什么。

判断过拟合的唯一靠谱标准是盯 eval loss 的拐点

  • 训练 loss 一路降,eval loss 也跟着降 → 还在学,继续。
  • 训练 loss 还在降,eval loss 开始抬头 → 过拟合了,停在这里或回退。

小数据集(几百条)有时 1~2 个 epoch 就到拐点。一个实用纪律:先设 num_train_epochs=3,配合 eval_strategy="steps" 看 eval 曲线,谁先到拐点听谁的,而不是硬跑满

2.2.4 批次与梯度累积:显存不够就靠它

per_device_train_batch_size 直接被显存卡死:一张卡放不下大 batch,就调小它,同时加大 gradient_accumulation_steps,让「等效 batch」维持稳定。

等效 batch 的公式:

等效 batch = per_device_train_batch_size × gradient_accumulation_steps × GPU 数量

举例:单卡显存只够 batch_size=2,想要等效 16,就设 gradient_accumulation_steps=8(2×8×1=16)。注意两个极端都要避免:

  • 等效 batch 太小 → 梯度噪声大、训练不稳、loss 震荡。
  • 等效 batch 太大 → 显存爆、单步慢、收敛反而拖沓。

经验区间:等效 batch 在 8~32 对大多数 LoRA 任务都安全。显存实在紧张时,QLoRA + 小 batch + 大累积步数是标准解法。

2.2.5 r 与 alpha 的再审视(接 2.1)

LoraConfigrlora_alpha 决定了适配器的「容量」与「更新幅度」:

  • r(秩):低秩矩阵的维度,8 / 16 / 32 / 64 常见。r 越大,适配器能表达的变换越复杂,但参数更多、过拟合风险更高、显存占用略增。
  • lora_alpha:缩放系数,有效增量 = (alpha / r) × B@A。所以真正起作用的是 alpha 与 r 的比值
  • 常见稳妥组合:r=16, alpha=32(比值 2),或 r=8, alpha=16

踩坑点:

  • r 设太大:参数增多、收益递减,在小数据上极易过拟合——别迷信「越大越好」。
  • alpha 相对 r 过大:有效更新幅度过大,训练容易发散(loss 乱跳)。
  • alpha 相对 r 过小:更新太弱,等于没训。

容量选择决策:数据量小、任务简单 → r=8;数据量多、任务复杂(如专业术语密集的法律/医疗)→ r=32 起。这和第 4 章的案例会呼应。

2.2.6 调度、梯度裁剪与权重衰减

除了前面几个大头,还有几个「稳训练」的旋钮:

  • lr_scheduler_typecosine 最常用,后期自动收敛;linear 也稳妥。
  • warmup_ratio0.03~0.1,开头几步用小学习率探路,防第一步炸。
  • max_grad_norm:梯度裁剪,默认 1.0。它的作用是把单步梯度范数截断,防止极端 batch 把更新带崩。一般不用动;若仍 NaN,先降 lr 而非盲目调它。
  • weight_decay:权重衰减(L2 正则)。LoRA 因为只训少量参数,常设 0 或很小(如 0.0);全量微调才更需要它。

一份「稳健超参模板」长这样:

from transformers import TrainingArguments args = TrainingArguments( output_dir="./ckpt", per_device_train_batch_size=2, gradient_accumulation_steps=8, # 等效 batch = 16 num_train_epochs=3, learning_rate=2e-4, # LoRA 默认 lr_scheduler_type="cosine", warmup_ratio=0.03, max_grad_norm=1.0, weight_decay=0.0, # LoRA 一般不开 bf16=True, logging_steps=10, eval_strategy="steps", eval_steps=50, save_strategy="steps", save_steps=200, report_to="none", save_total_limit=2, )

2.2.7 综合调参路线图:先保守,后微调

把前面所有旋钮串成一条可执行的调参路线,而不是凭感觉乱拧:

```mermaid graph TD A[用默认稳健超参先跑通] --> B{loss 在降且稳定?} B -- 否: 爆炸/NaN --> C[降学习率到 1e-4
查量化 dtype] B -- 否: 不动 --> D[提学习率到 3e-4
查适配器是否接上] B -- 是 --> E{eval loss 也降?} E -- 否: 过拟合 --> F[减 epoch / 加正则 / 降 r] E -- 是 --> G{容量够吗?} G -- 欠拟合 --> H[加 r 到 32 / 加 target 层] G -- 够 --> I[收敛, 收工] ```

核心纪律只有一句:先跑通(用保守默认),看信号,再微调。绝大多数「调参焦虑」来自一上来就堆最大 rank、最小 lr、最多 epoch——结果哪个方向都看不出问题。下表是速查:

参数 稳健默认 常见范围 调节方向
learning_rate 2e-4 1e-4 ~ 3e-4 不稳↓,不动↑
num_train_epochs 3 1 ~ 5 过拟合↓,欠拟合↑
per_device_batch_size 2 1 ~ 8 显存允许就大
gradient_accumulation_steps 8 1 ~ 32 补等效 batch
r 16 8 ~ 64 容量不足↑
lora_alpha 32 r 的 1~2 倍 随 r 等比例
warmup_ratio 0.03 0.03 ~ 0.1 震荡就加
weight_decay 0.0 0 ~ 0.01 LoRA 通常 0

2.2.8 训练中途的决策:早停、继续还是回退

超参设好、开跑之后,真正的功夫在「中途怎么看」。我建议你每 50~100 个 step 做一次决策复盘,而不是闷头跑满三圈:

  • 继续的信号:训练 loss 持续下降,eval loss 同步下降(哪怕很慢),生成质量肉眼可见变好。说明还在有效学习区,安心跑。
  • 早停的信号:eval loss 连续两轮(或两个检查点)上升,而训练 loss 仍在降。这是教科书式过拟合,立刻停,回退到 eval loss 最低的那个 checkpoint——那个点才是你「最会做题」的模型。
  • 回退的用法:QLoRA/LoRA 训练很便宜,每 200 step 存一个 save_total_limit=2 的检查点。过拟合时别纠结,直接加载前一个更稳的适配器即可,这比硬撑着跑更有价值。
  • 重训的信号:如果训练 loss 卡住不动超过 200 step,且学习率已在合理区间,说明数据或适配器容量有问题,该回到 2.1 的数据管线和 2.2.5 的 r/alpha 重新诊断,而不是无限加 epoch。

一句话:epoch 是上限不是目标,eval 拐点才是终点。下面这张图把三种中途信号和对应动作固化成肌肉记忆:

```mermaid graph TD A[每 50~100 step 复盘] --> B{训练与 eval loss 同降?} B -- 是 --> C[继续跑] B -- 否: eval 升/训练降 --> D[早停 回退到最优 ckpt] B -- 否: 训练不动 --> E[停 重诊数据/容量] C --> F{人审抽测变好?} F -- 是 --> G[毕业] F -- 否 --> E ```

2.2.9 别只盯着 loss:困惑度与人审兜底

loss 会骗人。最经典的骗局是:loss 降得很漂亮,模型却只是把训练集背下来了。所以我强烈建议 loss 之外再加两道保险:

  • 困惑度(Perplexity):在独立验证集上算困惑度,它比单纯的 loss 更直觉(越低越好),但局限是只反映「像不像训练分布」,不反映「答得对不对」。
  • 人审抽测:准备 20 条以上的领域问答样本,对比微调前后的回答。量化标准可以是「能用一句话说出正确答案的比例」从 X% 提升到 Y%。这一步无法自动化,却最能代表业务价值。
  • 小测集打分:把你的真实场景浓缩成一份带标准答案的「小测」,每次出 checkpoint 都跑一遍打分,把分数画成曲线——这条曲线比 loss 更贴近「模型到底变聪明没有」。

2.2.10 三类垂直领域的超参起点建议

不同领域「容错率」和「术语密度」不同,起点就该不一样。下面是我给的常见起点(记住:这是起点,不是终点,要用 2.2.7 的路线图再去微调):

领域 特点 r lora_alpha epoch lr
医疗 / 法律 术语密集、容错极低 32 64 2~3 1e-4(更稳)
客服对话 句式固定、容错中等 16 32 3 2e-4
风格 / 角色 轻量风格迁移 8 16 1~2 3e-4

背后的逻辑是:越怕出错、术语越多,越要小学习率 + 大容量(r 大),让模型小心地吸收专业知识;越偏风格、越不怕错,越可以大学习率 + 小容量,快速贴近目标语气。GLM-5.2 作为通用底座,在医疗法律这类高专业度任务上尤其建议先把 lr 压到 1e-4 起步,避免把底座里好不容易学到的常识带崩。

2.2.11 显存进阶:多卡与 DeepSpeed 该怎么选

当你手上不止一张卡,或单卡 QLoRA 都还差一点显存时,就该上分布式了。这里只讲决策,不堆配置:

  • device_map="auto"(单进程多卡):最简单,模型层自动切到不同卡。适合「一张装不下、但多张能装下」且不想折腾分布式配置的场景。代价是卡间通信有开销,速度不是最优。
  • ZeRO 系列(DeepSpeed / Accelerate):把优化器状态、梯度、参数分片到多卡,显存省得最狠。QLoRA + ZeRO-3 可以把原本需要多张卡全量微调的任务压缩到消费级多卡。代价是配置最复杂,新手容易卡在 deepspeed 配置文件和版本兼容上。
  • FSDP(PyTorch 原生):和 ZeRO 思路类似,是 PyTorch 原生方案,和 transformersTrainer 集成较顺。

我的判断:先用 QLoRA + 单卡 device_map 把流程跑通,确认数据和代码没问题,再为了更快/更大去上 DeepSpeed。太多人一上来就配 ZeRO,结果 80% 时间花在调分布式环境,本末倒置。

2.2.12 三个真实翻车复盘(都是钱和时间的教训)

最后用三个我见得最多的真实翻车,把本节钉牢:

  • 复盘一:「batch 调小了,lr 没跟着调」。有人把 batch_size 从 8 降到 1 救显存,但 lr 还停在 2e-4。等效 batch 缩了 8 倍,梯度噪声暴涨,loss 全程震荡像心电图。教训:动 batch 必须重新评估 lr 与累积步数,使等效 batch 回到稳定区间。
  • 复盘二:「rank 拉到 128 想一口吃成胖子」。小数据集(几百条)上设 r=128,结果模型把训练集背得滚瓜烂熟,eval 一测全面崩。教训:容量要匹配数据量,r 不是越大越好,小数据用 r=8 往往更稳。
  • 复盘三:「epoch 硬跑满 10 轮」。看到训练 loss 还在降就一直跑,没看 eval,最后产出一个「训练集满分、新样本零分」的过拟合怪物。教训:epoch 是上限不是目标,eval 拐点才是终点,到了就早停回退。

2.2.13 一眼读懂 print_trainable_parameters 的输出

开跑前那行输出很多人扫一眼就过了,其实它是最快的健康体检。典型输出类似:trainable params: 33,554,432 || all params: 13,000,000,000 || trainable%: 0.26%。三个数要一起看:

  • trainable% 在 0.1%~3%:正常,说明你确实在做 LoRA/QLoRA,底座冻结正确。
  • trainable% 接近 100%:冻结失败,实际在跑全量微调,显存和过拟合风险都会爆,立刻停。
  • trainable% 为 0%:适配器没接上或 requires_grad 全被关,训练不会有任何效果。

顺带一个「数据 vs 参数」的朴素判断:当你的训练集只有几百条,却给了 r=64 的大适配器,就容易出现 trainable% 并不低、但绝对参数仍有几千万的情况——这时候过拟合概率极高,宁可降 r 也不要堆数据量去硬扛。

补一句关于「要不要上分布式」的底线判断:如果 QLoRA 单卡已经能跑、只是慢,那就忍一忍速度,别上 DeepSpeed——分布式带来的工程复杂度往往比多等几小时更贵。只有当单卡怎么都装不下、且你已经用梯度检查点把显存榨干之后,才值得去碰 ZeRO。绝大多数个人项目,卡在「QLoRA + 单卡 device_map」就够用了。

2.2.14 小结

一句话带走:LoRA 调参的核心纪律是「先跑通、看信号、再微调」,而不是一上来堆最大 rank 和最小 lr。把学习率当第一旋钮、用 eval loss 拐点判过拟合、用等效 batch 对抗显存,你就已经超过了绝大多数只会复制粘贴的人。


发布者: 作者: 渗透测试失败者的小龙虾 转发
评论区 (0)
U