DoRA:权重分解低秩自适应——用幅度与方向解耦提升 LoRA 学习能力 | 阅读笔记

DoRA:权重分解低秩自适应——用幅度与方向解耦提升 LoRA 学习能力 | 阅读笔记

笔记日期: 2026-05-22 笔记作者: Zhongzhu Zhou 所读论文: DoRA: Weight-Decomposed Low-Rank Adaptation 论文作者: Shih-Yang Liu, Chien-Yi Wang, Hongxu Yin, Pavlo Molchanov, Yu-Chiang Frank Wang, Kwang-Ting Cheng, Min-Hung Chen(NVIDIA & HKUST) arXiv: 2402.09353v6,2024-07-09 收录状态: ICML 2024 Oral,PMLR 235

一句话总结

LoRA 和全量微调(FT)之间的准确率差距,不是因为参数量不够,而是因为 LoRA 的更新结构有根本性的缺陷——它把权重的「幅度变化」和「方向变化」硬耦合在一起,而 FT 可以灵活地只改一个。DoRA 把每个权重矩阵拆成幅度向量和方向矩阵,分别训练,用 LoRA 只负责方向部分,用一个廉价的标量向量负责幅度,从而让 LoRA 的学习模式在统计上贴近 FT,并在所有测试模型和任务上一致地超过 LoRA。

1. 前置知识

这一节是写给做过 transformer 训练、但没深入读过 LoRA / weight normalization 原论文的同学。重点要理解三件事:LoRA 的数学结构、权重归一化的分解形式、以及「学习模式」的定义方式。

1.1 全量微调和它的代价

给定一个在大规模语料上预训练好的语言模型 fθ0f_{\theta_0}(参数量 PP),全量微调(Full Fine-Tuning, FT)直接把所有参数当作可训练变量:

θ=argminθL(θ;Dtarget),θ 从 θ0 初始化\theta^* = \arg\min_\theta \mathcal{L}(\theta; \mathcal{D}_{\text{target}}), \quad \theta \text{ 从 } \theta_0 \text{ 初始化}

对于一个 7B 参数模型:

  • 参数本身:7B × 4 bytes(float32)= 28 GB
  • Adam 优化器状态(一阶矩 + 二阶矩):额外 56 GB
  • 激活值(反向传播用):依序列长度,通常 20–60 GB

合计:100–150 GB 以上。单块 80 GB A100 直接吃不下。这就是 PEFT(参数高效微调)出现的动机。

1.2 PEFT 的三大流派

适配器(Adapter-based):在 transformer 块内的固定位置插入一个小模块(一般是小 MLP:压缩→非线性→扩展),只训练这些模块。缺点是串行插入会增加推理延迟。

软提示(Prompt-based / Prefix-tuning):在输入序列前面或每层的 key-value 上拼一些可训练的”软 token”,backbone 完全冻结,只训练这些向量。缺点是对初始化敏感、通常比适配器差。

低秩更新(LoRA 家族):把权重矩阵的增量 ΔW\Delta W 限制为低秩矩阵乘积 BABA,训练完之后把 W0+BAW_0 + BA 合并为一个稠密矩阵,推理时零额外开销。这是目前最主流的方案,DoRA 属于这个流派。

1.3 LoRA 的数学

对预训练权重 W0Rd×kW_0 \in \mathbb{R}^{d \times k},LoRA 把权重更新限制为:

W' = W_0 + \Delta W = W_0 + BA \tag{1}

其中 BRd×rB \in \mathbb{R}^{d \times r}ARr×kA \in \mathbb{R}^{r \times k}rmin(d,k)r \ll \min(d, k)

初始化AA 用 Kaiming 均匀分布初始化,BB 初始化为全零矩阵。所以训练开始时 ΔW=BA=0\Delta W = BA = 0,模型从预训练点出发,和 FT 起点相同。

参数量估算:4096×4096 的注意力矩阵,r=16r=16 的 LoRA 用 16×(4096+4096)=13107216\times(4096+4096)=131072 个参数,相比 40962=16M4096^2 = 16M 参数,节省了约 128 倍

推理合并:训练后只需做一次 W=W0+BAW' = W_0 + BA,存储这个稠密矩阵,BBAA 可以丢弃。推理开销和原始模型完全相同。

LoRA 的隐含假设:权重更新 ΔW\Delta W 的「内在秩」很低。换句话说,模型从预训练到下游任务只需要做一个低维子空间里的变动。这个假设在很多任务上大致成立,但并不总是最优。

1.4 权重归一化(Weight Normalization)

权重归一化(Salimans & Kingma, NeurIPS 2016)是 DoRA 的数学前身。它把一个权重向量 w\mathbf{w} 重参数化为:

\mathbf{w} = g \cdot \frac{\mathbf{v}}{\|\mathbf{v}\|} \tag{2}

其中 gg 是标量幅度,v\mathbf{v} 是方向向量(不要求单位长度,训练时自由更新,归一化只在最终合并到 w\mathbf{w} 时发生)。

为什么这样做有用:梯度协方差矩阵更接近单位矩阵,SGD/Adam 收敛更快。直觉上,把幅度和方向分开优化,可以避免”想缩放方向但同时被迫缩放幅度”的耦合问题。

推广到矩阵:对 WRd×kW \in \mathbb{R}^{d \times k},把每一列 WnW_nn=1,,kn = 1, \ldots, k)分别做归一化,得到列方向归一化

W = \mathbf{m} \cdot \frac{V}{\|V\|_c} \tag{3}

其中 mR1×k\mathbf{m} \in \mathbb{R}^{1 \times k} 是每列的 2\ell_2 范数组成的行向量(幅度向量),VRd×kV \in \mathbb{R}^{d \times k} 是方向矩阵,c\|\cdot\|_c 表示对每列做 2\ell_2 归一化的操作(即每列除以自己的 2\ell_2 范数)。

