题目

为什么LoRA的矩阵不能简单地初始化为零?

解答

LoRA的核心思想是使用一个低秩的“旁路”矩阵(B*A)来模拟全参数微调时的增量更新(ΔW)。其更新公式为:
W' = W + ΔW = W + B * A

其中:

  • W 是预训练模型的原始权重(冻结,不可训练)。

  • A 是一个降维矩阵(从原始维度 d 降到低秩 r),通常用高斯分布初始化。

  • B 是一个升维矩阵(从低秩 r 升回原始维度 d),在原始LoRA论文中初始化为全零

核心问题1:为什么不要使用双重零初始化

双重零初始化指的是:

  • 矩阵 A 初始化为全零

  • 矩阵 B 初始化为全零

在这种情况下:ΔW = B × A = 0 × 0 = 0

让我们看看在反向传播时会发生什么。对于损失函数 L,根据链式法则:

∂L/∂B = (∂L/∂(BA)) × Aᵀ
∂L/∂A = Bᵀ × (∂L/∂(BA))

当 A = 0 且 B = 0 时:

∂L/∂B = (上游梯度) × 0 = 0
∂L/∂A = 0 × (上游梯度) = 0

结果:两个矩阵的梯度都为零!

训练动态:

  1. 第一步更新

    • 梯度为零 → 参数更新量为零

    • A 和 B 保持为零

  2. 第二步及以后

    • 由于参数没有变化,前向传播结果不变

    • 梯度计算仍然为零

    • 形成死循环

直观类比:

想象一下你要教两个人协作完成一个任务:

  • 人A说:"我不知道怎么做,等人B先做"

  • 人B说:"我也不知道怎么做,等人A先做"

  • 结果:两人永远等待对方先行动,任务永远无法开始

这就是双重零初始化的状况——两个矩阵都在等待对方先提供有用的信号。

核心问题2:单重零初始化的陷阱

既然不能双重零初始化,思考一下:如果我们将 B 初始化为0,在训练的第一步会发生什么?

  1. 前向传播: h = Wx + BAx = Wx + 0*x = Wx

  2. 反向传播: 计算 B 的梯度,∂L/∂B 依赖于 A 和上游梯度。

  3. 参数更新: B = B - η * (∂L/∂B)

由于 B 初始为0,第一步更新后,B 会得到一个非零值,但 A 的梯度同样依赖于 B(在反向传播链式法则中)。因为 B 初始为0,这会导致 A 的梯度在第一步也非常小。

结果是: 整个LoRA适配器在训练初期等效于一个几乎不存在的模块,需要经过多个训练步骤才能“启动”并开始有效学习。这相当于浪费了初期的训练步骤,可能导致收敛速度变慢。

为了解决这个问题,研究人员提出了几种更聪明的初始化方法。

主要的初始化技术

1. 原始LoRA方法:将 B 初始化为0
  • 思路: 确保训练开始时,模型的输出与原始预训练模型完全一致,即 ΔW = B*A = 0

  • 优点: 简单,保证训练起始点稳定。

  • 缺点: 如上所述,存在“启动延迟”问题,训练初期效率不高。

  • 现状: 虽然这是原始论文的方法,但现在许多实现和后续研究已经转向了更优的初始化策略。

2. Kaiming初始化 / He初始化

这是深度学习中最常用、最有效的权重初始化方法之一。

  • 思路: 为了在前向和反向传播中保持激活值和梯度的方差稳定,根据激活函数的性质来初始化权重。对于ReLU及其变体,通常使用Kaiming正态初始化或均匀初始化。

  • 具体操作

    • 矩阵 A 使用(均值为0,标准差为 σ = sqrt(2 / fan_in))的高斯分布初始化。其中 fan_in 是 A 的输入维度。

    • 矩阵 B 使用全零初始化

  • 优点

    • 理论基础坚实,被广泛验证有效。

    • 能有效避免梯度消失或爆炸,加速模型训练的收敛。

  • 应用: 这是目前最主流、最推荐的LoRA初始化方式。例如,Hugging Face的PEFT库就默认采用了类似Kaiming的初始化策略。

