【LLM】低秩矩阵LoRA的使用指南

78 阅读5分钟

为什么LoRA中低秩矩阵是一个优势?

前一篇文章【LLM】为什么现在的LLM都是Decoder-only架构中,我们在讨论 Attention 矩阵时说“满秩好,低秩意味着能力坍塌”,但到了 LoRA(Low-Rank Adaptation)这里,怎么“低秩”反而变成优势了呢?

这里关键在于:区分“原本的模型知识”和“针对新任务的增量改变”


2020 年,Facebook AI 的研究(Aghajanyan et al.)发现了一个反直觉的现象: 虽然大模型参数量巨大(过参数化),但在微调特定任务时,模型权重的实际变化量,其实只需要在一个极低维的空间里游走就够了。

类比:

想象你是一个受过高等教育的百科全书式全才(这就是预训练好的大模型 WW,高秩,什么都懂)。

现在我要你学一个具体的任务: “如何做回锅肉”

  • 全量微调(Full Fine-tuning): 相当于要把你的大脑神经元全部重塑一遍。虽然你最终学会了做菜,但动静太大,效率极低,甚至可能让你忘了怎么解微积分(灾难性遗忘)。
  • LoRA(低秩微调): 我不需要重塑你的大脑。我发现,要从“全才”变成“厨师”,其实只需要在你的知识库里增加一小部分特定的指令(比如“多放豆瓣酱”)。这个“特定的增量”,相对于你浩瀚的知识库来说,是非常简单的、维数很低的。

