LoRA(Low-Rank Adaptation)

一种参数高效微调(Parameter-Efficient Fine-Tuning, PEFT)方式

在冻结原始大模型参数的前提下,只训练一个很小的低秩权重增量

原理

需要一点点基础的线性代数的知识

假设某一层原始权重是:

$$ W_0 \in \mathbb{R}^{d_{out} \times d_{in}} $$

输入是:

$$ x \in \mathbb{R}^{d_{in}} $$

原始线性层输出是:

$$ y = W_0 x $$

全量微调会直接更新整个 $W_0$,得到:

$$ W_{new} = W_0 + \Delta W $$

这里的 $\Delta W$ 就是微调过程对原模型权重造成的变化。

而LoRA 不直接训练完整的 $\Delta W$,而是把它写成两个小矩阵的乘积: $$ \Delta W \approx BA $$

其中:

$$ A \in \mathbb{R}^{r \times d_{in}} $$

$$ B \in \mathbb{R}^{d_{out} \times r} $$

并且:

$$ r \ll \min(d_{in}, d_{out}) $$

于是原来的线性层从:

$$ y = W_0 x $$

变成:

$$ y = W_0 x + \frac{\alpha}{r}BAx $$

这里:

  • $W_0$:预训练模型原始权重,冻结不训练;
  • $A$ 和 $B$:LoRA 新增的小矩阵,训练时只更新它们;
  • $r$:rank,控制 LoRA 的容量;
  • $\alpha$:缩放系数,控制 LoRA 分支对原模型的影响强度;
  • $\frac{\alpha}{r}$:常见实现里的归一化缩放。

一句话总结:

LoRA 假设“微调需要的权重变化”是低秩的,所以只训练一个低秩增量,而不是训练整个大模型。

所谓的低秩的假设就是说微调中重要的调整方向是较少的,因此就可以在限制r这一自由度的低秩矩阵基础上,达到类似的全量微调的效果

详细讲就是:

矩阵的秩可以粗略理解为:这个矩阵真正包含多少个独立方向。

如果一个大矩阵的变化可以由少数几个方向组合出来,它就是低秩或近似低秩的。

举一个线性层例子:

$$ W_0 \in \mathbb{R}^{4096 \times 4096} $$

全量微调这一层需要训练:

$$ 4096 \times 4096 = 16,777,216 $$

个参数。

如果 LoRA 取:

$$ r = 8 $$

那么只训练:

$$ A: 8 \times 4096 $$

$$ B: 4096 \times 8 $$

总参数量是:

$$ 8 \times 4096 + 4096 \times 8 = 65,536 $$

这一层的可训练参数约减少:

$$ \frac{16,777,216}{65,536} = 256 $$

倍。

LoRA 的训练过程

LoRA 训练时一般这样初始化:

  • 冻结 $W_0$;
  • 随机初始化 $A$;
  • 将 $B$ 初始化为 0。

因为开始时:

$$ B = 0 $$

所以:

$$ BA = 0 $$

模型初始输出仍然是:

$$ y = W_0 x $$

这意味着 LoRA 微调从原模型行为出发,而不是一开始就扰动模型。

训练过程中,梯度只更新 $A$ 和 $B$:

base model W0: 冻结
LoRA A: 训练
LoRA B: 训练

最后得到一个任务专用的 adapter:

LoRA adapter = A + B + 配置参数

这个 adapter 通常很小,可以单独保存、分享和加载。

LoRA 的推理过程

训练完成后有两种使用方式。

第一种是分离加载:

base model + LoRA adapter

推理时仍然计算:

$$ y = W_0 x + \frac{\alpha}{r}BAx $$

第二种是合并权重:

$$ W_{merged} = W_0 + \frac{\alpha}{r}BA $$

合并后推理就变成普通线性层:

$$ y = W_{merged}x $$

合并后几乎没有额外推理延迟。

LoRA 加在哪里

Transformer里有大量线性层。LoRA 本质上可以加到任何线性层上,但在LLM里常见目标包括:

  • attention 的 q_proj
  • attention 的 k_proj
  • attention 的 v_proj
  • attention 的 o_proj
  • MLP 的 gate_proj
  • MLP 的 up_proj
  • MLP 的 down_proj

早期 LoRA 常只加在 attention 的 query 和 value 投影上,例如 q_projv_proj。现在很多指令微调实践会把 MLP 投影层也纳入目标模块。

选择方式可以这样理解:

LoRA 位置优点代价
只加 attention 的部分投影层参数最省,训练快表达能力较弱
加 attention 全部投影层仍然比较省,效果更稳参数略增
加 attention + MLP更适合复杂任务和领域适配显存、训练时间增加
加所有线性层最接近全量微调参数最多,也更可能过拟合

核心超参数

  • rank:r:上面说了

  • 缩放系数:lora_alpha

    • LoRA 实际使用的更新通常是:

      $$ \Delta W = \frac{\alpha}{r}BA $$

      所以就是控制LoRA矩阵的影响力的

      常见设置会让 alphar 相等或是 r 的 2 倍

  • dropout:lora_dropout:顾名思义是加在 LoRA 分支上的 dropout,dorpout是指训练中的一种防过拟合方法,训练时随机关闭一部分神经元或信号路径,让模型不要依赖单一特征,从而减少过拟合

  • 目标模块:target_modules:加在哪些层

    • 例如

      ["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"]
      

其他微调方式与比较

方法更新什么优点缺点
全量微调更新整个模型效果上限高显存、存储和部署成本高
Prompt Tuning学习软提示极省参数表达能力有限
Prefix Tuning学习前缀向量省参数,适合生成任务推理上下文开销更明显
Adapter插入小模块参数高效推理路径通常增加额外计算
LoRA学习低秩权重增量参数少,可合并,推理友好rank 太小会欠拟合

这里其他的还没太多了解

变体:QLoRA、AdaLoRA、DoRA

QLoRA

可以理解成:

QLoRA = 量化的冻结基座模型 + LoRA adapter 训练

普通 LoRA 减少的是可训练参数和优化器开销,但 base model 本身仍然要放进显存。

QLoRA 进一步把冻结的 base model 量化t,从而显著降低显存占用。训练时,梯度通过量化后的模型传播,但实际更新的仍然是 LoRA adapter。

AdaLoRA

普通 LoRA 通常给不同层分配相同的 rank。

但不同层的重要性并不一样。有的层对任务适配很关键,有的层几乎不需要动。

AdaLoRA 的思想是:

总 rank 预算固定,但动态分配给更重要的层和方向

它会根据重要性评分保留更有价值的低秩方向,剪掉不重要的方向。

DoRA

全称 Weight-Decomposed Low-Rank Adaptation。

它认为权重可以拆成两个方面:

  • magnitude:权重大小;
  • direction:权重方向。

普通 LoRA 主要通过低秩更新改变权重方向,但对 magnitude 的建模不够直接。

DoRA 把 magnitude 和 direction 分开处理,用 LoRA 更专注地适配 direction,同时单独学习 magnitude。这样可以更接近全量微调的效果。