使用LoRA(低秩适应)对LLMs进行微调的实用技巧

使用LoRA(低秩适应)对LLMs进行微调的实用技巧

首页角色扮演域之纪元更新时间:2024-06-05

LoRA,即低秩适应(Low-Rank Adaptation),是一种高效的参数微调技术。它通过向模型中添加少量可训练参数,同时保持原始模型参数不变,实现了对LLM的快速定制化。LoRA的核心在于其将权重矩阵分解为两个较小的矩阵,从而以更少的参数近似完全监督微调。

在使用LoRA技术的时候有很多技巧,需要大量的实战才能获取。Sebastian Raschka,一位来自威斯康星大学麦迪逊分校的统计学助理教授,也是人工智能平台Lightning AI的LLM研究员,进行了成百上千次关于LoRA的实验。前一段时间我翻译过他关于LoRA实验的文章:

北方的郎:使用 LoRA 和 QLoRA 微调LLM:数百次实验的见解

本篇是他后续又写的一篇关于使用LoRA进行微调的实用建议,内容非常的实用。这些实战经验对于做这方面工作、研究的人是非常宝贵的,可以让大家少走很多弯路。

文章链接:

Practical Tips for Finetuning LLMs Using LoRA (Low-Rank Adaptation)

添加图片注释,不超过 140 字(可选)

以下为译文:

低秩适应(LoRA) 是用于高效训练定制 LLM 的最广泛使用和最有效的技术之一。对于那些对开源LLM感兴趣的人来说,这是一项值得熟悉的基本技术。 上个月,我分享了一篇关于多个 LoRA 实验的文章,该文章基于我与 Lightning AI 的同事共同维护的开源Lit-GPT 存储库。这篇人工智能前沿文章旨在讨论我从实验中获得的主要经验教训。此外,我将解决一些与该主题相关的常见问题。如果您对微调定制LLM感兴趣,我希望这些见解能够在“长期”中为您节省一些时间(没有双关语)。 简而言之,我在本文中讨论的主要内容如下:

  1. 尽管 LLM 训练(或者一般在 GPU 上训练模型时)具有固有的随机性,但多次运行的结果仍然非常一致。
  2. 如果您受到 GPU 内存的限制,QLoRA 提供的权衡可能是值得的。它节省了 33% 的内存,但运行时间增加了 39%。
  3. 在微调 LLM 时,优化器的选择不应成为主要问题。虽然 SGD 本身并不是最优的,但无论您使用 AdamW、带有调度程序的 SGD 还是带有调度程序的 AdamW,结果的变化都很小。
  4. 虽然 Adam 由于为每个模型参数引入了两个新参数而经常被标记为内存密集型优化器,但这并不会显着影响 LLM 的峰值内存需求。这是因为大部分内存被分配用于大型矩阵乘法,而不是保留额外的参数。
  5. 对于静态数据集,多次迭代(如多纪元训练中所做的那样)可能没有好处。它通常会恶化结果,可能是由于过度拟合。
  6. 如果您要合并 LoRA,请确保将其应用于所有层,而不仅仅是键和值矩阵,以最大限度地提高模型性能。
  7. 调整 LoRA 等级至关重要,选择合适的 alpha 值也很重要。一个好的启发是将 alpha 设置为排名值的两倍。
  8. 在拥有 14 GB RAM 的单个 GPU 上,可以在几个小时内有效地微调 70 亿个参数模型。使用静态数据集,优化以在所有基准任务中表现出色是不可能的。解决这个问题需要不同的数据源,或者 LoRA 可能不是理想的工具。

此外,我将回答有关 LoRA 的十个常见问题:

(在上一期的 AI 中,我提到如果有兴趣的话,我想在某个时候写一篇更一般性的介绍,从头开始编写 LoRA 的代码实现。根据你们的反馈,大家很感兴趣,我计划分享另一个未来关于 LoRA 的文章。目前,本文重点关注使用 LoRA 的更广泛的想法和收获——自上而下的观点。)

LoRA简介