结论: 模型本身需要高秩(WW)来存储海量世界知识,但适应某个具体任务所需的变化量(ΔW\Delta W ,在数学上是低秩的。


LoRA公式推导

传统微调(Full Fine-tuning)

假设预训练的权重矩阵是 W0Rd×kW_0 \in \mathbb{R}^{d \times k}(比如 4096×40964096 \times 4096)。

微调后的权重是 Wnew=W0+ΔWW_{new} = W_0 + \Delta W

在全量微调中,ΔW\Delta WW0W_0 一样大,也是 4096×40964096 \times 4096,包含 1600万 个参数。

LoRA 微调

既然根据“内在维度假说”,ΔW\Delta W 是低秩的,那我们为什么不直接把 ΔW\Delta W 拆解成两个小矩阵相乘呢?=》 ΔW=BA\Delta W = B \cdot A

  • BRd×rB \in \mathbb{R}^{d \times r} (升维矩阵,通常初始化为0)
  • ARr×kA \in \mathbb{R}^{r \times k} (降维矩阵,通常是高斯分布初始化)
  • rr 是秩(Rank) ,且 rdr \ll d(比如 r=8r=8)。

算笔账(效率的来源):

如果 d=4096d=4096,我们取 r=8r=8

  • AA 矩阵参数量: 8×4096=32,7688 \times 4096 = 32,768
  • BB 矩阵参数量: 4096×8=32,7684096 \times 8 = 32,768
  • LoRA 总参数量: 6.56.5 万。

对比:

  • 全量微调:1600 万参数。
  • LoRA 微调:6.5 万参数。

LoRA 仅需训练原参数量的 0.04%,就能达到几乎一致的效果。 这就是“利用 ΔW\Delta W 是低秩的”带来的巨大红利。

高秩模型VS低秩补丁

回到之前的疑问: “Decoder 矩阵满秩意味着能力强,为什么这里要搞低秩?”

请注意 LoRA 的最终计算公式:

h=W0x+ΔWx=W0x+B(Ax)h = W_0 x + \Delta W x = W_0 x + B(Ax)

  1. 底座 W0W_0 依然是满秩的: 我们完全没有动预训练好的 W0W_0。它依然保持着上一节提到的“下三角满秩”特性,依然拥有强大的泛化表达能力和上下文理解能力。
  2. 补丁 BABA 是低秩的: 我们只是在原本强大的底座上,旁路加了一个极其轻微的引导信号。

形象解释:

  • 满秩的 W0W_0 是一艘在大海航行的巨轮(GPT),它的结构保证了它能抗住各种风浪(处理复杂语言)。
  • 低秩的 BABA 是这艘巨轮上的一个小小的。我们不需要重建这艘船,只需要轻轻转动这个“低秩”的舵,船(模型输出)就会驶向我们想要的新方向(垂直领域任务)。

如果 ΔW\Delta W 也是高秩的会怎样?

那就意味着这个新任务极其复杂,复杂到和预训练学到的所有东西都完全不同,必须彻底重组大脑。但在 LLM 实践中,大多数下游任务(如写代码、医疗问答)都共享通用的语言逻辑,所以低秩的 ΔW\Delta W 足够了。

  • LoRA中动态的任务更新量(ΔW\Delta W) 是低秩的,是为了“效率”,利用大模型已有的泛化能力,只学习最关键的差异。

如何科学地设置 LoRA 的 rralpha

在 LoRA 的实践中,很多时候大家是凭感觉调参,但其实只要看懂了它的缩放公式,就能找到科学的设定依据。LoRA 的核心更新公式:

ΔW=αr(BA)\Delta W = \frac{\alpha}{r} (B \cdot A)

这里有两个变量,rr 是秩,BAB \cdot A 是低秩矩阵乘积;αr\frac{\alpha}{r} 是一个缩放系数(Scaling Factor)rr 决定了模型适应新任务时,能学到的特征的“复杂度”或“维度”。

1. 理论直觉

  • rr 很小(如 4, 8): 假设新任务只是改变“说话的语气”或“格式”(例如:把普通回答变成莎士比亚风格,或者变成 JSON 格式)。这种任务不涉及复杂的逻辑推理,不需要太大的自由度,极小的 rr 就够了。
  • rr 很大(如 64, 128): 假设新任务是学习“全新的知识”(例如:一种原本模型没见过的编程语言,或者极其专业的生物化学机理)。这时你需要更大的几何空间来存储这些新信息。

2. 科学结论(来自微软原论文及后续实验)

  • 边际效应递减: 许多实验表明,将 rr 从 8 增加到 64,效果提升往往微乎其微。甚至在某些任务上,r=1r=1 都能达到很好的效果。
  • 过大的 rr 的风险: rr 越大,参数越多,越容易过拟合(Overfitting) ,且训练时的显存开销和噪声也会增加。

一般的,默认从 r=8r=8r=16r=16 开始。这对于大多数指令微调(Instruction Tuning)任务已经足够。

何时增加: 只有当你发现模型在训练集上 loss 降不下去(欠拟合),且你需要注入大量全新领域知识时,再尝试增加到 64 或 128。

如何理解 α\alpha?

α\alpha 本质上是对更新量 ΔW\Delta W 的一种权重放大(或缩小)

1. 为什么要有 αr\frac{\alpha}{r} 这个缩放项?

这是一个数学上的“归一化”技巧。当我们改变 rr 的大小时,矩阵 AABB 的元素数值分布会发生变化。

  • 如果没有 1r\frac{1}{r}:每次调整 rr,你都得重新去调学习率(Learning Rate),因为梯度的模长变了。
  • 有了 αr\frac{\alpha}{r}:它自动平衡了 rr 变化带来的标度影响。
2. α\alpha 与学习率(Learning Rate)的关系

α\alpha 在数学上等价于调整学习率。 模型权重的实际更新步长大约是:

Effective LR=Base LR×αr\text{Effective LR} = \text{Base LR} \times \frac{\alpha}{r}

如果你把 α\alpha 翻倍,就约等于把学习率翻倍。

科学设置建议:
  • 黄金法则(Golden Rule): 大多数开源社区(如 HuggingFace PEFT)和论文的经验法则是将 α\alpha 设置为 rr 的倍数。

    • 常见设置 A: α=r\alpha = r (即缩放系数为 1)。
    • 常见设置 B(推荐): α=2×r\alpha = 2 \times r (即缩放系数为 2)。这样可以稍微放大 LoRA 的更新权重,让微调收敛更快。
  • 固定 α\alpha 策略: 有一种更科学的调参流派建议:直接把 α\alpha 锁死(比如固定 16 或 32),然后只调 rr

    • 因为根据公式,如果固定 α\alpha,当你增加 rr 时,缩放系数 αr\frac{\alpha}{r} 会变小。这正好符合直觉: rr 越大(参数越多),我们越希望每次更新的步子迈得小一点,以防破坏预训练权重;rr 越小,我们需要步子迈大一点来确保学到东西。

如果将两者结合,科学的调参流程如下:

参数推荐起步值调整逻辑 (第一性原理)
Rank (rr)816决定“容量” 。如果是简单的风格/格式迁移,用小 rr;如果是复杂的推理/新知识注入,尝试大 rr(如 64)。不要盲目追求大 rr
Alpha (α\alpha)1632决定“力度” 。最稳妥的做法是保持 α=2r\alpha = 2r 或者 α=r\alpha = r
Learning Rate2e-4 ~ 5e-5LoRA 的学习率通常比全量微调要大。因为只有少量参数在动,大的学习率能帮助快速跳出局部最优。

举例:

  • 场景一:让 LLM 学会按特定 JSON 格式输出。

    • 这是一个简单的语法约束任务。
    • 设置: r=8,α=16r=8, \alpha=16 (Ratio=2)。
  • 场景二:让 LLM 学会一种从未见过的法律条文分析。

    • 这需要记忆新知识和复杂逻辑。
    • 设置: 尝试 r=64,α=128r=64, \alpha=128 (Ratio=2)。
    • 注意:如果显存不够,可以尝试 r=64,α=64r=64, \alpha=64 (Ratio=1) 并适当调小 Learning Rate。

这里有一个常见的误区: “我觉得模型学得太慢,所以我把 α\alpha 调得特别大。”

这通常会导致训练不稳定(Loss 震荡) 。如果你觉得学得慢,优先调整 Learning Rate,而不是去疯狂动 α\alpha,因为 α\alpha 会影响梯度的数值稳定性。


如何设置 Target Modules?

除了 rrα\alpha,LoRA 还有一个极其影响效果的参数:Target Modules(目标模块)

Target Modules(目标模块) 指的是:在 Transformer 的层级结构中,我们到底要把 LoRA 的低秩矩阵(AABB)挂载到哪些具体的权重矩阵上?

这不仅仅是一个“选勾”的问题,它直接决定了微调能触及模型能力的哪一部分。

一、 候选模块

为了科学选择,我们先得拆解一个标准的 Transformer Block(以 Llama 架构为例),看看里面有哪些矩阵可以调整:

  1. Attention 模块(注意力层): 负责“看哪里”和“整合信息”。

    • q_proj (Query): 查询矩阵。
    • k_proj (Key): 键矩阵。
    • v_proj (Value): 值矩阵。
    • o_proj (Output): 输出投影矩阵。
  2. MLP 模块(前馈神经网络层): 负责“思考”和“知识存储”。

    • gate_proj: 门控层。
    • up_proj: 升维层。
    • down_proj: 降维层。

二、 演进史

关于 Target Modules 的选择,学术界和工业界的最佳实践经历了一个明显的演变过程。

第一阶段:LoRA 原作时期(仅 Q, V)

  • 做法: 最初的 LoRA 论文(Hu et al.)主要针对 q_projv_proj 进行微调。
  • 逻辑: 只要改变了 Query(查什么)和 Value(取什么),就能改变模型的注意力模式。这是一种极其节省参数的做法。
  • 效果: 能够应对简单的指令跟随,但在复杂推理任务上表现一般。

第二阶段:QLoRA 与 全模块微调(All Linear)

  • 做法: 现在的标准操作(如 QLoRA 论文、Llama-Factory 默认设置)是将 LoRA 挂载到所有的线性层(All Linear Layers) ,即上述 7 个模块全选。
  • 逻辑: 由于Attention 只是负责信息的路由(Routing),而 MLP 才是真正存储知识和处理逻辑的地方。如果你只改 Attention,相当于你只教模型“怎么读题”,但没教它“怎么解题”。
  • 效果: 效果显著提升,尤其是在逻辑推理、代码生成和领域知识注入方面,逼近全量微调的效果。

三、 为什么 MLP 层如此重要?

如果你犹豫是否要勾选 MLP 层(gate, up, down),请看以下原理:

1. 知识存储假说 (Knowledge Storage)

孟德(Meng et al.)等人的研究表明,Transformer 中的 MLP 层类似于键值存储(Key-Value Memories) ,模型学到的大部分事实性知识(Fact)都存储在 MLP 的权重中。

  • 结论: 如果你的微调目的是让模型学会新的领域知识(比如企业内部规章、医学术语),你必须微调 MLP 层。只调 Attention 很难注入新知识。

2. 信息处理容量

Attention 机制本质上是在做加权平均(把上下文的信息聚合起来)。

MLP 层本质上是在做非线性变换(特征的重新组合与提取)。

  • 结论: 复杂任务(如数学推理、复杂的逻辑判断)需要强大的特征变换能力。如果锁死 MLP 不动,LoRA 的低秩矩阵往往“独木难支”,无法在数学上模拟出足够复杂的函数变换。

四、 科学设置建议

在实际工程中,你可以参考以下决策矩阵:

场景推荐 Target Modules理由资源消耗
通用指令微调All Linear (q,k,v,o,gate,up,down)首选方案。 最大限度释放模型潜力,防止短板效应。显存占用略高(需存储更多梯度),但在量化微调(QLoRA)下完全可接受。
简单风格迁移Attention (q,v)任务仅仅是改变说话语气(如“扮演猫娘”),不需要动用深层逻辑。极低,速度最快。
显存极其受限Attention + o_projo_proj 对整合输出很重要,性价比略高于纯 q,v低。
词表扩充All Linear + lm_head, embed_tokens特殊情况。 如果你增加了很多新 Token(如中文词表扩充),必须把输入输出层也加入训练。较高,且 embed_tokens 通常不使用 LoRA,而是全量训练。

五、 避坑指南

  1. 参数量恐慌:

    很多人看到“All Linear”会担心:“天哪,参数量是不是要爆炸了?”

    其实不会。 即使开启所有线性层,LoRA 的参数量通常也只占原模型的 1%~2% 左右。相比于从 0.1% 提升到 0.2%,带来的效果提升是数量级的。所以,大胆地开启 All Linear

  2. 不要漏掉 lm_head(如果需要):

    虽然 lm_head(输出层)通常不包含在标准 LoRA 配置里,但如果你的任务极其依赖特定的输出格式或者微调后的词汇分布与原模型差异巨大,给 lm_head 加上 LoRA 往往有奇效。

总结

  • 旧观念: 只要调 q_projv_proj 就够了。
  • 新共识: 应尽可能对所有线性层(All Linear)应用 LoRA。
  • 核心原因: MLP 是知识和推理的载体,不调它,模型只能学会“皮毛”,学不到“灵魂”。

到现在为止,我们已经搞定了 LoRA 的原理、Rank、Alpha 和 Target Modules。

但这只是“怎么练”。在实际落地中,还有一个非常致命的问题:数据质量与配比。 比如,你微调时使用 100% 的新数据,还是应该混入一部分通用数据来防止“变傻”?这涉及到 **Catastrophic Forgetting(灾难性遗忘)**问题 ... 待更新

上述内容来自Gemini