为什么 LoRA 梯度是建立在全量参数 W 的梯度之上

🧠 首先搞清楚 LoRA 是怎么做微调的

我们原来要训练的参数矩阵是 WWW,但 LoRA 说:

别动 W,我在它旁边加一个低秩矩阵 ΔW=UV\Delta W = UVΔW=UV,只训练这个部分!

也就是说,LoRA 用一个新的权重矩阵:

W′=W+UV W' = W + UV W=W+UV

只训练 UUUVVVWWW 不动。


📦 所以前向传播其实用的是:

模型输入x⟶W′x=Wx+UVx⟶输出⟶L \text{模型输入}x \longrightarrow W'x = Wx + UVx \longrightarrow \text{输出} \longrightarrow \mathcal{L} 模型输入xWx=Wx+UVx输出L

在这个过程中,损失函数 L\mathcal{L}L 是基于 W+UVW + UVW+UV 来计算的。


🔁 反向传播的时候怎么求梯度?

LoRA 要训练的是 UUUVVV,所以我们要算:

∂L∂U和∂L∂V \frac{\partial \mathcal{L}}{\partial U} \quad \text{和} \quad \frac{\partial \mathcal{L}}{\partial V} ULVL

但问题是:损失函数 L\mathcal{L}L 不是直接依赖 UUUVVV,而是依赖 UVUVUV

所以要用链式法则,先对 UVUVUV 求导,然后传播回 UUUVVV。而对UV求导等价于对WWW求导


✅ 关键点来了

我们记:

∂L∂W=G \frac{\partial \mathcal{L}}{\partial W} = G WL=G

这个 GGG 就是“如果我们在做全量微调,该怎么更新 WWW 的梯度”。

LoRA 说:

“虽然我不更新 WWW,但我要更新的是 UVUVUV。所以我也可以用这个 GGG 来指导我怎么更新 UUUVVV。”

于是我们得到:

∂L∂U=GV⊤,∂L∂V=U⊤G \frac{\partial \mathcal{L}}{\partial U} = G V^\top, \quad \frac{\partial \mathcal{L}}{\partial V} = U^\top G UL=GV,VL=UG


LoRA 的梯度建立在 ∂L∂W\frac{\partial \mathcal{L}}{\partial W}WL 上, 是因为它相当于“用低秩矩阵 UVUVUV 来代替全量的参数更新”, 所以梯度传播也必须从 ∂L∂W\frac{\partial \mathcal{L}}{\partial W}WL 开始。
LoRA 往往只是显存不足的无奈之选,因为一般情况下全量微调的效果都会优于 LoRA,所以如果算力足够并且要追求效果最佳时,请优先选择全量微调。
使用 LoRA 的另一个场景是有大量的微型定制化需求,要存下非常多的微调结果,此时使用 LoRA 能减少储存成本。

🔍 为什么

为什么 ∂L∂W\frac{\partial \mathcal{L}}{\partial W}WL,就是对 UVUVUV 的梯度?

换句话说:LoRA 中的 W′=W+UVW' = W + UVW=W+UV,那我们训练时不是更新 WWW,只更新 UVUVUV,那为什么还能用 ∂L∂W\frac{\partial \mathcal{L}}{\partial W}WL 来指导 UUUVVV 的更新呢?


✅ 答案是:因为前向传播中 W+UVW + UVW+UV一起作为整体参与运算的

所以:

∂L∂W=∂L∂(W+UV)=∂L∂(UV) \frac{\partial \mathcal{L}}{\partial W} = \frac{\partial \mathcal{L}}{\partial (W + UV)} = \frac{\partial \mathcal{L}}{\partial (UV)} WL=(W+UV)L=(UV)L

这是因为:

  • 我们的模型使用的是 W+UVW + UVW+UV
  • 所以损失函数 L\mathcal{L}L 是以 W+UVW + UVW+UV 为输入计算出来的
  • 那么对 WWW 求导,其实是对这个整体求导
  • 而因为 WWW 是固定的(不训练,看作常数),所以梯度全部由 UVUVUV 来承接

  • 本来我们应该更新 WWW
    W←W−η∂L∂W W \leftarrow W - \eta \frac{\partial \mathcal{L}}{\partial W} WWηWL
  • 现在我们不动 WWW,让 UVUVUV 来“做这个事情”:
    W+UV←W+UV−η⋅(LoRA方向上的梯度) W + UV \leftarrow W + UV - \eta \cdot \left(\text{LoRA方向上的梯度}\right) W+UVW+UVη(LoRA方向上的梯度)

所以如果要算 UVUVUV 的导数,就是算 ∂L∂W\frac{\partial \mathcal{L}}{\partial W}WL

03-25
### LoRA(低秩适配)概述 LoRA 是一种用于高效微调大型语言模型的技术,其核心思想是在保持预训练权重冻结的情况下引入可训练的低秩矩阵来适应新任务。这种方法显著减少了需要优化的参数数量,同时能够维持较高的模型性能[^3]。 具体来说,在传统的全量微调方法中,所有的模型参数都会被更新,这不仅计算成本高而且容易过拟合到特定数据集上。相比之下,LoRA 只允许部分新增的小规模矩阵参与训练,这些矩阵通常具有较低维度(即所谓的“low-rank”),从而大幅降低了内存消耗和计算复杂度。 #### 训练过程中的效率改进 尽管 LoRA 提供了一种高效的解决方案,但在实际应用过程中发现增加 **lora_rank** 参数可能会导致训练速度变慢的情况发生。这是因为较大的 rank 值意味着更多的计算资源需求以及更复杂的梯度传播路径[^1]。不过针对这一局限性提出了改进版本——rsLoRA(Rank-Stabilized LoRA)。该算法通过对缩放因子进行调整使整个流程变得更加平稳可靠;更重要的是它消除了因改变 lora_rank 所带来的额外开销问题。换句话说,在启用 rsLoRA 后即使调节不同的 ranks 设置也不会再明显影响整体运行时间了。 另外值得注意的一点是如果决定采用 rsLoRA 方案,则只需简单设置 `use_rslora=True` 即可完成切换操作而不需要再去单独调整其他相关配置项比如 lora_rank 或者 lora_alpha 等等。 以下是实现标准 LoRA 方法的一个 Python 示例代码片段: ```python from peft import LoraConfig, get_peft_model config = LoraConfig( r=8, # The rank of the low-rank decomposition. alpha=32, target_modules=["q_proj", "v_proj"], # Specify which modules should have adapters added. bias="none", ) model = get_peft_model(pretrained_model, config) ``` 上述脚本展示了如何利用 Hugging Face 的 PEFT 库快速构建基于 Transformer 架构上的自定义 LoRA 配置对象,并将其应用于目标模块之中以便后续进一步定制化开发工作得以顺利开展下去。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

frostmelody

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值