一、核心原理与数学基础
一、 核心原理与数学基础¶
1. LoRA 的核心假设是什么?为什么模型微调中的权重更新可以是低秩的?¶
这个问题的答案分两层:一层是“是什么”,一层是“为什么它成立”。
第一层:核心假设¶
LoRA 的核心假设是:在一个已经充分预训练的大模型上,为了适配一个特定的下游任务,我们不需要调整模型的全部参数,只需要对权重矩阵做一个小的、低秩的扰动。
更精确地说,预训练模型的权重矩阵 $ W_0 $ 已经编码了丰富的通用知识。当它需要适应一个新任务时,其权重的变化量 $ \Delta W $ 可以被一个低秩矩阵很好地逼近。这个假设是整个方法的基石。
第二层:为什么这个假设成立?(内在思想)¶
这就触及到了深度学习的本质。Aghanjanyan 等人在 2020 年的研究为这个假设提供了重要的理论支撑。他们发现,过参数化的模型,其内在任务的“本质维度”其实很低。也就是说,模型实际上是在一个低维的子空间里学习。
你可以这样理解:一个在互联网海量数据上训练出来的语言模型,它的高维权重空间(可能有数十亿维)已经包含了语言理解、常识推理、语法结构等极其丰富的“知识基座”。现在,你要让它去完成“写一篇有夏目漱石风格的散文”这个下游任务。这个任务并需要颠覆它对整个世界的认知,它只需要在已有的、强大的文本生成能力之上,调整对特定风格、特定用词、特定语境的“注意力”即可。
这种调整是高度结构化的,而不是杂乱无章的。这个结构化的调整,在数学上就表现为一个低维子空间。LoRA的聪明之处就在于,它没去大海捞针般地找这个子空间,而是直接用两个小矩阵的乘积BA来显式地“创造”和“训练”出这个低秩的更新量。参数r就是我们对这个子空间维度的估计。
2. 写出 LoRA 的数学公式,并解释每一项的含义。¶
核心公式非常简洁:
$$ h=W_{0}x+\frac{\alpha}{r}\cdot B A x $$
我们拆开来看:
· h:这是经过 LoRA 适配后的当前层的输出,也是下一层的输入。
• $ W_0x $:这是原始预训练权重 $ W_0 $ 对输入 $ x $ 的计算结果。这部分是冻结的,不参与梯度更新。它保证了预训练模型学到的通用知识被完整保留,不会在微调中被破坏。这就像一个经验丰富的老师傅,他的手艺( $ W_0 $)是底子,不会轻易改变。
• BAx:这是 LoRA 的核心,即我们学习到的增量部分。A 和 B 是两个小巧的、可训练的矩阵。其中,A 负责将输入 x 从高维空间投影到一个低维空间,B 负责从这个低维空间再投影回原始的高维空间。这一降一升之间,捕捉的就是那个“低秩的适配变化”。这就像在老师傅手艺的基础上,加了一个非常精妙的小技巧或小工具,来适配这个新任务。
• $ \frac{\alpha}{r} $:这是一个缩放因子。它的设计非常巧妙。引入它之后,我们调整秩 r 时,就不需要像调整其他超参数那样同时去大规模调整学习率。因为 r 增大,虽然 BA 的量级容易变大,但系数 $ \alpha/r $ 会自动将其缩放回去,保持了训练的稳定性。在实践中,调大 $ \alpha $ 的效果,近似于在不改变其他超参数的情况下,等比例地放大了 LoRA 模块的学习率。
3. 为什么 LoRA 要把 B 初始化为 0,而 A 使用高斯初始化?¶
这个设计的核心目标是:在训练开始时,让 LoRA 模块的效果等同于原始的预训练模型,即 $ \Delta W = 0 $。
我们来看这个设计的精妙之处:
A 的初始化:使用高斯分布进行随机初始化。这为矩阵 A 的各个分量赋予了不同的、随机的初始值。这一步的目的是打破对称性,使得不同的维度(或者说不同的“特征方向”)在一开始就能捕捉到不同的信息,是后续能够有效学习的基础。
B 的初始化:全零初始化。这是实现“从零开始”的关键。因为 B = 0,所以无论 A 是什么,BA 的乘积严格为 0。这样,在第一步前向传播时,模型的输出 $ h = W_0 x + 0 = W_0 x $,完全等同于原始的预训练模型。
这个设计哲学是"先继承,再微调"。它表达了对预训练权重的绝对尊重。训练之初,我们完全不修改模型的行为。随着反向传播的进行,B不再是0,它会根据梯度信号,小心翼翼地让模型开始偏移,去学习那些专门服务于下游任务的特征。这比把A和B都随机初始化要稳定得多,因为如果A和B都随机初始化,一开始就会给模型一个随机的、可能很大的扰动,这会破坏预训练权重,导致训练不稳定甚至发散。
4. 系数 $ \alpha/r $ 中, $ \alpha $ 和 r 分别起什么作用?为什么调大 $ \alpha $ 近似于调大学习率?¶
我们先理清各自最基础的作用:
• r (秩): 它是我们为那个“低秩适配子空间”假设的维度。它决定了 A 和 B 矩阵的“宽度”。r 决定了 LoRA 模块的容量上限。
• α:它是一个纯粹的缩放因子,独立于 r,用来控制 LoRA 模块整体输出 BAx 的强度。α 决定了我们允许多大的“更新步幅”。
现在回答核心问题:为什么调大 $ \alpha $ 近似于调大学习率?
这可以从梯度的角度看。LoRA 模块中,真正更新的参数是 A 和 B。我们关注对它们的梯度缩放。为简化分析,我们考虑对其中一个矩阵,比如 B 的更新。损失函数 L 对 B 的梯度可以写成(忽略中间细节):
$$ \frac{\partial L}{\partial B}\propto\frac{\alpha}{r}\cdot( 来自输入和输出梯度的部分 ) $$
当你在优化器中更新参数时,更新量是学习率 $ \times $梯度。所以,B的实际更新量为:
$$ \Delta B=\eta\cdot\frac{\partial L}{\partial B}\propto\eta\cdot\frac{\alpha}{r} $$
可以看到,学习率 $ \eta $ 和 $ \alpha $ 以乘积的形式共同作用于参数的更新量。在 r 固定的前提下,将 $ \alpha $ 翻倍,对 LoRA 模块参数 A, B 的更新量产生的缩放效果,与将学习率 $ \eta $ 翻倍是完全等价的。
这就是它带来的便利性:当我们调整 r 时,可以固定 $ \alpha $(比如 $ \alpha = 2r $),这样 LoRA 输出的初始量级大致不变,我们只需要微调学习率 $ \eta $ 即可。而当我们想整体控制 LoRA 对模型的影响程度时,可以直接调整 $ \alpha $,这比在复杂的优化器调度中大幅修改学习率要简单、解耦得多。
5. 推导 LoRA 前向传播并说明反向传播中为什么只更新 A 和 B。¶
前向传播推导:
这部分比较直接,遵循“矩阵运算可加性”。
-
输入向量 x 同时进入两条路径。
-
冻结路径:直接与冻结的预训练权重 $ W_0 $ 计算,得到 $ h_{\text{pretrained}} = W_0 x $。
-
可训练路径(LoRA):
首先,x 经过 A 矩阵,被投影到低维空间: $ x_{low_dim} = Ax $。
然后,低维特征 $ x_{low_dim} $ 经过 B 矩阵,被投影回原始高维空间: $ h_{lora} = B(Ax) = BAx $。
最后,这个输出被缩放因子 $ \frac{\alpha}{r} $调制。
- 合井输出:最终输出 h 是两条路径的简单相加:
$$ h=W_{0}x+\frac{\alpha}{r}\cdot B A x $$
反向传播分析:
自动微分框架构建计算图时,会清楚记录每个操作。 $ W_0 $ 是参与前向计算的,所以梯度流会经过它。但是,PyTorch 允许将张量的 requires_grad 属性设为 False。
我们在定义 LoRA 时,显式地将 $ W_0 $ 的 requires_grad 设置为 False,将 A 和 B 的 requires_grad 设置为 True。
• 反向传播时,损失 $ L $ 的梯度 $ \frac{\partial L}{\partial h} $ 会通过加法节点,无损地传递给 $ \frac{\partial L}{\partial(W_0x)} $ 和 $ \frac{\partial L}{\partial(\frac{\alpha}{r}BAx)} $。
在 $ W_0x $ 这条路径上,虽然计算图连通,但框架在计算到 $ W_0 $ 时,会检查其 requires_grad 属性。发现是 False,便停止计算其梯度,并且不会为其分配存储梯度的内存空间。
而在 $ \frac{\alpha}{r}BAx $ 这条路径上,框架会继续追溯,根据链式法则,计算出 $ \frac{\partial L}{\partial B} $ 和 $ \frac{\partial L}{\partial A} $,并更新这两个矩阵。
因此, $ 2014 $之所以大变样 $ ^{①} $和 $ ^{②} $,不是因为数字上遵守 $ ^{③} $的梯度,而是我们通过往来机制,主动选择不计算、不存储它的梯度。这带来了巨大的显存节省。
6. 从梯度角度分析,LoRA 的训练和全量微调相比,更新矩阵的梯度有何本质不同?¶
这个问题触及了两种方法最根本的差异:参数更新的自由度。
在全量微调中,对于一个权重矩阵 W,它的更新是完全自由的。梯度 $ \frac{\partial L}{\partial W} $ 是一个和 W 形状完全相同的矩阵,没有任何约束。优化器可以独立地、任意地调整 W 中的每一个参数。参数更新的轨迹是高维空间中的任意路径。
而在LoRA中,情况截然不同。尽管最终作用在模型上的更新增量是 $ \Delta W = BA $,但这并不是参数本身,参数是 A 和 B。我们来看 $ \Delta W $ 的更新是如何进行的。
考虑参数 A 和 B 的一步更新:
$$ B^{\prime}=B-\eta\frac{\partial L}{\partial B} $$
$$ A^{\prime}=A-\eta\frac{\partial L}{\partial A} $$
那么,等效的权重的更新 $ \delta W_{lora} $ 是:
$$ \delta W_{lora}=B^{\prime}A^{\prime}-BA $$
这个表达式可以展开为:
$$ \delta W_{lora}=\underbrace{(-\eta B\frac{\partial L}{\partial A})}{ 项 1}+\underbrace{(-\eta\frac{\partial L}{\partial B}A)} $$ }+\underbrace{(\eta^{2}\frac{\partial L}{\partial B}\frac{\partial L}{\partial A})}_{ 高阶项
(通常高阶项很小可以忽略,但它存在)
这个推导得出的结论是深刻的:LoRA 对最终权重产生的等效更新 $ \delta W_{lora} $,是被严格限制的。它的每一行都位于由矩阵 A 的行向量和张成的空间里,它的每一列都位于由矩阵 B 的列向量张成的空间里。换句话说,全量微调的梯度更新是“无拘无束”的,而 LoRA 的等效梯度更新是“戴着镣铐跳舞”的,它只能在由当前 A 和 B 的向量所定义的、秩最高为 r 的低维子空间里进行。
这既是 LoRA 泛化能力好的原因(不容易过拟合),也是它在某些极难任务上可能不如全量微调的原因(子空间限制了其逼近最优解的能力)。
7. 为什么说 LoRA 不引入额外的推理延迟?请通过矩阵合并的数学等价性说明。¶
这是 LoRA 最优雅的工程特性之一。其核心在于矩阵乘法的线性性质。
推理时,我们拿到的是一个已经训练好的 LoRA 模块。此时,A 和 B 已经是固定的矩阵,不再是变量。我们来看前向传播公式:
$$ h=W_{0}x+\frac{\alpha}{}-B A x $$
根据矩阵乘法对加法的分配律,我们可以提取公因子 x:
$$ h=(W_{0}+\frac{\alpha}{r}BA)x $$
合并过程:我们只需在推理前,做一次矩阵加法运算,计算出一个新的权重矩阵:
$$ W_{merged}=W_{0}+\frac{\alpha}{r}BA $$
然后,用这个 $ W_{merged} $ 去替换原来的 $ W_{0} $。那么推理时的计算就变成了:
$$ h=W_{m e r g e d}x $$
这个计算过程和你直接使用原始的预训练模型没有任何区别,都只包含一次矩阵-向量乘法。
结论是:
延迟不变:推理过程不会多出任何一步计算,没有额外的网络分支。
架构不变:你不需要修改任何模型的代码或结构。
· 显存不增:你不用在显存中额外加载一个分离的 LoRA 模块。
这就像给一辆车加了一个涡轮增压器,在比赛前(训练时)你可以单独调试它。但一旦调好,你把它完美地焊死在引擎上(合并权重),赛道上跑的时候,它并不会成为额外的负担。与Adapter那样串联在通路上的方法相比,LoRA的这一优势是碾压级的。Adapter的推理延迟是“永久”的,因为它就在计算路径上,拿不掉。
8. 如果基座模型已经是量化的(如 INT4),LoRA 参数可以保持 FP16 吗?合并时会遇到什么问题?¶
第一个问题的答案:完全可以,而且这正是 QLoRA 的核心做法。
基座模型量化(INT4)的目的是为了省显存,让它能加载起来。而 LoRA 的参数(A 和 B)作为微调中主要的更新来源,保持较高的精度(FP16 或 BF16)是保证训练精度和稳定性的关键。两者的分工非常明确:
• $ W_{0} $ 用 INT4,负责以极低的显存代价提供“知识基座”和计算。
A 和 B 用 FP16,负责以较高的精度学习“更新量”。
在训练时,计算流程是这样的:输入 x 过来,先和反量化回 FP16 的 $ W_0 $ 做矩阵乘(或者直接用 INT4 算,这是框架优化的事),然后再和 FP16 的 $ BAx $ 结果相加。这个过程没有任何问题。
第二个问题:合并时的挑战,这是问题的关键。
推理时,我们想把 FP16 的 $ \frac{\alpha}{r}BA $ 合并进已经量化成 INT4 的 $ W_0 $ 里,得到一个“INT4 的 $ W_{merged} $”,这个理想情况在数学上是不封闭的。
主要会遇到以下几个问题:¶
-
精度不匹配:这是最核心的矛盾。你不能把一个高精度浮点数(FP16 的 LoRA 增量)无损地加到一个低精度定点数(INT4 的权重)上,并希望结果还能用 INT4 完美表示。强行合并再量化,会引入二次量化误差,可能导致模型性能骤降。
-
量化参数失效:原始的 $ W_0 $ 在 INT4 格式下,有其配套的量化缩放因子(scale)和零点(zero-point)。加上一个 $ \Delta W $ 后,这些量化参数就不再是统计最优的了。你需要对整个合并后的矩阵重新进行量化校准(Calibration),这个过程不仅费时,而且得到的模型质量不一定好。
那么实践中通常怎么处理?¶
- 分离式部署(最常用):这也是大部分推理框架(如 vLLM, TGI)支持 LoRA 的方式。模型权重保持 INT4 不变,同时加载一个 FP16 的 LoRA 适配器。推理时,计算图里保留两个分支:一个 INT4 的 $ W_0x $,一个 FP16 的 $ \frac{\alpha}{r}BAx $,然后相加。这确实引入了一点点额外的矩阵乘法和加法开销,但是为了保持模型精度,这点代价通常是值得的。
先反量化再合并(特定场景):如果硬件资源允许,你可以在运行时把 INT4 的 $ W_{0} $ 反量化为 FP16,然后执行 FP16 的矩阵加法合并,得到一个 FP16 的 $ W_{merged} $。这样做没有精度损失,但失去了 INT4 省显存的意义。
• 重新量化(最彻底,但风险高):合并为 FP32 后,针对新权重重新执行一遍完整的量化流程(GPTQ/AWQ)。效果好,但流程复杂,一般只在新模型发布或大规模部署时使用。
所以,LoRA 和量化的结合,在训练时是完美搭档,分别解决显存和精度问题。但在推理部署时,它们之间的张力——“要精度”还是“要速度/省显存”——就显现出来了,而分离式部署成为了当前工业界平衡这两者的标准答案。