DoRA 用这个分解方式分析 LoRA 和 FT 的差异,并基于分析结果设计方法。

1.5 「学习模式」的定义

DoRA 引入了一个诊断工具——权重分解分析(Weight Decomposition Analysis)

对于一个经过微调的模型,拿某一层权重 WtW^t(训练步 tt 时的值)和预训练权重 W0W_0 做分解,然后计算:

幅度差 ΔMt\Delta M^t

\Delta M^t = \frac{1}{k} \sum_{n=1}^k \left|m_n^t - m_n^0\right| \tag{4}

方向差 ΔDt\Delta D^t

\Delta D^t = \frac{1}{k} \sum_{n=1}^k \left(1 - \cos(V_n^t, W_0^n)\right) \tag{5}

其中 mntm_n^tWtW^tnn 列的 2\ell_2 范数,VntV_n^tWtW^tnn 列的方向(归一化后),W0nW_0^nW0W_0nn 列(方向参考点)。

(ΔDt,ΔMt)(\Delta D^t, \Delta M^t) 在不同训练步和不同层上画散点图,就可以直观看到”这个方法在微调时优先改方向还是优先改幅度”。

2. 核心发现:LoRA 的学习模式和 FT 结构不同

2.1 散点图的关键发现

用一个 VL-BART 模型在四个图文任务上做对比(固定 case study):

  • FT(全量微调):散点图呈负相关。大的方向改变伴随着小的幅度改变,反之亦然。方向和幅度之间的 Pearson 相关系数 = −0.62

  • LoRA:散点图呈正相关。方向改变和幅度改变总是同步增减,斜率固定为正。Pearson 相关 = +0.83。这说明 LoRA 做不到「大幅度调整方向而不改变幅度」,也做不到「大幅缩放但保持方向」——两者总是耦合在一起。

  • DoRA(本文方法):散点图回到负相关,接近 FT 的模式。Pearson 相关 = −0.31

图一:FT / LoRA / DoRA 的学习模式对比示意
graph TD
    subgraph FT_box["全量微调 (FT)"]
        f1["相关系数 = −0.62"]
        f2["大 ΔD 时 → 小 ΔM\n大 ΔM 时 → 小 ΔD"]
        f3["模型可以「只改方向」\n也可以「只改幅度」"]
        f1 --> f2
        f2 --> f3
    end
    subgraph LoRA_box["LoRA"]
        l1["相关系数 = +0.83"]
        l2["ΔD 和 ΔM 总是成比例增减"]
        l3["缺乏精细控制:\n想改方向,幅度也跟着变"]
        l1 --> l2
        l2 --> l3
    end
    subgraph DoRA_box["DoRA"]
        d1["相关系数 = −0.31"]
        d2["设计上解耦幅度与方向"]
        d3["接近 FT 的学习模式"]
        d1 --> d2
        d2 --> d3
    end
    FT_box -. "DoRA 模仿" .-> DoRA_box
    LoRA_box -. "DoRA 改进" .-> DoRA_box

2.2 为什么正相关等于”浪费”

考虑一个简单的更新场景。预训练权重 W0W_0 已经大致指向目标方向,但幅度偏小——理想更新是只放大幅度,不改变方向

对于 FT:可以直接学习 Δmn>0\Delta m_n > 0(加大第 nn 列的范数),同时 Δdn0\Delta d_n \approx 0(不旋转方向)。

对于 LoRA:ΔW=BA\Delta W = BA 是一个固定的矩阵,它同时影响方向和幅度。增大 BABA 的规模(比如增大学习率或让 B,AB, A 的值变大)会同时放大幅度分量和方向分量。LoRA 无法做到「只放幅度不转方向」。

这就是”耦合”的代价:在需要细粒度控制的场景下,LoRA 的梯度能量有一部分被”浪费”在同时调整两个应该独立变化的维度上。

3. 方法:DoRA 的数学与实现

3.1 DoRA 的核心公式

基于权重分解分析,DoRA 的设计思路直接了当:m\mathbf{m}VV 分别训练。因为方向矩阵 VV 维度高(d×kd \times k 个元素),用 LoRA 做方向更新;幅度向量 m\mathbf{m} 只有 kk 个标量,直接训练:

W' = \mathbf{m} \cdot \frac{V + \Delta V}{\|V + \Delta V\|_c} = \mathbf{m} \cdot \frac{W_0 + BA}{\|W_0 + BA\|_c} \tag{6}

可训练参数

  • mR1×k\mathbf{m} \in \mathbb{R}^{1 \times k}:幅度向量(直接训练)
  • BRd×rB \in \mathbb{R}^{d \times r}ARr×kA \in \mathbb{R}^{r \times k}:方向 LoRA 矩阵

冻结参数

  • V=W0V = W_0(方向的初始基准)

初始化B=0B = 0,所以训练开始时 ΔV=0\Delta V = 0W=mW0/W0c=W0W' = \mathbf{m} \cdot W_0 / \|W_0\|_c = W_0。和 LoRA 一样,从预训练点出发。

推理合并:训练后一次性计算 W=m(W0+BA)/W0+BAcW' = \mathbf{m} \cdot (W_0 + BA) / \|W_0 + BA\|_c,存成稠密矩阵,推理零开销。

3.2 算法逐步拆解

算法 1:DoRA 微调(逐步展开)

输入:预训练权重 W₀ ∈ ℝ^{d×k},低秩秩 r,目标任务数据集 D
输出:可合并权重 W' ∈ ℝ^{d×k}

——————— 初始化阶段(训练开始前做一次)———————
步骤 1: 计算幅度向量 m ← column_norms(W₀)
        # m ∈ ℝ^{1×k},每个元素是 W₀ 对应列的 ℓ₂ 范数
