选自SebastianRaschka博客转载机器之心编译
编辑:佳琪
由于GPU内存的限制,在训练过程中更新模型权重成本高昂。例如,假设我们有一个7B参数的语言模型,用一个权重矩阵W表示。在反向传播期间,模型需要学习一个ΔW矩阵,旨在更新原始权重,让损失函数值最小。权重更新如下:W_updated=W+ΔW。如果权重矩阵W包含7B个参数,则权重更新矩阵ΔW也包含7B个参数,计算矩阵ΔW非常耗费计算和内存。由EdwardHu等人提出的LoRA将权重变化的部分ΔW分解为低秩表示。确切地说,它不需要显示计算ΔW。相反,LoRA在训练期间学习ΔW的分解表示,如下图所示,这就是LoRA节省计算资源的奥秘。
如上所示,ΔW的分解意味着我们需要用两个较小的LoRA矩阵A和B来表示较大的矩阵ΔW。如果A的行数与ΔW相同,B的列数与ΔW相同,我们可以将以上的分解记为ΔW=AB。(AB是矩阵A和B之间的矩阵乘法结果。)这种方法节省了多少内存呢?还需要取决于秩r,秩r是一个超参数。例如,如果ΔW有10,000行和20,000列,则需存储200,000,000个参数。如果我们选择r=8的A和B,则A有10,000行和8列,B有8行和20,000列,即10,000×8+8×20,000=240,000个参数,比200,000,000个参数少约830倍。当然,A和B无法捕捉到ΔW涵盖的所有信息,但这是LoRA的设计所决定的。在使用LoRA时,我们假设模型W是一个具有全秩的大矩阵,以收集预训练数据集中的所有知识。当我们微调LLM时,不需要更新所有权重,只需要更新比ΔW更少的权重来捕捉核心信息,低秩更新就是这么通过AB矩阵实现的。LoRA的一致性
虽然LLM,或者说在GPU上被训练的模型的随机性不可避免,但是采用LoRA进行多次实验,LLM最终的基准结果在不同测试集中都表现出了惊人的一致性。对于进行其他比较研究,这是一个很好的基础。
具有4位正常浮点数的QLoRA
此外,我发现模型的性能几乎不受影响,这说明QLoRA可以作为LoRA训练的替代方案,更进一步解决常见GPU内存瓶颈问题。
学习率调度器
在实验中,我在LoRA微调脚本中添加了一个余弦退火调度器,它显著地提高了SGD的性能。但是它对Adam和AdamW优化器的增益较小,添加之后几乎没有什么变化。
在下一节中,将讨论SGD相对于Adam的潜在优势。AdamvsSGDAdam和AdamW优化器在深度学习中很受欢迎。如果我们正在训练一个7B参数的模型,那使用Adam就能够在训练的过程中跟踪额外的14B参数,相当于在其他条件不变的情况下,模型的参数量翻了一番。SGD不能在训练过程中跟踪附加的参数,所以相比于Adam,SGD在峰值内存方面有什么优势呢?在我的实验中,使用AdamW和LoRA(默认设置r=8)训练一个7B参数的Llama2模型需要14.18GB的GPU内存。用SGD训练同一模型需要14.15GB的GPU内存。相比于AdamW,SGD只节省了0.03GB的内存,作用微乎其微。为什么只节省了这么一点内存呢?这是因为使用LoRA时,LoRA已经大大降低了模型的参数量。例如,如果r=8,在7B的Llama2模型的所有6,738,415,616个参数,只有4,194,304个可训练的LoRA参数。只看数字,4,194,304个参数可能还是很多,但是其实这么多参数仅占用4,194,304×2×16位=134.22兆位=16.78兆字节。(我们观察到了存在0.03Gb=30Mb的差异,这是由于在存储和复制优化器状态时,存在额外的开销。)2代表Adam存储的额外参数的数量,而16位指的是模型权重的默认精度。
如果我们把LoRA矩阵的r从8拓展到256,那么SGD相比AdamW的优势就会显现:
因此,当矩阵规模扩大时,SGD节省出的内存将发挥重要作用。由于SGD不需要存储额外的优化器参数,因此在处理大模型时,SGD相比Adam等其他优化器可以节省更多的内存。这对于内存有限的训练任务来说是非常重要的优势。迭代训练
在传统的深度学习中,我们经常对训练集进行多次迭代,每次迭代称为一个epoch。例如,在训练卷积神经网络时,通常会运行数百个epoch。那么,多轮迭代训练对于指令微调也有效果吗?答案是否定的,当我将数据量为50k的Alpaca示例指令微调数据集的迭代次数增加一倍,模型的性能下降了。
因此,我得出的结论是,多轮迭代可能不利于指令微调。我在1k的示例LIMA指令微调集中也观察到了同样的状况。模型性能的下降可能是由过拟合造成的,具体原因仍需进一步探索。在更多层中使用LoRA
下表显示了LoRA仅对选定矩阵(即每个Transformer中的Key和Value矩阵)起效的实验。此外,我们还可以在查询权重矩阵、投影层、多头注意力模块之间的其他线性层以及输出层启用LoRA。
如果我们在这些附加层上加入LoRA,那么对于7B的Llama2模型,可训练参数的数量将从4,194,304增加到20,277,248,增加五倍。在更多层应用LoRA,能够显著提高模型性能,但也对内存空间的需求量更高。此外,我只对(1)仅启用查询和权重矩阵的LoRA,(2)启用所有层的LoRA,这两种设置进行了探索,在更多层的组合中使用LoRA会产生何种效果,值得深入研究。如果能知道在投影层使用LoRA对训练结果是否有益,那么我们就可以更好地优化模型,并提高其性能。
平衡LoRA超参数:R和Alpha
正如提出LoRA的论文中所述,LoRA引入了一个额外的扩展系数。这个系数用于在前向传播过程中将LoRA权重应用于预训练之中。扩展涉及之前讨论过的秩参数r,以及另一个超参数α(alpha),其应用如下:
正如上图中的公式所示,LoRA权重的值越大,影响就越大。在之前的实验中,我采用的参数是r=8,alpha=16,这导致了2倍的扩展。在用LoRA为大模型减重时,将alpha设置为r的两倍是一种常见的经验法则。但我很好奇这条规则对于较大的r值是否仍然适用。
我还尝试了r=32,r=64,r=128,andr=512,但为了清晰起见省略了此过程,不过r=256时,的确效果最佳。事实上,选择alpha=2r确实提供了最优结果。在单个GPU上训练7B参数模型
LoRA允许我们在单个GPU上微调7B参数规模的大语言模型。在这个特定情况下,采用最佳设置过的QLoRA(r=256,alpha=512),使用AdamW优化器处理17.86GB(50k训练样例)的数据在A100上大约需要3个小时(此处为Alpaca数据集)。
在本文的其余部分中,我将回答你可能遇到的其他问题。10个问题
Q1:数据集有多重要?
对于这个问题,我目前还没有一个明确的答案。根据经验,知识通常是从预训练数据集中提取的。通常情况下,语言模型通常会从预训练数据集中吸收知识,而指令微调的作用主要是帮助LLM更好地遵循指令。既然算力紧张是限制大语言模型训练的关键因素,LoRA也可以被用于在特定领域的专用数据集,进一步预训练现有的预训练LLM。另外,值得注意的是,我的实验中包括两个算术基准测试。在这两个基准测试中,使用LoRA进行微调的模型表现明显比预训练的基础模型差。我推测这是由于Alpaca数据集没有缺少相应的算术示例,导致模型「忘记了」算术知识。我们还需要进一步的研究来确定模型是「忘记」了算术知识,还是它对相应指令停止了响应。然而,在这里可以得出一条结论:「在微调LLM时,让数据集包含我们所关心的每个任务的示例是一个好主意。」Q3:如何确定最佳r值?
对于这个问题,目前我还没有比较好的解决方法。最佳r值的确定,需要根据每个LLM和每个数据集的具体情况,具体问题具体分析。我推测r值过大将导致过拟和,而r值过小,模型可能无法捕捉数据集中多样化的任务。我怀疑数据集中的任务类型越多,所需r值就越大。例如,如果我仅需要模型执行基本的两位数算术运算,那么一个很小的r值可能就已经满足需要了。然而,这只是我的假设,需要进一步的研究来验证。Q4:LoRA是否需要为所有层启用?
我只对(1)仅启用查询和权重矩阵的LoRA,(2)启用所有层的LoRA,这两种设置进行了探索。在更多层的组合中使用LoRA会产生何种效果,值得深入研究。如果能知道在投影层使用LoRA对训练结果是否有益,那么我们就可以更好地优化模型,并提高其性能。如果我们考虑各种设置(lora_query,lora_key,lora_value,lora_projection,lora_mlp,lora_head),就有64种组合可供探索。Q5:如何避免过拟和?
一般来说,较大的r更可能导致过拟合,因为r决定着可训练参数的数量。如果模型存在过拟合问题,首先要考虑降低r值或增加数据集大小。此外,可以尝试增加AdamW或SGD优化器的权重衰减率,或者增加LoRA层的dropout值。我在实验中没有探索过LoRA的dropout参数(我使用了0.05的固定dropout率),LoRA的dropout参数也是一个有研究价值的问题。Q6:还有其他优化器作为选择吗?
除了精度和量化设置、模型大小、batchsize和可训练LoRA参数数量之外,数据集也会影响内存使用。Llama2的块大小为4048个token,这代表着Llama可以一次处理包含4048个token的序列。如果对后来的token加上掩码,训练序列就将变短,可以节省大量的内存。例如Alpaca数据集相对较小,最长的序列长度为1304个token。当我尝试使用最长序列长度达2048个token的其他数据集时,内存使用量会从17.86GB飙升至26.96GB。Q8:与全微调、RLHF相比,LoRA有哪些优势?
我没有进行RLHF实验,但我尝试了全微调。全微调至少需要2个GPU,每个GPU占用36.66GB,花费了3.5个小时才完成微调。然而,基线测试结果不好,可能是过拟合或次超优参数导致的。Q9:LoRA的权重可以组合吗?
答案是肯定的。在训练期间,我们将LoRA权重和预训练权重分开,并在每次前向传播时加入。假设在现实世界中,存在一个具有多组LoRA权重的应用程序,每组权重对应着一个应用的用户,那么单独储存这些权重,用来节省磁盘空间是很有意义的。同时,在训练后也可以合并预训练权重与LoRA权重,以创建一个单一模型。这样,我们就不必在每次前向传递中应用LoRA权重。
weight+=(lora_B@lora_A)*scaling我们可以采用如上所示的方法更新权重,并保存合并的权重。同样,我们可以继续添加很多个LoRA权重集: