LoRA原理精讲

本文面向刚准备学习人工智能的同学,对LoRA(Low-Rank Adaptation,低秩适应)进行一个简单的介绍,帮助大家理解LoRA的基本原理。阅读本文需要了解高数和线代的基础知识,最好先阅读过MLP和transformer的文章,因为LoRA是针对大模型微调的一种技术。

为什么需要LoRA

在transformer的文章中,我们学习了注意力机制和transformer的基本原理。随着模型规模的增大,比如GPT-3有1750亿个参数,如何让这些大模型适应特定的下游任务就成了一个重要的问题。

传统的方法是全参数微调(Full Fine-tuning),也就是把预训练模型的所有参数都拿来重新训练。这种方法虽然效果好,但存在几个问题:

存储成本高。每个下游任务都需要保存一份完整的模型参数。假设有100个不同的任务,就需要存储100个1750亿参数的模型,这在实际应用中是不可接受的。

计算资源消耗大。更新所有参数需要大量的GPU内存和计算时间。

灾难性遗忘。全参数微调容易让模型忘记预训练时学到的通用知识,过度适应新任务。

那么,有没有一种方法,既能达到全参数微调的效果,又能大幅减少需要训练的参数量呢?LoRA就是来解决这个问题的。

LoRA的基本原理

LoRA的想法很简单:虽然预训练模型的参数量很大,但在适应特定任务时,真正需要改变的部分其实很小。换句话说,模型参数的变化具有低秩性。

什么是低秩?

在介绍LoRA之前,我们先回顾一下矩阵的秩。矩阵的秩是指矩阵中线性无关的行或列的最大数量。

举个例子,假设有一个1000×10001000 \times 10001000×1000的矩阵WWW,它的秩是rrr,其中r≪1000r \ll 1000r≪1000。这意味着这个矩阵虽然看起来很大,但实际上可以用两个小矩阵的乘积来表示:

W=A⋅BW = A \cdot BW=A⋅B

其中AAA的形状是1000×r1000 \times r1000×r,BBB的形状是r×1000r \times 1000r×1000。这样,原本需要存储1000×1000=100万1000 \times 1000 = 100万1000×1000=100万个参数,现在只需要存储1000×r+r×1000=2000r1000\times r + r \times 1000 = 2000r1000×r+r×1000=2000r个参数。如果 r=8r=8r=8,那么参数量就从100万减少到了1.6万,减少了98%以上。

LoRA的数学原理

在传统的微调中,预训练模型的权重矩阵W0W_0W0会直接更新为W0+ΔWW_0 + \Delta WW0+ΔW,其中ΔW\Delta WΔW是通过训练学习到的变化量。

LoRA的做法是:不直接学习ΔW\Delta WΔW,而是假设ΔW\Delta WΔW可以分解为两个低秩矩阵的乘积:

ΔW=B⋅A\Delta W = B \cdot AΔW=B⋅A

其中BBB的形状是d×rd \times rd×r,AAA的形状是r×kr \times kr×k,rrr是一个很小的数,通常取4、8、16等。

这样,前向传播的公式就变成了:

h=W0⋅x+ΔW⋅x=W0⋅x+B⋅A⋅xh = W_0 \cdot x + \Delta W \cdot x = W_0 \cdot x + B \cdot A \cdot xh=W0⋅x+ΔW⋅x=W0⋅x+B⋅A⋅x

其中W0W_0W0是预训练模型的原始权重,在微调过程中冻结不变;AAA和BBB是需要训练的低秩矩阵,参数量非常小;xxx是输入,hhh是输出。

为什么这样设计有效?

你可能会问:为什么假设ΔW\Delta WΔW是低秩的就有效呢?

一个解释是过参数化假说:深度学习模型通常是过参数化的,模型中的很多参数其实是冗余的,真正对任务有用的信息可能只存在于一个低维的子空间中。

另一个解释是任务特异性:预训练模型已经学到了通用的知识,适应特定任务只需要在原有的基础上做微小的调整。这个调整可以理解为在参数空间中的一个小范围移动,而这个移动可以用低秩矩阵来近似。

实验表明,LoRA在大多数任务上都能达到与全参数微调相当的效果,但参数量却减少了1000倍甚至更多。其实简单说来就是这么做有效,这样的说法完全是建立在实际的实验结果上的。是为了解释这样的实验结果,所以才提出了这样的设计。

LoRA的架构设计

初始化策略

LoRA的一个关键设计是初始化策略。在训练开始时,我们希望B⋅A⋅x=0B \cdot A \cdot x = 0B⋅A⋅x=0,这样模型就完全等同于原始的预训练模型,不会破坏预训练的效果。

为了实现这一点,LoRA采用了以下初始化方法:矩阵AAA使用随机高斯分布初始化,矩阵BBB初始化为全0。