3. 将 B 初始化为非零(如高斯分布)
  • 思路: 既然零初始化有问题,那就让 B 一开始就是有值的。

  • 具体操作: A 和 B 都使用高斯分布进行初始化(例如,均值为0,标准差为一个较小的值)。

  • 优点: 解决了“启动延迟”问题,训练一开始LoRA适配器就是活跃的。

  • 缺点

    • 训练起始点不再是原始的预训练模型,因为 ΔW = B*A 从一开始就是一个随机矩阵。这可能会引入不必要的噪声,在某些任务上可能不稳定。

    • 需要谨慎选择初始化的标准差,过大会破坏预训练知识。

4. SVD(奇异值分解)初始化 - 一种更高级的技术

这是一种非常巧妙且理论上优雅的方法,旨在让LoRA适配器在初始化时就尽可能接近“某个理想状态”。

  • 思路: 如果我们能获得一个在少量下游数据上全参数微调后的权重 W_finetuned,那么其增量 ΔW = W_finetuned - W_pretrained 就是我们希望LoRA(B*A)去学习和逼近的目标。我们可以直接对这个理想的 ΔW 进行奇异值分解(SVD),并用分解后的前 r 个主要成分来初始化 A 和 B

  • 具体步骤

    1. 在小部分训练数据上,对目标模块(如Q、K、V投影层)进行极少量(1-几个epoch)的全参数微调,得到 W_finetuned

    2. 计算增量矩阵: ΔW = W_finetuned - W_pretrained

    3. 对 ΔW 进行SVD分解: ΔW = U * Σ * V^T

    4. 取前 r 个最大的奇异值及其对应的奇异向量来初始化LoRA:

      • A = √Σ[:r] * V[:r, :]^T (注意形状,需要转置和维度匹配)

      • B = U[:, :r] * √Σ[:r]

  • 直观理解: SVD找到了 ΔW 中最重要的 r 个“方向”。我们用这 r 个最重要的方向来构建LoRA矩阵,使得LoRA网络从一开始就抓住了全参数微调的关键变化。

  • 优点

    • 极快的收敛速度: 因为起点非常接近最优解。

    • 可能达到更好的性能: 尤其是在低秩 r 很小的情况下,这种方式能更有效地利用有限的参数。

  • 缺点

    • 计算成本高: 需要先进行一次小规模的全参数微调和SVD计算,增加了额外的步骤和开销。

    • 实现复杂: 不如其他方法简单易用。

总结与对比

初始化方法 描述 优点 缺点 适用场景
零初始化(B=0) LoRA原始方法,保证训练起点不变。 简单,稳定。 存在“启动延迟”,收敛慢。 现已不常用,作为理解的基础。
Kaiming/He初始化 A 用Kaiming初始化,B 初始为0。 理论扎实,收敛快且稳定,广泛适用。 - 通用推荐,默认选择
非零初始化 A 和 B 都用高斯分布初始化。 解决了启动问题。 起点引入噪声,可能不稳定。 可以尝试,但需要调参。
SVD初始化 利用全微调增量的SVD来初始化A和B。 收敛极快,性能可能更好。 实现复杂,计算成本高。 对性能和收敛速度有极致要求,且资源充足的场景。

实践建议

  1. 首选Kaiming初始化: 对于绝大多数任务,使用现代库(如PEFT)默认的初始化(通常是Kaiming的变体)就足够了,它提供了最佳的开箱即用体验。

  2. 不要使用双重零初始化: 确保 A 和 B 不同时为零初始化。

  3. 谨慎尝试SVD初始化: 虽然它在理论上很吸引人,但其额外的计算成本可能并不总是值得的。除非你是在一个非常关键的项目中,并且对微调速度和模型性能有极致追求,否则可以暂时不考虑它。

  4. 理解其本质: 所有初始化技术的核心目标,都是在 “保持预训练知识稳定性” 和 “快速启动并高效学习新知识” 之间找到一个最佳的平衡点。

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