大型语言模型很大,并且由于 GPU 内存限制,在训练期间更新所有模型权重的成本可能会很高。 例如,假设我们有一个 LLM,其 7B 个参数以权重矩阵W表示。(实际上,模型参数当然分布在多层的不同矩阵中,但为了简单起见,我们在这里指的是单个权重矩阵)。在反向传播过程中,我们学习一个ΔW矩阵,其中包含我们想要更新多少原始权重以在训练期间最小化损失函数的信息。 那么权重更新如下: W更新= W ΔW 如果权重矩阵W包含7B个参数,则权重更新矩阵ΔW也包含7B个参数,并且计算矩阵ΔW可能是计算量和内存密集型的。 Hu等人提出的LoRA方法。替换为将权重变化ΔW分解为较低等级的表示。准确地说,它不需要显式计算ΔW相反,LoRA 在训练期间直接学习ΔW的分解表示,这就是节省的来源,如下图所示。

添加图片注释,不超过 140 字(可选)

如上所示,ΔW的分解意味着我们用两个较小的 LoRA 矩阵AB来表示大矩阵ΔW 。如果A的行数与ΔW相同B的列数与ΔW相同,我们可以将分解写为ΔW = AB 。( AB是矩阵AB之间的矩阵乘法结果。) 这能节省多少内存?它取决于等级r ,它是一个超参数。例如,如果ΔW有 10,000 行和 20,000 列,则它存储 200,000,000 个参数。如果我们选择r=8AB ,那么A有 10,000 行和 8 列,B有 8 行和 20,000 列,即 10,000×8 8×20,000 = 240,000 个参数,大约比 200,000,000 少 830×。 当然,AB无法捕获ΔW可以捕获的所有信息,但这是设计使然。当使用 LoRA 时,我们假设模型需要W是一个满秩的大矩阵,以捕获预训练数据集中的所有知识。然而,当我们微调LLM时,我们不需要更新所有权重并以比ΔW更少的权重捕获适应的核心信息因此,我们通过AB进行低等级更新。

1.LoRA一致性

使用 LoRA 运行多个实验,我发现尽管 LLM 训练或一般在 GPU 上训练模型具有固有的随机性,但不同运行中的基准测试结果惊人地一致。这是进一步比较研究的良好基础。

添加图片注释,不超过 140 字(可选)

(请注意,结果是在默认设置下使用较小的r=8获得的。实验详细信息可以在我的另一篇文章中找到。)

2. QLoRA 计算内存权衡

Dettmers 等人的 QLoRA 。是量化 LoRA 的缩写,是一种在微调过程中进一步减少内存使用的技术。在反向传播期间,QLoRA 将预训练权重量化为 4 位精度,并使用分页优化器来处理内存峰值。 事实上,我发现使用 QLoRA 时可以节省 33% 的 GPU 内存。然而,由于 QLoRA 中预训练模型权重的额外量化和反量化,训练运行时间增加了 39%。 默认 LoRA 具有 16 位脑浮点精度:

具有 4 位普通浮点的 QLoRA:

此外,我发现建模性能几乎没有受到影响,这使得 QLoRA 成为常规 LoRA 训练的可行替代方案,以解决常见的 GPU 内存瓶颈。

添加图片注释,不超过 140 字(可选)

3. 学习率调度器(Learning Rate Schedulers)

学习率调度程序会在整个训练过程中降低学习率,以优化收敛并避免超过损失最小值。 余弦退火是一种学习率调度程序,可按照余弦曲线调整学习率。它以高学习率开始,然后平滑下降,以类似余弦的方式接近零。常用的变体是半周期变体,在训练过程中仅完成半余弦周期,如下图所示。

添加图片注释,不超过 140 字(可选)

作为实验的一部分,我在 LoRA 微调脚本中添加了余弦退火调度程序,并观察到它显着提高了 SGD 性能。然而,它对 Adam 和 AdamW 优化器的影响较小,几乎没有区别。

添加图片注释,不超过 140 字(可选)

下一节将讨论 SGD 相对 Adam 的潜在优势。

4. Adam 与 SGD

