04. LoRA 的原理是什么?为什么它更省显存?秩 r 怎么选?

整理 LoRA 的核心原理、显存优势与秩 r 的选择思路。

简单回答

LoRA(Low-Rank Adaptation)的核心思想是:微调时不直接更新原始权重矩阵 ,而是冻结 ,额外学一个低秩分解 ,其中 。因为只训练 A 和 B,可训练参数量和优化器状态都大幅缩小,所以显存省很多。秩 r 一般取 8~64,具体取决于任务复杂度和数据量。

详细解释

LoRA 的出发点来自一个经验性的观察:大模型微调时,权重的变化量 其实是低秩的,也就是说它的有效信息可以用远小于原始维度的空间来表达。既然如此,就没必要更新整个 的权重矩阵,直接把 参数化为两个小矩阵的乘积就行了。

具体来说,原始的前向传播是 ,加了 LoRA 之后变成 。训练时 完全冻结不更新,只训练 A 和 B。初始化时 A 用高斯随机初始化,B 初始化为零,这样训练开始时 ,不会破坏原始模型的行为。 是一个缩放因子,用来控制 LoRA 的更新幅度,实际使用中 这个比值才是真正起作用的。

为什么省显存?算一下就清楚了。一个 4096×4096 的权重矩阵有 16M 参数,全量微调需要存梯度(16M)和 Adam 优化器的两个动量(各 16M),加起来 48M 参数的额外开销。用 LoRA r=16,A 是 4096×16,B 是 16×4096,总共只有 131K 参数,优化器状态也只有 393K,相当于原来的不到 1%。模型原始参数虽然还在显存里,但因为冻结了不需要存梯度和优化器状态,这部分开销就省掉了。

LoRA 一般加在 Transformer 的 attention 层的 Q、K、V、O 投影矩阵上,有些实现也会加在 FFN 的 up/down/gate projection 上。加的层越多,表达能力越强,但参数量和显存也会增加。实际经验中,只加 Q 和 V 已经能拿到不错的效果,加全部投影矩阵效果更好但提升边际递减。

关于秩 r 的选择,这是一个表达能力 vs 效率的 trade-off。r 越大,LoRA 能表达的变化越丰富,效果上限越高,但参数量线性增长。实际经验中,r=8 在大多数简单任务上就够了,r=16 ~ 32 是比较常用的平衡点,r=64 或更高一般只有在数据量大或任务特别复杂时才有意义。一个实用的方法是先用 r=16 跑一版看效果,如果效果不够再调大。同时要注意 一般设成 r 的 1 ~ 2 倍,比如 r=16 时 设 16 或 32。

推理时 LoRA 可以和原始权重合并:,合并后推理速度和原模型完全一样,没有任何额外开销。这也是 LoRA 相比 Adapter Tuning 等方法的一个重要优势。

面试时可以这样答

LoRA 的核心思路是:微调时权重的变化量是低秩的,所以不用更新整个权重矩阵,直接把变化量分解成两个小矩阵 A 和 B 的乘积就行。原始权重完全冻结,只训练 A 和 B。

省显存的原因很直接。全量微调时每个参数都要存梯度和优化器状态,一个 4096×4096 的矩阵有 16M 参数,光优化器状态就要多存 48M 参数。LoRA r=16 的话可训练参数只有 131K,不到原来的 1%。原始参数虽然还在显存里但不需要梯度和优化器状态,省的就是这块。

LoRA 通常加在 attention 的 Q、V 投影矩阵上,也可以扩展到 K、O 和 FFN。初始化上 B 矩阵初始化为零,保证训练开始时不破坏原始行为。推理时可以把 LoRA 参数直接合并回原始权重,不增加任何推理开销。

秩 r 的选择上,r=8 ~ 16 能覆盖大部分场景,复杂任务或数据量大时可以试到 32 ~ 64。我一般会先用 r=16 跑 baseline,效果不够再调大。α 一般设成和 r 一样或者两倍。

常见追问

  1. LoRA 的 A 和 B 分别怎么初始化?为什么这样设计?
  2. LoRA 加在哪些层效果最好?有没有经验性的结论?
  3. 多个 LoRA adapter 能不能合并?合并后效果怎么样?