🧠LoRA 微调超参数指南

最佳 LoRA 秩、alpha、训练轮数、批量大小与梯度累积、QLoRA 与 LoRA、目标模块等!

LoRA 超参数是可调参数,用于控制低秩适配(LoRA)如何对大型语言模型(LLM)进行微调。由于有许多选项(例如学习率和训练轮数)以及数百万种可能的组合,选择合适的数值对于在微调过程中实现准确性、稳定性、质量以及减少幻觉至关重要。

你将学习基于数百篇研究论文与实验的见解得出的这些参数最佳实践,并看到它们如何影响模型。 虽然我们建议使用 Unsloth 的默认值,但理解这些概念将赋予你完全控制权。 目标是通过改变超参数数值来提高准确性,同时对抗 过拟合或欠拟合。过拟合发生在模型记住了训练数据,损害了其对新的、未见输入的泛化能力。目标是获得一个能很好泛化的模型,而不是一个仅仅会记忆的模型。

那么什么是 LoRA?

在大型语言模型中,我们有模型权重。Llama 70B 有 700 亿个数字。我们不是改变所有 700 亿个数字,而是向每个权重添加薄矩阵 A 和 B 并优化它们。这意味着我们只优化 1% 的权重。

我们不是优化模型权重(黄色),而是优化两个薄矩阵 A 和 B。

🔢 关键微调超参数

学习率

定义在每次训练步中模型权重被调整的幅度。

  • 较高的学习率:可以导致更快的初始收敛,但如果设置过高会使训练不稳定或无法找到最佳最小值。

  • 较低的学习率:带来更稳定且更精确的训练,但可能需要更多的训练轮数才能收敛,从而增加总体训练时间。尽管人们常认为低学习率会导致欠拟合,但它们实际上可能导致 过拟合 甚至阻止模型学习。

  • 典型范围: 2e-4 (0.0002)到 5e-6 (0.000005). 🟩 对于常规的 LoRA/QLoRA 微调, 我们推荐 2e-4 作为起点。 🟦 对于强化学习 (DPO、GRPO 等),我们推荐 5e-6 . 对于全量微调, 通常更适合使用更低的学习率。

训练轮数(Epochs)

模型看到完整训练数据集的次数。

  • 更多的训练轮: 可以帮助模型更好地学习,但过多会导致其 记住训练数据,从而损害其在新任务上的表现。

  • 更少的训练轮: 减少训练时间并能防止过拟合,但如果训练轮数不足以让模型学习数据集的底层模式,可能导致模型训练不足。

  • 推荐: 1-3 个 epoch。对于大多数基于指令的数据集,超过 3 个 epoch 后收益递减且增加过拟合风险。

LoRA 或 QLoRA

LoRA 使用 16 位精度,而 QLoRA 是一种 4 位的微调方法。

  • LoRA: 16 位微调。它略快且略微更准确,但占用显存明显更多(比 QLoRA 多 4 倍)。推荐用于 16 位环境和需要最大精度的场景。

  • QLoRA: 4 位微调。略慢且精度略低,但显存占用大幅减少(少 4 倍)。 🦥 在 Unsloth 中,70B LLaMA 使用 QLoRA 可装入 <48GB 显存 - 更多细节在此arrow-up-right.

超参数与推荐:

超参数
功能
推荐设置

LoRA 秩(Rank) (r)

控制 LoRA 适配器矩阵中可训练参数的数量。较高的秩增加模型容量,但也增加内存使用。

8、16、32、64、128 选择 16 或 32

LoRA Alpha (lora_alpha)

按秩缩放微调调整的强度(r).

r (标准)或 r * 2 (常见启发式)。 更多细节在此.

LoRA Dropout

一种正则化技术,在训练期间随机将部分 LoRA 激活置为零以防止过拟合。 不太有用,因此我们默认将其设置为 0。

0(默认)到 0.1

权重衰减(Weight Decay)

一种正则项,用于惩罚过大的权重以防止过拟合并提高泛化能力。不要使用过大的数值!

0.01(推荐) - 0.1

预热步数(Warmup Steps)

在训练开始时逐步增加学习率。

总步数的 5-10%

学习率调度器类型(Scheduler Type)

在训练过程中动态调整学习率。

linearcosine

随机种子(random_state)