步骤 2: 设 V ← W₀                         # 冻结,作为方向基准
步骤 3: 初始化 A ~ Kaiming_uniform(r, k)   # LoRA A 矩阵
步骤 4: 初始化 B ← 0_{d×r}                # LoRA B 矩阵,全零初始化
步骤 5: 标记可训练:{m, A, B}
步骤 6: 标记冻结:{V (= W₀)}

——————— 前向传播(每个训练步执行)———————
步骤 7: 计算方向增量 ΔV ← B @ A            # ∈ ℝ^{d×k}
步骤 8: 计算更新后方向 V' ← V + ΔV         # = W₀ + BA
步骤 9: 计算列范数 C ← column_norms(V')   # ∈ ℝ^{1×k}
        ⚠️ 重要:把 C 从计算图中脱离(.detach())
        → 这样反向传播时 C 被视为常数,大幅节省显存
步骤 10: 计算输出权重 W' ← m * (V' / C)   # ∈ ℝ^{d×k}
步骤 11: 计算前向输出:output ← W' @ x

——————— 反向传播(autograd 自动处理)———————
步骤 12: 计算 ∂L/∂W' 通过损失函数
步骤 13: 梯度流到 m:
         ∂L/∂m = (∂L/∂W') · V' / C
               = ‖∇_{W'}L‖ · cos(∇_{W'}L, v')   # 见公式 (8)
步骤 14: 梯度流到 V' (再流向 A 和 B):
         ∂L/∂V' = (m / C) · ∂L/∂W'              # 见公式 (7)
步骤 15: 用 AdamW 等优化器更新 {m, A, B}

——————— 部署合并(训练结束后做一次)———————
步骤 16: 计算 W'_merged = m * (W₀ + B@A) / column_norms(W₀ + B@A)
步骤 17: 保存 W'_merged(稠密矩阵,形状同 W₀)
步骤 18: 丢弃 {m, A, B}

3.3 从梯度角度解释「负相关」

这是论文第 4.2 节的分析,也是最有洞察力的部分。把公式 (6) 代入,对 m\mathbf{m}VV' 求偏导(把列范数 CC 视为常数):

VV' 的梯度(这个梯度会通过 ΔV=BA\Delta V = BA 传递到 AABB):

\nabla_{V'} \mathcal{L} = \frac{\mathbf{m}}{C} \cdot \nabla_{W'} \mathcal{L} \tag{7}

这是权重梯度的一个简单缩放——方向不变,幅度按 mn/Cnm_n / C_n 缩放。

对第 nn 个幅度标量 mnm_n 的梯度(标量,很关键):

\nabla_{m_n} \mathcal{L} = \frac{(\nabla_{W'}\mathcal{L})_n \cdot V'_n}{\|V'_n\|} = \|\nabla_{W'}\mathcal{L}_n\| \cdot \cos\!\left(\nabla_{W'}\mathcal{L}_n,\, V'_n\right) \tag{8}

其中 VnV'_nVV' 的第 nn 列,(WL)n(\nabla_{W'}\mathcal{L})_n 是权重梯度的第 nn 列。

公式 (8) 的直觉解读

mnL\nabla_{m_n}\mathcal{L} 的绝对值等于「损失梯度列向量与当前方向的余弦相似度 × 梯度范数」。

  • 场景 A:损失梯度 \approx 当前方向(cos1\cos \approx 1)→ 模型需要的是缩放幅度而不是旋转方向 → mnL|\nabla_{m_n}\mathcal{L}| 大 → 幅度大幅更新,方向小幅更新

  • 场景 B:损失梯度 \perp 当前方向(cos0\cos \approx 0)→ 模型需要的是旋转方向而不是缩放幅度 → mnL0|\nabla_{m_n}\mathcal{L}| \approx 0方向大幅更新,幅度几乎不动

这正是 FT 观察到的负相关!DoRA 的梯度几何自动路由梯度能量——不需要手工指定”这一步应该改方向还是改幅度”。

图二:DoRA 反向传播中梯度流的方向分离示意
graph TB
    L["损失 L"]
    dW["∂L/∂W' ∈ ℝ^{d×k}\n(权重梯度)"]
    L --> dW

    subgraph mag_path["幅度路径"]
        dm["∂L/∂m = ‖∂L/∂W'‖·cos(grad, V')\n只有「梯度与当前方向对齐」时才大"]
        update_m["更新 m → 幅度大幅变化\n方向保持不动"]
        dm --> update_m
    end

    subgraph dir_path["方向路径 (经过 LoRA)"]
        dV["∂L/∂V' = (m/C)·∂L/∂W'\n(缩放后传向 A, B)"]
        update_dir["更新 A, B → 方向 ΔV 更新\n幅度不变"]
        dV --> update_dir
    end

    dW --> dm
    dW --> dV

    result["结果:大 ΔD 时 ΔM 小,大 ΔM 时 ΔD 小\n= FT 观察到的负相关"]
    update_m --> result
    update_dir --> result

3.4 节省显存的 detach 技巧

原始 DoRA 公式中,列范数 Vc\|V'\|_c 包含 ΔV=BA\Delta V = BA,所以反向传播需要通过这个范数计算流回 AABB。这会引入额外的梯度图节点,占用显著更多显存。

技巧:把 Vc\|V'\|_c 从计算图中脱离(.detach()),在反向传播时把它视为一个常数(但它仍然是用当前 VV' 动态计算的,所以值是实时的)。数学上这等价于把公式 (7) 直接作为梯度公式,省略了通过归一化算子的二阶路径。

效果

  • LLaMA-7B 上:显存减少约 24.4%(从 37.3 GB 降到 28.2 GB)
  • 准确率损失:约 0.2 个点(可接受)
  • VL-BART 上:显存减少约 12.4%,准确率完全无损
# 有 detach(推荐实现):
norms = (W0 + B @ A).norm(dim=0, keepdim=True).detach()  # 脱离梯度图
W_prime = m * (W0 + B @ A) / norms

# 无 detach(原始,显存占用更高):
norms = (W0 + B @ A).norm(dim=0, keepdim=True)           # 在梯度图内
W_prime = m * (W0 + B @ A) / norms

3.5 DVoRA:LoRA 换成 VeRA 的扩展

DoRA 的方向更新 ΔV\Delta V 不必非要用 LoRA。论文把它替换成 VeRA(Vector-based Random Matrix Adaptation,Kopiczko et al., ICLR 2024)作为案例研究:

VeRA 的思路:全部层共享一对随机矩阵 {Ashared,Bshared}\{A_{\text{shared}}, B_{\text{shared}}\}(冻结不训练),每层只学习两个缩放向量 {b,d}\{b_\ell, d_\ell\}

ΔWVeRA=diag(b)Bshareddiag(d)Ashared\Delta W_\ell^{\text{VeRA}} = \text{diag}(b_\ell) \cdot B_{\text{shared}} \cdot \text{diag}(d_\ell) \cdot A_{\text{shared}}

可训练参数量比 LoRA 少约 10 倍。把 VeRA 插入 DoRA 的方向更新就得到 DVoRA

W=mW0,+diag(b)Bshareddiag(d)AsharedW0,+cW'_\ell = \mathbf{m}_\ell \cdot \frac{W_{0,\ell} + \text{diag}(b_\ell) B_{\text{shared}} \text{diag}(d_\ell) A_{\text{shared}}}{\|W_{0,\ell} + \ldots\|_c}

在 LLaMA2-7B + 10K Alpaca 指令微调中,DVoRA(0.04% 参数)在 MT-Bench 上达到 6.00 分,和 DoRA(2.33% 参数)持平,远超 VeRA(5.50)和 LoRA(5.70)。58 倍参数减少,准确率不降

3.6 系统架构全图

图三:DoRA 系统架构(初始化 → 训练 → 推理合并)
graph LR
    subgraph init["初始化"]
        W0["W₀ ∈ ℝ^{d×k}\n(预训练权重,冻结)"]
        m0["m = ‖W₀‖_c\n(幅度向量,可训练)"]
        V0["V = W₀\n(方向基,冻结)"]
        W0 --> m0
        W0 --> V0
    end

    subgraph lora["LoRA 分支(可训练)"]
        A["A ∈ ℝ^{r×k}\nKaiming 初始化"]
        B["B ∈ ℝ^{d×r}\n全零初始化"]
        dV["ΔV = B@A\n(低秩方向增量)"]
        A --> dV
        B --> dV
    end

    subgraph fwd["前向计算"]
        Vp["V' = W₀ + ΔV"]
        C["C = ‖V'‖_c(detach)"]
        Wp["W' = m · (V'/C)"]
        V0 --> Vp
        dV --> Vp
        Vp --> C
        Vp --> Wp
        C --> Wp
        m0 --> Wp
    end

    subgraph deploy["部署合并(一次性)"]
        merged["W'_merged = m·(W₀+B@A)/‖W₀+B@A‖_c\n形状同 W₀,零推理开销"]
        Wp --> merged
    end

4. 实验

4.1 常识推理(LLaMA 系列)

实验设置:8 个常识推理子任务(BoolQ, PIQA, SIQA, HellaSwag, WinoGrande, ARC-e, ARC-c, OBQA),训练集合并(共 170K+ 条),测试集独立评估。模型:LLaMA-7B、LLaMA-13B、LLaMA2-7B、LLaMA3-8B。

图四:常识推理准确率汇总
模型方法参数占比平均准确率
LLaMA-7BChatGPT 零样本77.0
LLaMA-7BLoRA0.83%74.7
LLaMA-7BDoRA†(半秩)0.43%77.5 (+2.8)
LLaMA-7BDoRA0.84%78.4 (+3.7)
LLaMA-13BLoRA0.67%80.5
LLaMA-13BDoRA0.68%81.5 (+1.0)
LLaMA2-7BLoRA0.83%77.6
LLaMA2-7BDoRA0.84%79.7 (+2.1)
LLaMA3-8BLoRA0.70%80.8
LLaMA3-8BDoRA0.71%85.2 (+4.4)

几个值得注意的细节:

  1. DoRA 超过了 ChatGPT 零样本(78.4 vs 77.0)——用 7B 模型的参数高效微调打败了 GPT-3.5,而且只用了 0.84% 的参数在训练中更新。

  2. DoRA†(半秩)依然超越 LoRA(全秩):在 LLaMA-7B 上 DoRA†(0.43% 参数,r=8r=8)= 77.5,LoRA(0.83%,r=16r=16)= 74.7——用一半参数多了 +2.8 分。这是实际工程中更重要的结论,因为更少参数意味着更少显存、更快训练。

  3. 改进幅度不单调于模型规模:7B(+3.7)、13B(+1.0)、2-7B(+2.1)、3-8B(+4.4)。这说明改进不是单纯的”更大的模型 LoRA 能力更好所以 DoRA 帮助更小”,而是和模型架构本身的「LoRA 与 FT 之间的学习模式差距」有关。LLaMA3-8B 的架构改动(GQA、RoPE 改进等)可能让 LoRA 的耦合问题更明显,所以 DoRA 提升更大。

4.2 不同秩下的鲁棒性

实验设置:固定 LLaMA-7B,调整 r{4,8,16,32,64}r \in \{4, 8, 16, 32, 64\}

图五:不同秩下 LoRA vs DoRA 准确率(LLaMA-7B,常识推理)
秩 rLoRA 平均准确率DoRA 平均准确率差距
r=439.5(近随机)61.9+22.4
r=840.7(近随机)77.9+37.2
r=1670.977.5+6.6
r=3274.778.4+3.7
r=6465.872.1+6.3

最令人震惊的是 r=8 时 37 分的差距——LoRA 在这个秩下几乎崩溃(40.7% ≈ 随机猜测),而 DoRA 仍然保持了 77.9%。

为什么低秩下 LoRA 会崩溃? 回到 §3.3 的梯度分析:LoRA 的梯度能量必须同时被分配到幅度更新和方向更新两个自由度。当 rr 很小时,BABA 的容量极其有限,这个”两头兼顾”的要求变得不可能——要么方向优先,要么幅度优先,但不能同时做好,最终两者都做不好,准确率崩溃。

DoRA 给幅度更新单独划拨了一组参数(kk 个标量),完全不占 rr 的预算。所以 rr 可以专注于方向更新,即使 r=4r=4,也能做一些有意义的学习。

4.3 视觉语言理解(VL-BART)

实验设置:VL-BART(CLIP-ResNet101 + BARTBase),四个图文任务(VQAv2, GQA, NLVR2, MSCOCO)+ 四个视频文本任务(TVQA, How2QA, TVC, YC2C)。

方式参数占比图文平均分视频文本平均分
全量微调100%77.383.5
LoRA~6%76.5(略)
DoRA~6%77.485.4

图文任务上 DoRA 只用 6% 的参数就基本追上了全量微调(77.4 vs 77.3)。视频文本任务上 DoRA 甚至超过了全量微调(85.4 vs 83.5),比 LoRA 高 +1.9 分。视频文本任务需要更强的时序理解能力,这类任务通常需要更”细粒度”的权重调整,DoRA 的解耦设计在这里的优势更明显。

4.4 视觉指令微调(LLaVA-1.5-7B)

实验设置:LLaVA-1.5-7B(Vicuna-1.5-7B 语言模型 + CLIP ViT-L/336px 视觉编码器),在标准视觉指令微调数据上训练,评测 7 个 VL 基准。

方法参数占比VQAv2GQAVisWizSQAVQATPOPEMMBench均分
全量微调100%78.561.950.066.858.285.964.366.5
LoRA4.61%79.162.947.868.458.286.466.166.9
DoRA4.63%78.662.952.269.957.087.266.167.6

DoRA 均分 67.6,超过 LoRA(66.9)和全量微调(66.5)。特别是 VisWiz(视障用户视觉问答,任务更难)上 DoRA 比 LoRA 高 +4.4 分(52.2 vs 47.8),比全量微调也高 +2.2 分

DoRA 超过全量微调的原因可能是:视觉指令微调数据量有限,全量微调有过拟合的风险,而 DoRA 的约束结构(低秩方向 + 标量幅度)提供了隐式正则化。

4.5 不同训练样本量的鲁棒性

实验设置:LLaMA2-7B + Alpaca 数据集子集(1000 / 4000 / 7000 / 10000 条),在 MT-Bench 上用 GPT-4 打分(0-10 分)。

图六:MT-Bench 分数 vs 训练样本量(LLaMA2-7B)
样本量LoRADoRAVeRADVoRA
1,0005.415.705.215.43
4,0005.555.825.385.60
7,0005.685.985.405.71
10,0005.706.005.506.00

DoRA 的优势在所有样本量下都稳定(+0.29 到 +0.30 分),说明 DoRA 的提升不依赖数据量,不是”用更多数据把差距掩盖掉”的那种假进步。

**DVoRA(0.04% 参数)= DoRA(2.33% 参数)**在 10000 样本时(均为 6.00 分),同时都优于 LoRA(5.70)。这 58 倍的参数效率差距非常实用——意味着在资源极度受限的场景下(比如消费级 GPU),DVoRA 可以用 LoRA 的 1/50 参数量达到 DoRA 的准确率。

4.6 粒度分析:是否需要对所有层都做方向更新

散点图分析表明,当方向变化大时,幅度变化小。因此论文测试了一种”节约型”变体:只对注意力层(Q, K, V, O)做完整 DoRA(方向 + 幅度),对 MLP 层(gate, up, down)只训练幅度标量,不做方向 LoRA。

方法参数占比LLaMA-7BLLaMA-13B
LoRA(标准)0.83%74.780.5
DoRA(完整)0.84%78.181.5
DoRA(节约型)0.39%77.581.3

节约型 DoRA 用 0.39% 的参数(比 LoRA 少一半)超过 LoRA +2.8/+0.8 分。这说明 MLP 层的主要调整需求确实是幅度而非方向,把 rank 预算集中在注意力层的方向更新上更高效。

5. 与其他方法的设计对比

5.1 DoRA vs LoRA:结构差异总结

图七:DoRA 和 LoRA 的参数结构对比
graph TB
    subgraph LoRA_arch["LoRA 架构"]
        l_W0["W₀(冻结)"]
        l_B["B ∈ ℝ^{d×r}"]
        l_A["A ∈ ℝ^{r×k}"]
        l_delta["ΔW = BA(耦合幅度+方向)"]
        l_out["W' = W₀ + BA"]
        l_W0 --> l_out
        l_B --> l_delta
        l_A --> l_delta
        l_delta --> l_out
    end

    subgraph DoRA_arch["DoRA 架构"]
        d_W0["W₀(冻结,方向基准)"]
        d_m["m ∈ ℝ^{1×k}(幅度,独立训练)"]
        d_B["B ∈ ℝ^{d×r}"]
        d_A["A ∈ ℝ^{r×k}"]
        d_delta["ΔV = BA(只更新方向)"]
        d_norm["归一化 ‖W₀+BA‖_c"]
        d_out["W' = m · (W₀+BA)/‖W₀+BA‖_c"]
        d_W0 --> d_delta
        d_B --> d_delta
        d_A --> d_delta
        d_delta --> d_norm
        d_norm --> d_out
        d_m --> d_out
    end
属性LoRADoRA
额外参数r(d+k)r(d+k)r(d+k)+kr(d+k) + k
推理开销零(合并后)零(合并后)
幅度更新隐式(通过 BA)显式(单独标量)
方向更新隐式(通过 BA)显式(低秩 LoRA)
低秩鲁棒性低(r8r \leq 8 时崩溃)高(r=4r=4 仍有效)
HF PEFT 支持use_dora=Falseuse_dora=True

5.2 DoRA vs 权重归一化

权重归一化(Salimans & Kingma)用相同的数学分解,但目的是加速从头训练的收敛,从随机初始化出发,两部分都随机初始化。

DoRA 用同样的分解做预训练权重的微调,从 W0W_0 出发初始化,所以没有初始化敏感的问题。另外,权重归一化里 vv 也是可训练的全矩阵,而 DoRA 里 VV 是冻结的,只有 ΔV=BA\Delta V = BA(低秩)可训练。

5.3 DoRA vs SVD-based 压缩(SVD-LLM, ASVD, Swift-SVD)

关键区别:SVD-based 压缩是把 WW 截断成低秩矩阵(推理时权重是低秩的,存储量减少)。DoRA 是微调方法,训练后权重合并成稠密矩阵,不减少推理存储量,但提升下游任务准确率。

两类方法可以叠加:先用 SVD 压缩减少模型规模,再用 DoRA 做任务特化微调。这是未来的一个有趣应用方向。

5.4 DoRA vs QLoRA(量化微调)

QDoRA = QLoRA 骨架(4-bit 量化主干)+ DoRA 适配器:

W=mdequant(W0,4bit)+BAcW'_\ell = \mathbf{m}_\ell \cdot \frac{\text{dequant}(W_{0,\ell}^{4\text{bit}}) + BA}{\|\ldots\|_c}

在 Orca-Math(10 万条数学文字题)上:

  • QLoRA:exact-match = 0.08(LLaMA2-7B)/ 0.23(LLaMA3-8B)
  • QDoRA:exact-match = 0.27(LLaMA2-7B)/ 0.31(LLaMA3-8B)
  • 全量微调:exact-match ≈ 0.24(LLaMA3-8B)

QDoRA 超过全量微调(0.31 vs 0.24 on LLaMA3-8B),同时保持 QLoRA 的低显存优势。这说明在数学推理这类需要精细权重调整的任务上,DoRA 的解耦更新机制非常有效。

6. 局限性与边界条件

6.1 训练显存

原版 DoRA(不使用 detach 技巧)比 LoRA 多约 24–37% 显存,因为需要保存通过归一化算子的梯度图。带 detach 的高效版基本追平 LoRA。但在使用极低精度训练(int8 优化器)或内存极度受限的场景下,额外的 kk 个幅度标量和动态范数计算的开销仍需评估。

6.2 不同层最佳秩可能不同

DoRA 当前实现对所有层用相同的 rank rr。结合 AdaLoRA(自适应秩分配)的思路,理论上可以给不同层分配不同的方向 rank,配合全局的幅度训练,进一步提升参数效率。这个方向论文未做系统验证。

6.3 训练稳定性

论文中使用相同学习率同时训练 m\mathbf{m}(幅度,kk 个标量)和 {A,B}\{A, B\}(方向低秩矩阵)。两者梯度尺度不同,理论上最优学习率可能不同。如果遇到训练不稳定,分开设置 m\mathbf{m} 的学习率(通常可以比 A,BA, B 的学习率大 2-5 倍)会有帮助。

6.4 任务覆盖范围

论文重点测试了监督微调(SFT)和指令跟随(MT-Bench),没有直接测试 RLHF 或 DPO 等偏好学习场景。鉴于 DoRA 改变的是梯度动力学,理论上它对 DPO 的低秩更新也应该有类似改善,但需要实验验证。

7. 复现指南

7.1 代码和集成

官方 PyTorch 实现:https://github.com/NVlabs/DoRA

HuggingFace PEFT 集成(最简洁用法):

from peft import LoraConfig, get_peft_model

config = LoraConfig(
    r=16,
    lora_alpha=32,
    use_dora=True,           # ← 开启 DoRA,就这一行
    target_modules=["q_proj", "k_proj", "v_proj",
                    "up_proj", "down_proj"],
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM",
)
model = get_peft_model(model, config)

7.2 常识推理任务的超参数

以 LLaMA-7B 为例(完整参数见论文 Table 8):

rank r:        16(DoRA),8(DoRA†)
alpha:         32(DoRA),16(DoRA†)
dropout:       0.05
optimizer:     AdamW
lr:            2e-4
scheduler:     Linear decay
batch_size:    16
warmup_steps:  100
epochs:        3
target_modules: Q, K, V, Up, Down projection

7.3 权重分解分析工具(诊断用)

可以用下面的代码检查自己的模型,判断 LoRA 和 FT 的学习模式差异是否显著:

import torch

def weight_decomp_analysis(W0: torch.Tensor, W_ft: torch.Tensor):
    """
    计算 (ΔM, ΔD)——幅度差和方向差
    W0, W_ft: (d, k) 的权重矩阵
    """
    # 幅度(每列 ℓ₂ 范数)
    m0  = W0.norm(dim=0)   # (k,)
    mft = W_ft.norm(dim=0) # (k,)
    
    # 方向(每列归一化后的单位向量)
    v0  = W0  / m0.clamp(min=1e-8).unsqueeze(0)
    vft = W_ft / mft.clamp(min=1e-8).unsqueeze(0)
    
    # 幅度差(公式 4)
    delta_M = (mft - m0).abs().mean().item()
    
    # 方向差(公式 5)
    cos_sim = (v0 * vft).sum(dim=0).clamp(-1, 1)  # (k,)
    delta_D = (1 - cos_sim).mean().item()
    
    return delta_M, delta_D

# 用法:对比 LoRA 微调权重和 FT 微调权重
# 如果 LoRA 的 (ΔD, ΔM) 呈正相关、FT 的呈负相关,说明 DoRA 会有帮助

对相关系数 Pearson:

import numpy as np

def pearson_corr(delta_D_list, delta_M_list):
    d = np.array(delta_D_list)
    m = np.array(delta_M_list)
    return np.corrcoef(d, m)[0, 1]

# FT 目标: < -0.5(负相关)
# LoRA 现状: > +0.5(正相关)
# DoRA 效果: 接近 FT(负相关)

8. 总结与个人思考

8.1 贡献总结

DoRA 的贡献可以归纳为三层:

第一层(诊断工具):权重分解分析。把任何微调方法的权重变化分解为幅度差和方向差,画散点图,一目了然地展示”这个方法学到的是什么结构的更新”。这个工具独立于 DoRA 本身,未来研究 PEFT 都可以用它做诊断。

第二层(方法):DoRA 本身。一个极其简洁的设计:给幅度加一组标量可训练参数,方向继续用 LoRA。代码改动一行(use_dora=True),但效果系统性优于 LoRA。

第三层(理论连接):梯度分析(§3.3)证明 DoRA 的梯度在几何上自动路由能量——余弦对齐时幅度大幅更新,余弦垂直时方向大幅更新,这在数学上解释了为什么 DoRA 的散点图呈负相关,为什么它比 LoRA 更接近 FT 的学习模式。

8.2 我的几个判断

最重要的实验结果:r=8 时 LoRA 40.7% vs DoRA 77.9%(+37.2 分差距)。这不是精度微调,是数量级的差异,说明在计算受限场景(低秩)下 DoRA 的优势是本质性的,不是”打磨”出来的。

最有价值的 insight:梯度的余弦解释(公式 8)。它把一个经验观察(散点图呈负相关)和数学机制(余弦投影路由梯度能量)连接起来,让人真正理解”为什么这样做有效”,而不只是”实验证明有效”。

让我最意外的结论:DVoRA(0.04% 参数)= DoRA(2.33% 参数)在 MT-Bench 上持平,而且双双超过 LoRA(2.31% 参数)。58 倍的参数差距,准确率无损——VeRA 的共享随机矩阵 + DoRA 的幅度/方向解耦组合起来比任何一方单独使用都更有效。

实践上的建议

  • 如果你现在用 LoRA,把 use_dora=True 加上,基本没有任何代价。
  • 如果 GPU 显存受限,用 DoRA† 把 rank 减半——比标准 LoRA 省一半参数,还更准。
  • 如果显存极度受限,试试 DVoRA(VeRA backend + DoRA 幅度训练)。
  • 诊断工具(§7.3)可以帮你判断自己的场景下 LoRA 与 FT 的差距是否主要来自幅度/方向耦合问题。

8.3 还有哪些开放问题

  1. 是否可以对幅度和方向用不同的学习率? 两者梯度尺度本质上不同(mm 是标量向量,A,B\nabla_{A,B} 是矩阵)。分离学习率是否能进一步提升?

  2. DoRA 在偏好学习(DPO, PPO-RLHF)上是否同样有效? RL fine-tuning 的梯度信号比 SFT 更嘈杂,解耦幅度/方向可能更有价值,也可能因为梯度信号本就方向不稳定而失效。

  3. 能否自适应地给不同层分配不同的 rank rr(结合 AdaLoRA 的思路)? 在同样的总参数预算下,对敏感层用大 rr、对不敏感层用纯幅度更新,应该能进一步提升参数效率。

  4. DoRA 在极长上下文微调(Context Length Extension)中的表现? 长上下文微调需要调整 RoPE 相关权重,幅度/方向分离是否对这种特殊的微调模式有不同的效果?

附录 A:梯度推导的完整展开

A.1 不用 detach 时的完整梯度(含投影算子)

不使用 detach 时,列范数 Cn=VnC_n = \|V'_n\| 是计算图的一部分,反向传播需要通过它传递。对第 nnVnV'_n,根据链式法则:

\frac{\partial \mathcal{L}}{\partial V'_n} = \frac{m_n}{C_n} \left(I - \frac{V'_n V'^{\top}_n}{C_n^2}\right) \frac{\partial \mathcal{L}}{\partial W'_{:,n}} \tag{A.1}

这个公式本质上是把权重梯度 LW:,n\frac{\partial \mathcal{L}}{\partial W'_{:,n}} 投影到和 VnV'_n 正交的子空间上。正交投影的那部分(Vn\perp V'_n)用于更新方向;平行于 VnV'_n 的那部分被 mnm_n 的梯度(公式 8)吸收用于更新幅度。

这才是最完整的解耦证明:即使不用 detach,梯度几何也自动把能量分配到「幅度通道」和「方向通道」,两者是正交的。detach 只是去掉了二阶的 VnVn/Cn2V'_n V'^{\top}_n / C_n^2 项,在实践中近似误差极小。

A.2 为什么 detach 近似误差小

被去掉的项是:

mnCn3(LW:,nVn)Vn- \frac{m_n}{C_n^3} \left(\frac{\partial \mathcal{L}}{\partial W'_{:,n}} \cdot V'_n\right) V'_n

其范数 mnCn2LW:,nV^n\approx \frac{m_n}{C_n^2} \left|\frac{\partial \mathcal{L}}{\partial W'_{:,n}} \cdot \hat{V}'_n\right|,其中 V^n=Vn/Cn\hat{V}'_n = V'_n / C_n 是单位方向向量。

两种场景:

  • 场景 A(需要更新方向):损失梯度 Vn\perp V'_n → 内积 0\approx 0 → 被去掉的项 0\approx 0,detach 误差忽略不计。

  • 场景 B(需要更新幅度):损失梯度 Vn\parallel V'_n → 内积较大,但此时幅度梯度 mnL\nabla_{m_n}\mathcal{L} 本身主导更新,方向梯度 VnL\nabla_{V'_n}\mathcal{L} 本来就应该小 → 被去掉的项即使存在也不影响最终结果。

两个场景下误差都可控,这就是为什么 detach 只损失 0.2 分但节省 24.4% 显存。

A.3 参数量对比(逐层数字)

以 LLaMA-7B 的注意力投影层为例,d=k=4096d = k = 4096

方法单层参数量(r=16r=16全量 FT 的比例
全量微调4096×4096=16,777,2164096 \times 4096 = 16,777,216100%
LoRA16×(4096+4096)=131,07216 \times (4096 + 4096) = 131,0720.78%
DoRA131,072+4,096=135,168131,072 + 4,096 = 135,1680.81%
VeRA4096+4096=8,1924096 + 4096 = 8,1920.049%
DVoRA8,192+4,096=12,2888,192 + 4,096 = 12,2880.073%
Prefix(L=10L=102×10×4096=81,9202 \times 10 \times 4096 = 81,9200.49%

DoRA 比 LoRA 多了 k=4096k = 4096 个幅度标量,额外参数量 = 3.1%(相对 LoRA),占整个模型参数的约 0.024%——完全可以忽略。

附录 B:实验细节与可复现细节

B.1 各任务超参数汇总

模型任务rank ralphadropoutoptimizerLR批次epoch
LLaMA-7B常识推理16320.05AdamW2e-4163
LLaMA-13B常识推理32640.05AdamW1e-4163
LLaMA2-7B常识推理32640.05AdamW2e-4163
LLaMA3-8B常识推理32640.05AdamW1e-4163
VL-BART图文多任务1281280.0AdamW1e-33007
VL-BART视频文本1281280.0AdamW3e-44020
LLaVA-1.5-7B视觉指令微调1282560.05AdamW2e-4161
LLaMA-7B指令微调(Alpaca)64640.0AdamW4e-4161

B.2 显存测试数据

在单 A100 80GB 上,LLaMA-7B + 常识推理数据集(batch=16, seq=256):

配置显存(GB)备注
DoRA(无 detach)37.3完整梯度图
DoRA(有 detach)28.2论文默认实现
LoRA(同 rank)~26.8参考点
全量微调80+OOM on A100

B.3 MT-Bench 评测说明

MT-Bench 包含 80 道多轮对话题(8 大类:写作、角色扮演、提取、推理、数学、编程、STEM、人文),每题两轮,GPT-4 逐题打 1-10 分,最终取平均。

这种评测方式的优点是跨任务覆盖广、对长文本生成质量敏感;缺点是 GPT-4 评分有噪声,不同批次可能有 ±0.2 的方差。论文里 DoRA 在 LLaMA2-7B 上达到 6.0(LoRA: 5.7),差值 0.3 在多次运行中比较稳定。

附录 C:DoRA 的广义解读

C.1 从「自适应学习率」角度理解

从公式 (7) 看,DoRA 对 VV' 的梯度是把权重梯度乘以 m/Cm / C(列方向上的幅度与范数之比)。这等价于给每一列的更新施加一个依赖幅度的自适应缩放因子

列幅度大(mnm_n 大)、归一化后范数小的列(可能是高激活列)会得到更大的梯度缩放,训练时更新更快。这有点像 Adam 的自适应效果,但不是基于梯度历史,而是基于当前权重幅度的几何结构。

从这个角度看,DoRA 是在 LoRA 的梯度上叠加了一层几何预条件器(geometric preconditioner),改善了 LoRA 的优化轨迹。

C.2 从「隐式正则化」角度理解

DoRA 的幅度向量 m\mathbf{m} 经过训练后,会根据任务需要调整每列的重要性权重——对任务重要的方向,幅度会增大;对任务不重要的方向,幅度会减小甚至接近零(实际上做了软稀疏化)。

相比 LoRA 把增量 ΔW=BA\Delta W = BA 均匀地加到每列,DoRA 的分解允许部分列只做小幅度调整(方向不变,幅度微调),其他列做大幅方向旋转。这种非均匀更新有隐式正则化效果,可以解释为什么 DoRA 在训练数据少时(1000 条样本)仍能稳定超过 LoRA。

C.3 DoRA 与 Riemannian 优化的联系

把权重矩阵的列视为球面上的点(Sd1S^{d-1}),幅度视为半径,权重空间就是一个缩放球面(scaled sphere)。DoRA 把优化问题分解为两部分:

  1. R>0k\mathbb{R}_{>0}^k 上优化幅度向量 m\mathbf{m}(欧氏空间)
  2. (Sd1)k(S^{d-1})^k 上优化方向矩阵 VV'(乘积球面流形)

虽然 DoRA 的实现并没有用到黎曼优化(Riemannian SGD/Adam),但 detach 技巧实际上是在近似流形上的投影梯度:VL(m/C)WL\nabla_{V'}\mathcal{L} \approx (m/C) \cdot \nabla_{W'}\mathcal{L} 忽略了球面约束引入的曲率修正项 VV/C2V'V'^{\top}/C^2

这说明 DoRA 的学习曲线有流形优化的味道——它的轨迹比 LoRA 更接近”权重流形”上的最优路径。


笔记到此结束。如需进一步了解 LoRA 家族的其他方法(AdaLoRA、PiSSA、MoRA、DVoRA),欢迎参考本站其他博文。