这样,在训练开始时,B⋅A=0B \cdot A = 0B⋅A=0,模型的行为与预训练模型完全一致。随着训练的进行,BBB逐渐学习到有用的信息,模型开始适应新任务。

应用于哪些层?

在transformer模型中,LoRA通常应用于注意力层的权重矩阵,包括WqW_qWq(Query的投影矩阵)、WkW_kWk(Key的投影矩阵)、WvW_vWv(Value的投影矩阵)和WoW_oWo(Output的投影矩阵)。

当然,也可以应用于前馈神经网络(FFN)的权重矩阵。实验表明,只对WqW_qWq和WvW_vWv应用LoRA,就能在大多数任务上取得很好的效果。

缩放因子

在实际应用中,LoRA还会引入一个缩放因子α\alphaα,公式变为:

h=W0⋅x+αr⋅B⋅A⋅xh = W_0 \cdot x + \frac{\alpha}{r} \cdot B \cdot A \cdot xh=W0⋅x+rα⋅B⋅A⋅x

其中α\alphaα是一个超参数,通常设置为rrr的倍数(比如α=2r\alpha = 2rα=2r或α=16\alpha = 16α=16)。这个缩放因子的作用是当我们调整rrr的大小时,不需要重新调整学习率,αr\frac{\alpha}{r}rα可以看作是对LoRA分支的权重控制。

参数量对比

让我们来算一笔账。假设一个transformer模型的隐藏维度d=4096d = 4096d=4096,注意力头的数量为32。

对于WqW_qWq矩阵,其形状为4096×40964096 \times 40964096×4096,参数量为16,777,21616,777,21616,777,216个。

如果使用LoRA,设置r=8r = 8r=8,那么矩阵AAA的形状为8×40968 \times 40968×4096,参数量为32,76832,76832,768;矩阵BBB的形状为4096×84096 \times 84096×8,参数量为32,76832,76832,768;总参数量为655366553665536。

参数量减少了16,777,21665,536≈256\frac{16,777,216}{65,536} \approx 25665,53616,777,216≈256倍。

如果对Wq,Wk,Wv,WoW_q,W_k,W_v,W_oWq,Wk,Wv,Wo四个矩阵都应用LoRA,参数量也只增加了65,536×4=26214465,536 \times 4 = 26214465,536×4=262144个,相比原始模型的数十亿参数,只占很小一部分。

LoRA的训练过程

LoRA的训练过程可以分为以下几个步骤:

1. 冻结预训练权重

首先,加载预训练模型的权重W0W_0W0,并将其设置为requires_grad = False,这样在训练过程中就不会更新这些权重。

2. 初始化LoRA矩阵

对于需要应用LoRA的每一层,初始化两个矩阵AAA和BBB:AAA使用随机高斯分布初始化,均值为0,标准差为σ\sigmaσ(通常取σ=0.01\sigma = 0.01σ=0.01);BBB初始化为全0。

3. 前向传播

在前向传播时,输入xxx会同时经过两条路径:原始路径h1=W0⋅xh_1 = W_0 \cdot xh1=W0⋅x,LoRA路径h2=αr⋅B⋅A⋅xh_2 = \frac{\alpha}{r} \cdot B \cdot A \cdot xh2=rα⋅B⋅A⋅x。最终的输出为h=h1+h2h = h_1 + h_2h=h1+h2。

4. 反向传播

在反向传播时,只需要计算损失函数对AAA和BBB的梯度,并更新这两个矩阵。预训练权重W0W_0W0保持不变。

5. 合并权重

在训练完成后,可以将B⋅AB \cdot AB⋅A合并到W0W_0W0中,得到新的权重W=W0+αr⋅B⋅AW = W_0 + \frac{\alpha}{r} \cdot B \cdot AW=W0+rα⋅B⋅A。这样做的好处是推理时不需要额外的计算,与原始模型完全一致,不会增加推理延迟。

当然,也可以选择不合并,这样可以在同一个预训练模型上训练多个LoRA,根据不同的任务切换不同的LoRA权重。

LoRA的变体和扩展

自从LoRA被提出以来,研究者们提出了许多改进和扩展版本。下面介绍几个重要的变体。

AdaLoRA

AdaLoRA(Adaptive LoRA)的想法是:不同的层、不同的参数对任务的贡献是不同的,因此应该分配不同的秩。

在标准的LoRA中,所有层的秩rrr都是相同的。但AdaLoRA认为某些层可能需要更大的秩来捕捉复杂的任务特征,某些层可能只需要很小的秩就能达到很好的效果。

AdaLoRA通过一个重要性评分机制,动态地调整每个LoRA模块的秩。具体来说,对每个LoRA矩阵的重要性进行评分,根据重要性评分动态地增加或减少秩,将不重要的秩"剪枝"掉,节省参数量。