一个固定数字以确保结果可复现。

任意整数(例如, 42, 3407)

目标模块(Target Modules)

指定希望将 LoRA 适配器应用到模型的哪些部分 —— 注意力层、MLP 或两者。

注意力(Attention): q_proj、k_proj、v_proj、o_proj MLP: gate_proj、up_proj、down_proj

建议针对所有主要线性层: q_proj、k_proj、v_proj、o_proj、gate_proj、up_proj、down_proj.

🌳 梯度累积与批量大小等价性

有效批量大小(Effective Batch Size)

正确配置你的批量大小对于在训练稳定性与 GPU 显存限制之间取得平衡至关重要。这由两个参数管理,它们的乘积是 有效批量大小(Effective Batch Size). 有效批量大小(Effective Batch Size) = batch_size * gradient_accumulation_steps

  • 一个 更大的有效批量大小 通常会带来更平滑、更稳定的训练。

  • 一个 更小的有效批量大小 可能会引入更多方差。

虽然每个任务不同,下面的配置为在现代 GPU 上的大多数微调任务提供了一个很好的起点,以实现稳定的 有效批量大小(Effective Batch Size) 为 16,这对大多数现代 GPU 上的微调任务表现良好。

参数
描述
推荐设置

批量大小(Batch Size) (batch_size)

在单个前向/反向传播中单个 GPU 处理的样本数。 显存使用的主要驱动因素。更高的数值可以提高硬件利用率并加速训练,但前提是它们能装入内存。

2

梯度累积(Gradient Accumulation) (gradient_accumulation_steps)

在执行一次模型权重更新之前要处理的微批次数量。 训练时间的主要驱动因素。 允许模拟更大的 batch_size 以节省显存。更高的数值会增加每个 epoch 的训练时间。

8

有效批量大小(Effective Batch Size) (计算得出)

每次梯度更新使用的真实批量大小。它直接影响训练稳定性、质量和最终模型性能。

4 到 16 推荐:16(从 2 * 8 得来)

显存与性能的权衡

假设你希望每次训练步使用 32 个样本。那么你可以使用以下任一配置:

  • batch_size = 32,gradient_accumulation_steps = 1

  • batch_size = 16,gradient_accumulation_steps = 2

  • batch_size = 8,gradient_accumulation_steps = 4

  • batch_size = 4,gradient_accumulation_steps = 8

  • batch_size = 2,gradient_accumulation_steps = 16

  • batch_size = 1,gradient_accumulation_steps = 32

虽然对于模型的权重更新这些配置都是等价的,但它们对硬件的要求差别很大。

第一种配置(batch_size = 32)使用了 最多的显存 ,并且很可能在大多数 GPU 上失败。最后一种配置(batch_size = 1)使用了 )使用最少的显存, 但代价是训练速度略慢. 为避免 OOM(内存不足)错误,始终优先设置较小的 batch_size 并增加 gradient_accumulation_steps 以达到你的目标 有效批量大小(Effective Batch Size).

🦥 Unsloth 梯度累积修复

梯度累积和批量大小 在 Unsloth 中现在完全等价 由于我们对梯度累积的 bug 修复。我们已实现针对梯度累积的特定修复,解决了两种方法未产生相同结果的常见问题。这曾是社区中已知的挑战,但对于 Unsloth 用户而言,这两种方法现在可以互换使用。

阅读我们的博客文章arrow-up-right 以获取更多详情。

在我们的修复之前,组合 batch_sizegradient_accumulation_steps 虽然产生相同的 有效批量大小(Effective Batch Size) (即, batch_size × gradient_accumulation_steps = 16)但并未导致等价的训练行为。例如,像 b1/g16, b2/g8, b4/g4, b8/g2,以及 b16/g1 都具有 有效批量大小(Effective Batch Size) 为 16,但如图所示,在使用标准梯度累积时损失曲线并不一致:

(之前 - 标准梯度累积)

在应用我们的修复后,无论如何实现该 有效批量大小(Effective Batch Size) 为 16,损失曲线现在都会正确对齐:

(之后 - 🦥 Unsloth 梯度累积)

🦥 Unsloth 中的 LoRA 超参数