Adam 和 AdamW 优化器仍然是深度学习中的流行选择,尽管在我们处理大型模型时它们非常占用内存。原因是 Adam 优化器为每个模型参数维护两个移动平均值:梯度的一阶矩(均值)和梯度的二阶矩(非中心方差)。换句话说,Adam 优化器在内存中为每个模型参数存储两个附加值。如果我们使用 7B 参数模型,那么在训练期间需要跟踪额外的 14B 参数。 SGD 优化器在训练期间不需要跟踪任何额外参数,因此问题是:在训练 LLM 时,将 Adam 替换为 SGD 对峰值内存需求有什么优势? 在我的实验中,训练使用 AdamW 和 LoRA 默认值 ( r=8 )训练的 7B 参数 Llama 2 模型需要 14.18 GB 的 GPU 内存。使用 SGD 训练相同的模型需要 14.15 GB 的 GPU 内存。换句话说,节省的空间(0.03 GB)微乎其微。 为什么节省的内存这么少?这是因为对于 LoRA,我们只有少量的可训练参数。例如,如果r=8 ,则 7B Llama 2 模型中的所有 6,738,415,616 个参数中有 4,194,304 个可训练 LoRA 参数。 如果我们只看简单的数字,4,194,304 个可训练参数听起来仍然很多,但如果我们计算一下,我们只有 4,194,304 × 2 × 16 位 = 134.22 兆位 = 16.78 兆字节。(我们观察到 0.03 Gb = 30 Mb 的差异,因为存储和复制优化器状态会产生额外的开销。)2 表示 Adam 存储的额外参数的数量,16 位表示模型权重的默认精度。

添加图片注释,不超过 140 字(可选)

然而,如果我们将 LoRA r增加到256(我在后面的实验中所做的),Adam 和 SGD 优化器之间的差异就会变得更加明显:

值得一提的是,当 LoRA 的r很小时,将 Adam 优化器替换为 SGD 可能不值得。然而,当我们增加r时,这可能是值得的

5. 多个训练周期

在传统的深度学习中,我们经常对训练集进行多次迭代——对训练集的迭代称为一个时期。例如,在训练卷积神经网络时,通常会运行数百个训练周期。多时期训练对于指令微调也有用吗? 当我将包含 5 万个示例的 Alpaca指令微调数据集的迭代次数增加两倍(类似于 2 个训练周期)时,我注意到模型性能有所下降。

添加图片注释,不超过 140 字(可选)

结论是,多时期训练可能不会有利于指令微调,因为它可能会恶化结果。我在 1k-example LIMA 数据集上观察到了同样的情况。这种性能下降可能是由于过度拟合增加造成的,这需要进行额外的调查。

6. 为更多层启用 LoRA

上表显示了仅针对选定权重矩阵(即每个变压器层中的键和值权重矩阵)启用 LoRA 的实验。此外,我们还可以为查询权重矩阵、投影层、多头注意力块之间的其他线性层以及线性输出层启用 LoRA。

添加图片注释,不超过 140 字(可选)

如果我们为所有这些附加层启用 LoRA,则 7B Llama 2 模型的可训练参数数量将增加 5 倍,从 4,194,304 增加到 20,277,248。这还需要更大的内存要求(16.62 GB 而不是 14.18 GB),但可以显着提高建模性能。

添加图片注释,不超过 140 字(可选)

然而,我的实验的一个限制是我只探索了两种设置:(1)仅启用查询和值权重矩阵的 LoRA,以及(2)启用所有层的 LoRA。在未来的实验中探索其他组合可能是值得的。例如,了解为投影层激活 LoRA 是否真的有益将会很有趣。

7. 平衡 LoRA 超参数:R 和 Alpha

正如最初的 LoRA 论文所述,LoRA 引入了一个额外的缩放系数,用于在前向传播过程中将 LoRA 权重应用于预训练权重。缩放涉及我们之前讨论过的排名参数 r,以及另一个超参数 α (alpha),其应用如下:

scaling = alpha / r weight = (lora_B @ lora_A) * scaling

从上面的代码公式可以看出,LoRA权重的影响越大。 之前的实验使用r=8alpha=16 ,这导致了 2 倍的缩放。将 LoRA 用于 LLM 时,选择 alpha 作为r的两倍是常见的经验法则,但我很好奇这是否仍然适用于较大的r值。换句话说,“alpha = 2×rank”似乎确实是一个最佳点。然而,在模型和数据集的这种特定组合中,其中r=256alpha=128 (0.5 倍缩放)性能甚至更好。