实验表明,AdaLoRA在相同参数量的情况下,比标准LoRA效果更好。

QLoRA

QLoRA(Quantized LoRA)是一种结合了量化和LoRA的技术,目的是进一步降低微调大模型的内存需求。

QLoRA的做法是将预训练模型量化为4-bit精度(从16-bit降低到4-bit,内存减少75%),使用特殊的量化方法(NormalFloat4)保证量化后的模型性能不下降,然后在量化后的模型上应用LoRA进行微调。

QLoRA可以在单张GPU上微调65B参数的大模型,几乎不损失性能,训练速度与标准LoRA相当。

LoRA+

LoRA+是对LoRA的一种简单但有效的改进,对矩阵AAA和BBB使用不同的学习率。

在标准LoRA中,AAA和BBB使用相同的学习率。但LoRA+发现矩阵BBB的学习速度应该比AAA快,设置ηB=λ⋅ηA\eta_B = \lambda \cdot \eta_AηB=λ⋅ηA,其中λ\lambdaλ通常取2到16之间的值。这个简单的改进可以显著提高训练效率和最终性能。

DoRA

DoRA(Weight-Decomposed Low-Rank Adaptation)是2024年提出的一种新方法,将权重分解为幅度和方向两部分。

具体来说,DoRA将权重矩阵WWW分解为:

W=m⋅V∥V∥W = m \cdot \frac{V}{\|V\|}W=m⋅∥V∥V

其中mmm是幅度向量,控制每个输出神经元的整体强度;VVV是方向矩阵,控制权重的方向。

DoRA对这两部分分别应用LoRA:幅度mmm使用一个一维的LoRA,方向VVV使用标准的LoRA。实验表明,DoRA在多个任务上都优于标准LoRA,尤其是在参数量较小的情况下。

总结

恭喜你看到这里,学有余力的同学还可以思考一下:

  1. 为什么LoRA的初始化策略是B=0B=0B=0而不是A=0A=0A=0?如果反过来会怎样?

  2. LoRA的秩rrr越大,效果一定越好吗?为什么?

对于第一个问题,如果A=0A=0A=0,那么B⋅A=0B \cdot A = 0B⋅A=0,看起来也能达到初始时不改变模型行为的目的。但是,在反向传播时:

如果B=0B=0B=0,那么∂(B⋅A)∂A=BT\frac{\partial (B \cdot A)}{\partial A} = B^T∂A∂(B⋅A)=BT,初始时为0,但∂(B⋅A)∂B=A\frac{\partial (B \cdot A)}{\partial B} = A∂B∂(B⋅A)=A,初始时不为0,可以正常训练。

如果A=0A=0A=0,那么∂(B⋅A)∂A=BT\frac{\partial (B \cdot A)}{\partial A} = B^T∂A∂(B⋅A)=BT,初始时不为0,可以训练,但∂(B⋅A)∂B=A\frac{\partial (B \cdot A)}{\partial B} = A∂B∂(B⋅A)=A,初始时为0,无法训练。

所以,选择B=0B=0B=0是为了保证训练可以正常开始。

对于第二个问题,秩rrr越大,LoRA的表达能力越强,但也更容易过拟合。同时,更大的rrr意味着更多的参数,失去了LoRA的优势。因此,需要根据具体任务来选择合适的rrr,而不是一味地增大。

希望这篇文章能帮助你理解LoRA的基本原理和应用。如果你有任何问题,欢迎继续探索和讨论!

相关推荐
IronMurphy2 小时前
【算法三十一】46. 全排列
算法·leetcode·职场和发展
czlczl200209252 小时前
力扣1911. 最大交替子序列和
算法·leetcode·动态规划
晚秋贰拾伍2 小时前
科技周刊08-微博上线国内社交平台首个AI社区
人工智能·科技
小陈工2 小时前
2026年3月28日技术资讯洞察:5G-A边缘计算落地、低延迟AI推理革命与工业智造新范式
开发语言·人工智能·后端·python·5g·安全·边缘计算
openFuyao2 小时前
openFuyao亮相KubeCon Europe 2026 携InferNex套件深耕AI云原生推理领域
人工智能·云原生
剑穗挂着新流苏3122 小时前
203_深度学习的第一步:线性回归模型与 SGD 优化算法实战
人工智能·深度学习·机器学习
泯泷2 小时前
当AI排行榜成为一场数字游戏
人工智能·产品
神一样的老师2 小时前
【RT-Thread Titan Board 开发板】家庭AI相框
人工智能
靴子学长2 小时前
Decoder only 架构下 - KV cache 的理解
pytorch·深度学习·算法·大模型·kv