以下演示了一个标准配置。 虽然 Unsloth 提供了优化的默认值,但理解这些参数对于手动调整至关重要。

  1. 微调过程的秩(r)。较大的秩使用更多内存且会更慢,但可以在复杂任务上提高精度。我们建议使用例如 8 或 16(用于快速微调)以及最高可达 128 的秩。使用过大的秩可能导致过拟合并损害模型质量。\

  2. 为了获得最佳性能, 应将 LoRA 应用于所有主要线性层. 研究表明 针对所有主要层对于匹配全量微调的性能至关重要。尽管可以删除某些模块以减少内存使用,但我们强烈不建议这样做以保留最大质量,因为内存节省很小。\

  3. 一个控制微调调整强度的缩放因子。将其设置为等于秩(r)是一个可靠的基线。一个流行且有效的启发式是将其设置为秩的两倍(r * 2),这会通过给予 LoRA 更新更大的权重使模型更积极地学习。 更多细节在此。\

  4. 一种正则化技术,有助于 防止过拟合 ,通过在每个训练步骤中随机将一部分 LoRA 激活置为零来实现。 近期研究表明arrow-up-right 对于 微调中常见的短时训练lora_dropout 可能并不是一个可靠的正则化手段。 🦥 Unsloth 的内部代码在 lora_dropout = 0时可以优化训练,使其略快,但如果你怀疑存在过拟合,我们仍建议使用非零值。\

  5. 将此保持为 "none" 用于更快训练和降低内存使用。此设置避免训练线性层中的偏置项,这会增加可训练参数但在实际中几乎没有收益。\

  6. 选项为 True, False,以及 "unsloth". 🦥 我们推荐 "unsloth" 因为它额外降低约 30% 的内存使用并支持极长上下文的微调。你可以在以下内容中阅读更多 我们关于长上下文训练的博客文章arrow-up-right.\

  7. 用于确保确定性、可复现的运行。训练过程中会涉及随机数,因此设置固定种子对一致的实验至关重要。\

  8. 一个高级功能,它实现了 秩稳定的 LoRAarrow-up-right. 如果设置为 True, 有效缩放将变为 lora_alpha / sqrt(r) 而不是标准的 lora_alpha / r。这有时可以提高稳定性,尤其是在较高秩时。 更多细节在此。\

  9. 一种高级技术,如在以下文献中提出的 LoftQarrow-up-right,使用预训练权重的前 'r' 个奇异向量来初始化 LoRA 矩阵。这可以提高准确性,但可能在训练开始时引起显著的内存峰值。

验证 LoRA 权重更新:

在验证 LoRA 适配器权重在微调后是否已更新时,避免使用 np.allclose() 进行比较。此方法可能遗漏细微但有意义的变化,尤其是在 LoRA A中,其以较小的高斯值初始化。在宽松的数值容差下,这些变化可能不会被视为显著。感谢 贡献者arrow-up-right 提供本节内容。

为可靠地确认权重更新,我们建议:

  • 使用 校验和或哈希比较 (例如 MD5)

  • 计算 张量之间绝对差值的和 在张量之间

  • 检查张量统计量 (例如均值、方差)手动检查

  • 或者使用 np.array_equal() 如果期望精确相等

📐LoRA Alpha 与秩的关系

circle-check
W^=W+αrank×AB\hat{W} = W + \frac{\alpha}{\text{rank}} \times AB
rsLoRA 的其他缩放选项。sqrt(r) 是最佳的。
W^rslora=W+αrank×AB\hat{W}_{\text{rslora}} = W + \frac{\alpha}{\sqrt{\text{rank}}} \times AB

LoRA 的公式在左侧。我们需要将稀疏矩阵 A 和 B 按 alpha 除以秩进行缩放。 这意味着我们应保持 alpha/秩 至少 = 1.

根据 rsLoRA(秩稳定 LoRA)论文arrow-up-right,我们应改为按秩的平方根来缩放 alpha。其他选项存在,但理论上这是最优的。左侧图展示了其他秩及其困惑度(越低越好)。要启用此项,请设置 use_rslora = True 在 Unsloth 中。

我们的建议是将 alpha 设为等于秩,或至少为秩的 2 倍。 这意味着 alpha/秩 = 1 或 2。

🎯 LoRA 目标模块以及 QLoRA 与 LoRA 的对比

circle-check