添加图片注释,不超过 140 字(可选)

(我尝试了r=32r=64r=128r=512 ,但为了清楚起见省略了结果,因为r=256产生了最佳性能。) 选择 alpha 作为r 的两倍通常可能会产生最佳结果,但尝试不同的比率也可能没有坏处。

8. 在单 GPU 上训练 7B 参数模型

主要收获之一是 LoRA 允许我们在单个 GPU 上微调 7B 参数 LLM。在这种特殊情况下,使用 QLoRA 和最佳设置(r=256alpha=512 ),对于 50k 训练示例(此处为 Alpaca 数据集),使用 AdamW 的 17.86 GB 大约需要 3 小时(在 A100 上)。

添加图片注释,不超过 140 字(可选)

在本文的其余部分中,我将回答您可能有的其他问题。

常见问题解答

Q1:数据集有多重要? 数据集可能很关键。我在实验中使用了 Alpaca 数据集,其中包含 50k 个训练示例。我选择这个数据集是因为它非常受欢迎,并且由于文章的篇幅已经很长,因此尝试不同的数据集超出了范围。 然而,值得注意的是,Alpaca 是一个通过查询旧版本 ChatGPT 生成的合成数据集,按照今天的标准可能不是最好的。 数据质量非常重要。例如,在 6 月,我讨论了 LIMA 数据集(Ahead of AI #9:LLM 调优和数据集视角),这是一个仅包含 1k 个示例的精选数据集。 根据LIMA:Less Is More for Alignment论文,在 LIMA 上微调的 65B Llama 模型明显优于在 Alpaca 上微调的 65B Llama 模型。

添加图片注释,不超过 140 字(可选)

在 LIMA 上使用最佳配置(r=256,alpha=512 ),我得到了与大 50 倍的 Alpaca 数据集相似的性能,甚至更好。

添加图片注释,不超过 140 字(可选)

Q2:LoRA 是否适用于域适应?(Does LoRA Work for Domain Adaptation?) 不幸的是,我对这个问题没有一个好的答案。根据经验,知识通常是从预训练数据集中吸收的。指令微调通常更多的是帮助或指导LLM遵循指令。 然而,值得注意的是,如果内存是一个问题,LoRA 还可以用于在特定领域的数据集上进一步预训练现有的预训练 LLM。 请注意,我的实验还包括两个算术基准(它们包含在我的其他更具技术性的文章中),其中 LoRA 微调模型的表现明显比预训练的基础模型差。我的假设是模型未学习算术,因为 Alpaca 数据集不包含相应的示例。模型是否完全失去了知识,或者是否是因为模型无法再处理指令,都需要进一步调查。然而,这里的要点是,在微调LLM时,包含您关心的每项任务的示例可能是个好主意。 Q3:如何选择最佳排名? 不幸的是,我没有任何好的启发法来选择一个好的r ,并认为它是一个需要为每个 LLM 和每个数据集探索的超参数。我怀疑选择太大的r可能会导致更多的过度拟合。另一方面,较小的 r 可能无法捕获数据集中的不同任务。换句话说,我怀疑数据集中的任务越多样化,r应该越大。例如,如果我只想要一个执行基本两位数算术的模型,那么一个很小的​r可能就足够了。然而,这只是一个假设,还需要进一步的调查。 Q4:LoRA需要所有层都启用吗? 我只探索了两种设置:(1) 仅启用查询和值权重矩阵的 LoRA,以及 (2) 启用所有层的 LoRA。在未来的实验中探索其他组合可能是值得的。例如,了解为投影层激活 LoRA 是否真的有益将会很有趣。

添加图片注释,不超过 140 字(可选)

例如,如果我们考虑各种设置(lora_query、lora_key、lora_value、lora_projection、lora_mlp和lora_head),则需要探索2^6 = 64种组合。这种探索将是未来研究的一个有趣的话题。 Q5:如何避免过拟合? 一般来说,较大的r会导致更多的过度拟合,因为它决定了可训练参数的数量。如果模型出现过度拟合,那么首先要探索的就是减小 r 或增加数据集大小。此外,您可以尝试增加 AdamW 或 SGD 优化器中的权重衰减率,并且可以考虑增加 LoRA 层的 dropout 值。 我在实验中没有探索过 LoRA 丢失参数(我使用了 0.05 的固定丢失率),这是未来研究的一个有趣的主题。 Q6:其他优化器怎么样? 其他有趣的 LLM 优化器值得将来探索。其中一个优化器是Sophia: A Scalable Stochastic Second-order Optimizer for Language Model Pre-training ,于 5 月发布。 Sophia 是一种二阶优化算法,对于 Adam 和 AdamW 通常占主导地位的 LLM 来说尤其有吸引力。论文称,与 Adam 相比,Sophia 速度快 2 倍,并且用 Sophia 训练的模型可以获得更好的建模性能。简而言之,Sophia 通过梯度曲率而不是梯度方差来标准化梯度,就像 Adam 一样。 Q7:还有哪些其他因素影响内存使用? 除了精度和量化设置、模型大小、批量大小和可训练 LoRA 参数的数量之外,数据集还会影响内存使用情况。 请注意,Llama 2 的块大小为 4048。例如,如果 LLM 的块大小为 4048 个令牌,则它可以一次处理最多 4048 个令牌的序列。然而,由于未来标记的屏蔽,较短的训练序列可以节省大量内存。 例如,Alpaca 数据集相对较小,最大长度为 1304 个 token。

添加图片注释,不超过 140 字(可选)

当我尝试长度高达 2048 个标记的其他数据集时,我注意到内存使用量从 17.86 GB 增加到 26.96 GB。 Q8:它与 Full Finetuning 和 RLHF 相比如何? 我没有进行任何 RLHF 实验(对于那些好奇的人,我在这里介绍了 RLHF ),但我确实考虑过全面微调。完全微调至少需要 2 个 GPU,并且在每个 GPU 上使用 36.66 GB 并在 3.5 小时内完成。然而,基准测试结果不是很好,可能是由于过度拟合或次优超参数。

添加图片注释,不超过 140 字(可选)

Q9:LoRA 权重可以组合吗? 是的,可以组合多组 LoRA 权重。在训练期间,我们将 LoRA 权重与预训练权重分开,并在每次前向传递期间添加它们。 但是,如果您的实际应用程序具有多组 LoRA 权重(例如,每个应用程序客户一组),则单独存储这些权重以节省磁盘空间是有意义的。但是,可以在训练后将预训练权重与 LoRA 权重合并以创建单个模型。这样,我们就不必在每次前向传递中应用 LoRA 权重: weight = (lora_B @ lora_A) * scaling 相反,我们应用如上所示的权重更新并保存合并(添加)的权重。 同样,我们可以不断添加多个LoRA权重集: weight = (lora_B_set1 @ lora_A_set1) * scaling_set1 weight = (lora_B_set2 @ lora_A_set2) * scaling_set2 weight = (lora_B_set3 @ lora_A_set3) * scaling_set3 ... 我还没有做实验来评估这种方法的性能,但这在技术上已经可以通过Lit-GPT 中提供的script/merge_lora.py脚本实现。 Q10:逐层最优排名适应怎么样? 为简单起见,我们通常以每层相同的学习率来训练深度神经网络,学习率就是我们需要优化的超参数。更进一步,我们还可以为每一层选择不同的学习率(在 PyTorch 中,这并不太复杂)。然而,在实践中很少这样做,因为它增加了额外的开销,并且在训练深度神经网络时通常已经有很多旋钮需要调整。 类似于为不同层选择不同的学习率,我们也可以为不同层选择不同的 LoRA 等级。我还没有找到任何这方面的实验,但详细介绍这种方法的文档是Layer-wise Optimal Rank Adaptation (也缩写为 LORA)。从理论上讲,这在实践中听起来是个好主意。然而,它在优化超参数时也增加了大量的选择。

查看全文
大家还看了
也许喜欢
更多游戏

Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved