微调补充笔记:从训练稳定性到 LoRA 与张量并行

微调训练中,几个概念经常被放在一起,却解决不同的问题:学习率预热控制训练初期的步幅;梯度累积决定多少个小批次合成一次参数更新;梯度裁剪限制更新前的梯度范数;LoRA 减少可训练参数;张量并行把同一层的计算分到多个设备。把它们放回训练链路,位置就清楚了。

1. 一次训练更新的顺序

简化的顺序是:取小批次 → 前向计算损失 → 反向传播累积梯度 → 必要时裁剪梯度 → 优化器更新参数 → 更新学习率调度器 → 清空梯度。在梯度累积时,裁剪应放在所有小批次完成反向传播之后、优化器更新之前;每个小批次都裁剪一次会改变最终累积的梯度。

2. 学习率预热:训练初期逐渐达到目标步幅

学习率预热(warmup)先使用较小学习率,经过若干次优化器更新后升至目标值。线性预热可写为 lr(t) = 目标学习率 × min(t / 预热更新数, 1)。预热后可以保持固定,也可以接线性或余弦衰减。

例如目标学习率为 1e-4,预热 500 次更新。如果每 4 个小批次才更新一次参数,500 次更新约对应 2000 个小批次。预热有助于缓和训练初期的不稳定,但不保证一定更快收敛或性能更好。Hugging Face 的 get_linear_schedule_with_warmup 会在预热后线性衰减至 0。查看官方调度器文档。

3. 梯度累积:多次反向传播,一次更新

假设显存一次只能容纳 4 个样本,但希望每 16 个样本更新一次参数。设置 gradient_accumulation_steps = 4,连续执行 4 次前向与反向传播,然后调用一次 optimizer.step()。对于大小相同、损失按样本平均的小批次,常见写法是先缩放损失:

loss = outputs.loss / gradient_accumulation_steps
loss.backward()

这使累积梯度对应于 4 个小批次的平均损失。多设备时,常用的近似有效批量大小是“每设备小批量 × 累积次数 × 数据并行设备数”。不过,最后一组不足 4 个小批次、有效 token 数不等或随机操作不同,都会使其不完全等价于一次大批量训练。使用 Accelerate、Trainer 等框架时,要先确认它们是否已经代管损失缩放和梯度同步,避免重复处理。Accelerate 官方说明。

4. 梯度裁剪:在更新前约束全局范数

torch.nn.utils.clip_grad_norm_(parameters, max_norm) 会计算全部参数梯度的总体范数。若超过阈值,就按相同比例缩小。比如梯度为 [3,4] 和 [1,2,2],二者的 L2 范数为 5 和 3,总范数为 √34 ≈ 5.83;若 max_norm=1,全部梯度约乘以 1/5.83。函数返回裁剪前的总范数,可用于监控。PyTorch 官方文档。

裁剪与 L1/L2 正则化或 weight decay 解决的问题不同:正则化改变目标或参数衰减方式,不能保证每一步的梯度范数低于阈值。梯度裁剪也不能保证训练绝不出现 NaN。混合精度训练时,应在所有梯度累积完成后、裁剪前执行 scaler.unscale_(optimizer)。PyTorch 混合精度示例。

optimizer.zero_grad()
for batch_idx, batch in enumerate(train_dataloader):
    outputs = model(**batch)
    loss = outputs.loss / gradient_accumulation_steps
    loss.backward()
    if (batch_idx + 1) % gradient_accumulation_steps == 0:
        torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
        optimizer.step()
        scheduler.step()
        optimizer.zero_grad()

这段代码假定每组小批次数相同;最后不足一组时,需要单独处理缩放与更新。若按有效 token 平均语言模型损失,也要按有效 token 总数正确加权。

5. LoRA:用低秩矩阵表达权重更新

LoRA 冻结预训练权重 W0,只训练两个较小的矩阵 A 与 B,令 ΔW = (α/r)BA。r 是秩,影响可训练参数量与表达空间;α 与 r 共同决定缩放系数。常见默认初始化将 B 设为零,因此训练开始时 BA=0,不会立刻改变基座模型输出。LoRA 原论文、PEFT 文档。

例如 r=4, α=16 的显式系数为 4;r=8, α=16 则为 2。这不意味着实际权重更新必然减半,因为矩阵形状及训练结果也变了。α/r 是一种缩放设计,并不在数学上保证不同秩的实际更新量一致。PEFT 还支持使用 α/√r 的 Rank-Stabilized LoRA。

QLoRA、QA-LoRA 也是参数高效微调的相关方法。QLoRA 让梯度穿过量化且冻结的基座模型来训练 LoRA 适配器,论文重点包括 NF4、双重量化和分页优化器;QA-LoRA 研究量化感知的低秩适配。QLoRA 论文、QA-LoRA 论文。

6. P-Tuning v2:另一条参数高效路线

P-Tuning v2 在多个 Transformer 层使用可训练的连续提示,冻结原模型的大部分参数。它与 LoRA 都是参数高效微调,但训练的位置不同:LoRA 给选定权重添加低秩更新,P-Tuning v2 优化深层提示表示。论文报告的是特定模型规模与 NLU 任务上的表现,不能据此推断所有任务都能达到全量微调效果。P-Tuning v2 论文。

7. 张量并行:切分同一层的矩阵

以一个 MLP 算例说明:输入 X 为 2×4,第一层权重 A 为 4×6,第二层权重 B 为 6×3。按列切分 A 后,各设备分别计算部分输出并应用逐元素激活;按行切分 B 后,各设备计算部分结果,再把结果相加。这是在层内拆分矩阵,不是把不同层分给不同设备的流水线并行。Megatron-LM 论文。

在暂不应用 GeLU 与 Dropout的线性演示中,结果为:

XA = [[130, 140, 150, 160, 170, 180],
      [290, 316, 342, 368, 394, 420]]
(XA)B = [[ 8430,  9360, 10290],
         [19470, 21600, 23730]]

其中 XA 第二行第一项是 5×1 + 6×7 + 7×13 + 8×19 = 290。实际张量并行还要考虑通信成本及反向传播中的数据交换。

把概念放回训练链路

问题 手段 位置
训练初期步幅 学习率预热 优化器更新时
单次小批量受显存限制 梯度累积 多次反向传播后更新
某一步梯度过大 梯度裁剪 参数更新前
全量训练参数太多 LoRA 等参数高效方法 训练参数与权重表示
单设备放不下某一层 张量并行 层内矩阵计算
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