根据经验实验证据和像原始 QLoRA 论文arrow-up-right这样的研究,最好将 LoRA 应用于注意力和 MLP 层。

图表显示了不同目标模块配置下的 RougeL 分数(越高越好),比较了 LoRA 与 QLoRA。

前三个点显示:

  1. QLoRA-All: LoRA 应用于所有 FFN/MLP 和注意力层。 🔥 总体表现最佳。

  2. QLoRA-FFN: 仅在 FFN 上使用 LoRA。 相当于: gate_proj, up_proj, down_proj。

  3. QLoRA-Attention: 仅在注意力层上使用 LoRA。 相当于: q_proj, k_proj, v_proj, o_proj.

😎 仅对完成部分进行训练,屏蔽掉输入

QLoRA 论文arrow-up-right 表明屏蔽输入并且 仅在完成部分上训练 (输出或助理消息)可以进一步 提高准确性 几个百分点(1%)。下面演示了在 Unsloth 中如何实现:

不是 仅在完成部分上训练:

用户: 你好,2+2 等于多少? 助理: 答案是 4。 用户: 你好,3+3 等于多少? 助理: 答案是 6。

训练 仅在完成部分上训练:

用户: 你好,2+2 等于多少? 助理: 答案是 4。 用户: 你好,3+3 等于多少? 助理: 答案是 6.

QLoRA 论文指出, 仅在完成部分上训练 会显著提高准确性,特别是对于多轮对话微调!我们在我们的 对话笔记本中这样做,见此处arrow-up-right.

要启用 仅在完成部分上训练 在 Unsloth 中,你需要定义指令和助理部分。 🦥 我们计划将来进一步为你自动化此过程!

对于 Llama 3、3.1、3.2、3.3 和 4 型号,你按如下方式定义这些部分:

对于 Gemma 2、3、3n 型号,你按如下方式定义这些部分:

🔎仅对视觉模型(VLM)上的助理回复进行训练

对于语言模型,我们可以使用 from unsloth.chat_templates import train_on_responses_only 如前所述。对于视觉模型,请将额外参数作为 UnslothVisionDataCollator 的一部分使用,就像之前一样!

例如对于 Llama 3.2 Vision:

🔑 避免过拟合与欠拟合

过拟合 (泛化差/过于专化)

模型记住了训练数据,包括其统计噪声,因此在未见数据上无法很好泛化。

circle-check

解决方案:

  • 调整学习率: 过高的学习率通常导致过拟合,尤其是在短期训练中。对于更长的训练,较高的学习率可能效果更好。最好两者都尝试以确定哪种表现最佳。

  • 减少训练轮数。在 1、2 或 3 个 epoch 后停止训练。

  • 增加 权重衰减。一个值为 0.010.1 是一个良好的起点。

  • 增加 lora_dropout。使用像 0.1 这样的值以增加正则化。

  • 增加批量大小或梯度累积步数.

  • 扩展数据集 - 通过将开源数据集与您的数据集合并或连接来增大数据集规模。选择更高质量的。

  • 基于评估的早停 - 启用评估并在评估损失连续几步增加时停止。

  • LoRA Alpha 缩放 - 在训练后和推理时缩小 alpha — 这将使微调效果不那么明显。

  • 权重平均 - 直接将原始 instruct 模型与微调权重相加并将权重除以 2。

欠拟合 (过于通用)

模型未能捕捉训练数据中的潜在模式,通常是由于模型复杂度或训练时长不足。

解决方案:

  • 调整学习率: 如果当前学习率过低,增加学习率可能会加快收敛,尤其是在短期训练中。对于较长的训练,请尝试降低学习率。测试两种方法以查看哪种效果更好。

  • 增加训练轮数: 进行更多的 epoch 训练,但监控验证损失以避免过拟合。

  • 增加 LoRA 秩 (r)和 alpha:秩至少应等于 alpha 的数值,对于较小模型/更复杂的数据集应使用更大的秩;通常在 4 到 64 之间。

  • 使用更符合领域的数据集:确保训练数据高质量且与目标任务直接相关。

  • 将批量大小减小到 1。这将使模型更新更为剧烈。

circle-check

致谢: 非常感谢 Eyeraarrow-up-right 为本指南做出的贡献!

最后更新于

这有帮助吗?