低秩分解与低秩适配——从矩阵分析基础到 LoRA/QLoRA

目录

  • 第一部分:低秩分解基础理论
    • 第一章:绪论------模型压缩的第四条路
    • 第二章:矩阵低秩分解的数学基础------SVD 与 Eckart-Young 定理
    • 第三章:张量分解------从矩阵到高阶张量
  • 第二部分:低秩分解在模型压缩中的应用
    • 第四章:权重矩阵的低秩近似
    • 第五章:卷积层的低秩分解
    • 第六章:注意力机制的低秩近似
  • 第三部分:低秩适配(LoRA)与参数高效微调
    • 第七章:LoRA 的数学原理
    • 第八章:LoRA 的变体与扩展
    • 第九章:QLoRA------量化低秩适配
    • 第十章:DoRA、AdaLoRA 与自适应秩选择
  • 第四部分:低秩结构的理论分析
    • 第十一章:内在维度假说与低秩结构
    • 第十二章:神经网络的低秩偏置
    • 第十三章:低秩与泛化理论
  • 第五部分:完整可运行代码实现
    • 第十四章:从零实现 SVD 分解与低秩近似
    • 第十五章:从零实现权重低秩分解压缩
    • 第十六章:从零实现 LoRA 与 QLoRA
    • 第十七章:完整低秩压缩 Pipeline 与精度对比
  • 附录

第一部分:低秩分解基础理论


第一章:绪论------模型压缩的第四条路

1.1 低秩分解的定位

在前三篇文档中,我们讨论了量化剪枝蒸馏。低秩分解是模型压缩的第四大范式:

范式 核心思想 压缩方式 数学工具
量化 降低参数精度 减少每个参数的比特数 信息论
剪枝 移除冗余参数 减少非零参数数量 稀疏优化
蒸馏 知识迁移 训练更小的模型 KL 散度
低秩分解 矩阵分解 将大矩阵分解为小矩阵 线性代数

1.1.1 核心思想

低秩分解(Low-Rank Factorization) 的核心思想是:将大的权重矩阵分解为多个小矩阵的乘积,利用矩阵的低秩结构来减少参数量。

例子 :对于权重矩阵 W∈Rm×nW \in \mathbb{R}^{m \times n}W∈Rm×n,将其分解为:

W≈AB,A∈Rm×r,B∈Rr×nW \approx A B, \quad A \in \mathbb{R}^{m \times r}, \quad B \in \mathbb{R}^{r \times n}W≈AB,A∈Rm×r,B∈Rr×n

其中 r≪min⁡(m,n)r \ll \min(m, n)r≪min(m,n) 是秩(rank)

参数量对比

  • 原始:mnmnmn 个参数
  • 低秩分解:(m+n)r(m + n)r(m+n)r 个参数
  • 压缩比:mn(m+n)r\frac{mn}{(m+n)r}(m+n)rmn

当 r=mnk(m+n)r = \frac{mn}{k(m+n)}r=k(m+n)mn 时,压缩比为 kkk。

1.1.2 为什么权重矩阵是低秩的?

观察 :训练好的神经网络权重矩阵通常具有低有效秩(low effective rank)

定理 1.1(权重矩阵的有效秩) :设权重矩阵 W∈Rm×nW \in \mathbb{R}^{m \times n}W∈Rm×n 的奇异值为 σ1≥σ2≥⋯≥σmin⁡(m,n)\sigma_1 \geq \sigma_2 \geq \dots \geq \sigma_{\min(m,n)}σ1≥σ2≥⋯≥σmin(m,n)。定义有效秩为:

reff(W)=exp⁡(−∑ipilog⁡pi),pi=σi∑jσjr_{\text{eff}}(W) = \exp\left(-\sum_i p_i \log p_i\right), \quad p_i = \frac{\sigma_i}{\sum_j \sigma_j}reff(W)=exp(−i∑pilogpi),pi=∑jσjσi

实证观察 :对于典型的深度学习模型,reff(W)/min⁡(m,n)r_{\text{eff}}(W) / \min(m, n)reff(W)/min(m,n) 通常在 10%-50% 之间------意味着大部分奇异值接近零,权重矩阵近似低秩。

1.2 低秩分解的优势

效益 说明 数学刻画
减少参数量 mn→(m+n)rmn \to (m+n)rmn→(m+n)r 压缩比 mn/((m+n)r)mn / ((m+n)r)mn/((m+n)r)
减少计算量 O(mn)→O((m+n)r)O(mn) \to O((m+n)r)O(mn)→O((m+n)r) 矩阵乘法的 FLOPs
减少存储 更少的参数需要存储 与参数量成正比
提高泛化 低秩约束起到正则化作用 减少模型复杂度
参数高效微调 LoRA 只训练少量参数 r(m+n)≪mnr(m+n) \ll mnr(m+n)≪mn

第二章:矩阵低秩分解的数学基础------SVD 与 Eckart-Young 定理

2.1 奇异值分解(SVD)

2.1.1 SVD 的定义

定理 2.1(奇异值分解) :任意矩阵 W∈Rm×nW \in \mathbb{R}^{m \times n}W∈Rm×n 可以分解为:

W=UΣVTW = U \Sigma V^TW=UΣVT

其中:

  • U∈Rm×mU \in \mathbb{R}^{m \times m}U∈Rm×m 是正交矩阵(左奇异向量)
  • Σ∈Rm×n\Sigma \in \mathbb{R}^{m \times n}Σ∈Rm×n 是对角矩阵(奇异值 σ1≥σ2≥⋯≥0\sigma_1 \geq \sigma_2 \geq \dots \geq 0σ1≥σ2≥⋯≥0)
  • V∈Rn×nV \in \mathbb{R}^{n \times n}V∈Rn×n 是正交矩阵(右奇异向量)

紧凑 SVD :如果 rank(W)=r\text{rank}(W) = rrank(W)=r,则:

W=UrΣrVrTW = U_r \Sigma_r V_r^TW=UrΣrVrT

其中 Ur∈Rm×rU_r \in \mathbb{R}^{m \times r}Ur∈Rm×r,Σr∈Rr×r\Sigma_r \in \mathbb{R}^{r \times r}Σr∈Rr×r,Vr∈Rn×rV_r \in \mathbb{R}^{n \times r}Vr∈Rn×r。

2.1.2 SVD 的几何解释

定理 2.2(SVD 的几何意义) :SVD 将线性变换 W:Rn→RmW: \mathbb{R}^n \to \mathbb{R}^mW:Rn→Rm 分解为三个步骤:

  1. 旋转/反射 :VTV^TVT 将输入空间旋转到奇异向量方向
  2. 缩放 :Σ\SigmaΣ 沿每个奇异向量方向缩放 σi\sigma_iσi 倍
  3. 旋转/反射 :UUU 将结果旋转到输出空间

推论 2.1 :奇异值 σi\sigma_iσi 衡量了 WWW 在第 iii 个奇异方向上的"强度"------σi\sigma_iσi 越大,该方向越重要。

2.1.3 SVD 的计算

算法 2.1(SVD 的计算步骤)

复制代码
1. 计算 W^T W 的特征值分解:W^T W = V Λ V^T
2. 奇异值:σ_i = √λ_i
3. 左奇异向量:u_i = W v_i / σ_i

复杂度

  • 完整 SVD:O(mnmin⁡(m,n))O(mn \min(m, n))O(mnmin(m,n))
  • 截断 SVD(前 rrr 个奇异值):O(mnr)O(mnr)O(mnr)

2.2 Eckart-Young-Mirsky 定理

2.2.1 定理陈述

定理 2.3(Eckart-Young-Mirsky 定理) :设 W∈Rm×nW \in \mathbb{R}^{m \times n}W∈Rm×n 的 SVD 为 W=UΣVTW = U \Sigma V^TW=UΣVT,奇异值为 σ1≥σ2≥⋯≥σp\sigma_1 \geq \sigma_2 \geq \dots \geq \sigma_pσ1≥σ2≥⋯≥σp(p=min⁡(m,n)p = \min(m, n)p=min(m,n))。则对于任意秩不超过 rrr 的矩阵 XXX:

∥W−Wr∥F≤∥W−X∥F\|W - W_r\|_F \leq \|W - X\|_F∥W−Wr∥F≤∥W−X∥F

∥W−Wr∥2≤∥W−X∥2\|W - W_r\|_2 \leq \|W - X\|_2∥W−Wr∥2≤∥W−X∥2

其中 Wr=UrΣrVrTW_r = U_r \Sigma_r V_r^TWr=UrΣrVrT 是 WWW 的最佳秩 rrr 近似(截断 SVD)。

等号成立当且仅当 X=WrX = W_rX=Wr(对于 Frobenius 范数,在一定条件下)。

2.2.2 Frobenius 范数误差

定理 2.4(最佳秩 rrr 近似的误差)

∥W−Wr∥F=∑i=r+1pσi2\|W - W_r\|F = \sqrt{\sum{i=r+1}^{p} \sigma_i^2}∥W−Wr∥F=i=r+1∑pσi2

证明

∥W−Wr∥F2=∥U(Σ−Σr)VT∥F2=∥Σ−Σr∥F2=∑i=r+1pσi2\|W - W_r\|_F^2 = \|U(\Sigma - \Sigma_r)V^T\|_F^2 = \|\Sigma - \Sigma_r\|F^2 = \sum{i=r+1}^{p} \sigma_i^2∥W−Wr∥F2=∥U(Σ−Σr)VT∥F2=∥Σ−Σr∥F2=i=r+1∑pσi2

因为 Frobenius 范数在正交变换下不变。□\square□

推论 2.2 :如果奇异值快速衰减(σr+1,σr+2,...\sigma_{r+1}, \sigma_{r+2}, \dotsσr+1,σr+2,... 很小),则低秩近似误差很小。

2.2.3 谱范数误差

定理 2.5(谱范数下的最佳近似)

∥W−Wr∥2=σr+1\|W - W_r\|2 = \sigma{r+1}∥W−Wr∥2=σr+1

证明 :谱范数等于最大奇异值。∥W−Wr∥2=σr+1\|W - W_r\|2 = \sigma{r+1}∥W−Wr∥2=σr+1。□\square□

2.2.4 Eckart-Young 定理的重要性

核心意义 :截断 SVD 是全局最优 的低秩近似------在所有秩不超过 rrr 的矩阵中,截断 SVD 的误差最小。

这为低秩分解压缩提供了坚实的理论基础:使用 SVD 进行低秩近似是最佳策略

2.3 奇异值的衰减与低秩性

2.3.1 奇异值衰减的度量

定义 2.1(有效秩)

reff(W)=(∑iσi)2∑iσi2r_{\text{eff}}(W) = \frac{\left(\sum_i \sigma_i\right)^2}{\sum_i \sigma_i^2}reff(W)=∑iσi2(∑iσi)2

性质

  • 1≤reff≤p1 \leq r_{\text{eff}} \leq p1≤reff≤p
  • 当所有奇异值相等时,reff=pr_{\text{eff}} = preff=p
  • 当只有一个非零奇异值时,reff=1r_{\text{eff}} = 1reff=1

2.3.2 奇异值衰减的理论分析

定理 2.6(随机矩阵的奇异值分布) :设 W∈Rm×nW \in \mathbb{R}^{m \times n}W∈Rm×n 的元素独立同分布于 N(0,1/n)\mathcal{N}(0, 1/n)N(0,1/n)。当 m,n→∞m, n \to \inftym,n→∞ 且 m/n→γm/n \to \gammam/n→γ 时,奇异值的极限分布服从 Marchenko-Pastur 分布

p(σ)=12πγσ2(σ+2−σ2)(σ2−σ−2),σ∈σ−,σ+p(\sigma) = \frac{1}{2\pi \gamma \sigma^2} \sqrt{(\sigma_+^2 - \sigma^2)(\sigma^2 - \sigma_-^2)}, \quad \sigma \in \\sigma_-, \\sigma_+p(σ)=2πγσ21(σ+2−σ2)(σ2−σ−2) ,σ∈σ−,σ+

其中 σ±=1±γ\sigma_\pm = 1 \pm \sqrt{\gamma}σ±=1±γ 。

推论 2.3 :对于随机初始化的权重矩阵,奇异值分布是连续的,没有明显的低秩结构。但训练后的权重矩阵通常表现出快速的奇异值衰减------这是低秩分解有效的关键原因。

2.3.3 训练导致低秩的理论解释

定理 2.7(梯度下降的低秩偏置) :在过参数化的线性网络中,梯度下降训练倾向于找到低秩的权重矩阵。

直觉:梯度下降沿着损失函数的"最陡下降方向"更新权重。在过参数化的情况下,这些方向通常是低秩的------因为高秩方向对应的奇异值很小,梯度信号很弱。


第三章:张量分解------从矩阵到高阶张量

3.1 张量的基本概念

3.1.1 张量的定义

定义 3.1(张量) :NNN 阶张量是 NNN 个向量空间的张量积中的元素:

T∈RI1×I2×⋯×IN\mathcal{T} \in \mathbb{R}^{I_1 \times I_2 \times \dots \times I_N}T∈RI1×I2×⋯×IN

  • 1 阶张量 = 向量
  • 2 阶张量 = 矩阵
  • 3 阶及以上 = 高阶张量

3.1.2 张量的展开矩阵

定义 3.2(模式-nnn 展开) :将张量 T∈RI1×⋯×IN\mathcal{T} \in \mathbb{R}^{I_1 \times \dots \times I_N}T∈RI1×⋯×IN 沿第 nnn 个模式展开为矩阵 T(n)∈RIn×(I1...In−1In+1...IN)T_{(n)} \in \mathbb{R}^{I_n \times (I_1 \dots I_{n-1} I_{n+1} \dots I_N)}T(n)∈RIn×(I1...In−1In+1...IN)。

3.2 CP 分解(CANDECOMP/PARAFAC)

3.2.1 定义

定义 3.3(CP 分解) :NNN 阶张量 T\mathcal{T}T 的秩 RRR CP 分解为:

T≈∑r=1Rar(1)∘ar(2)∘⋯∘ar(N)\mathcal{T} \approx \sum_{r=1}^{R} \mathbf{a}_r^{(1)} \circ \mathbf{a}_r^{(2)} \circ \dots \circ \mathbf{a}_r^{(N)}T≈r=1∑Rar(1)∘ar(2)∘⋯∘ar(N)

其中 ar(n)∈RIn\mathbf{a}_r^{(n)} \in \mathbb{R}^{I_n}ar(n)∈RIn 是第 nnn 个模式的因子向量,∘\circ∘ 表示外积。

参数量 :R∑n=1NInR \sum_{n=1}^{N} I_nR∑n=1NIn

3.2.2 CP 分解的计算

算法 3.1(交替最小二乘,ALS)

复制代码
输入:张量 T,秩 R
输出:因子矩阵 A^(1), ..., A^(N)

1. 初始化:随机初始化 A^(1), ..., A^(N)
2. 重复:
   对 n = 1, ..., N:
     固定其他因子,更新 A^(n):
     A^(n) = T_(n) (A^(N) ⊙ ... ⊙ A^(n+1) ⊙ A^(n-1) ... ⊙ A^(1))†
3. 直到收敛

其中 ⊙\odot⊙ 是 Khatri-Rao 积,†\dagger† 是伪逆。

3.3 Tucker 分解

3.3.1 定义

定义 3.4(Tucker 分解)

T≈G×1U(1)×2U(2)⋯×NU(N)\mathcal{T} \approx \mathcal{G} \times_1 U^{(1)} \times_2 U^{(2)} \dots \times_N U^{(N)}T≈G×1U(1)×2U(2)⋯×NU(N)

其中:

  • G∈RR1×R2×⋯×RN\mathcal{G} \in \mathbb{R}^{R_1 \times R_2 \times \dots \times R_N}G∈RR1×R2×⋯×RN 是核心张量
  • U(n)∈RIn×RnU^{(n)} \in \mathbb{R}^{I_n \times R_n}U(n)∈RIn×Rn 是第 nnn 个模式的因子矩阵

参数量 :∏n=1NRn+∑n=1NInRn\prod_{n=1}^{N} R_n + \sum_{n=1}^{N} I_n R_n∏n=1NRn+∑n=1NInRn

3.3.2 Tucker 分解与 SVD 的关系

定理 3.1(Tucker 分解是 SVD 的高阶推广):对于 2 阶张量(矩阵),Tucker 分解退化为 SVD:

W=UΣVT=Σ×1U×2VW = U \Sigma V^T = \Sigma \times_1 U \times_2 VW=UΣVT=Σ×1U×2V

定理 3.2(高阶 SVD,HOSVD) :任意 NNN 阶张量可以精确分解为:

T=G×1U(1)×2U(2)⋯×NU(N)\mathcal{T} = \mathcal{G} \times_1 U^{(1)} \times_2 U^{(2)} \dots \times_N U^{(N)}T=G×1U(1)×2U(2)⋯×NU(N)

其中 U(n)U^{(n)}U(n) 是模式-nnn 展开矩阵 T(n)T_{(n)}T(n) 的左奇异向量。


第二部分:低秩分解在模型压缩中的应用


第四章:权重矩阵的低秩近似

4.1 全连接层的低秩分解

4.1.1 问题形式化

对于全连接层 Y=XWY = XWY=XW,W∈Rm×nW \in \mathbb{R}^{m \times n}W∈Rm×n,将其分解为:

W≈AB,A∈Rm×r,B∈Rr×nW \approx AB, \quad A \in \mathbb{R}^{m \times r}, \quad B \in \mathbb{R}^{r \times n}W≈AB,A∈Rm×r,B∈Rr×n

计算变为 :Y=X(AB)=(XA)BY = X(AB) = (XA)BY=X(AB)=(XA)B------先计算 XAXAXA(O(nr⋅T)O(nr \cdot T)O(nr⋅T)),再计算结果乘以 BBB(O(mr⋅T)O(mr \cdot T)O(mr⋅T))。

总 FLOPs :O((m+n)rT)O((m+n)rT)O((m+n)rT) vs O(mnT)O(mnT)O(mnT)------当 r<mnm+nr < \frac{mn}{m+n}r<m+nmn 时,计算量减少。

4.1.2 SVD 分解

算法 4.1(SVD 低秩近似)

复制代码
输入:权重矩阵 W,目标秩 r
输出:分解后的 A, B

1. 计算 SVD:W = U Σ V^T
2. 截断:U_r = U[:, :r], Σ_r = Σ[:r, :r], V_r = V[:, :r]
3. 分解:A = U_r Σ_r^{1/2}, B = Σ_r^{1/2} V_r^T
返回 A, B

误差 :∥W−AB∥F=∑i=r+1pσi2\|W - AB\|F = \sqrt{\sum{i=r+1}^{p} \sigma_i^2}∥W−AB∥F=∑i=r+1pσi2

4.1.3 非对称分解

问题 :SVD 分解得到的 AAA 和 BBB 是特殊的(正交矩阵)。更一般的分解可能更优。

定义 4.1(非对称分解)

min⁡A,B∥W−AB∥F2\min_{A, B} \|W - AB\|_F^2A,Bmin∥W−AB∥F2

定理 4.1(非对称分解的最优解):最优解为:

A=UrΣr1/2Q,B=Q−1Σr1/2VrTA = U_r \Sigma_r^{1/2} Q, \quad B = Q^{-1} \Sigma_r^{1/2} V_r^TA=UrΣr1/2Q,B=Q−1Σr1/2VrT

其中 QQQ 是任意 r×rr \times rr×r 可逆矩阵。

推论 4.1 :SVD 分解(Q=IQ = IQ=I)是所有非对称分解中的一个特解,但在 Frobenius 范数下是最优的。

4.2 低秩分解的微调

4.2.1 问题

直接 SVD 分解的问题:截断 SVD 最小化的是权重矩阵的近似误差,而不是模型输出的误差。这可能导致精度下降。

4.2.2 数据感知的低秩分解

定义 4.2(数据感知分解)

min⁡A,B∥(W−AB)X∥F2=min⁡A,B∥(W−AB)X∥F2\min_{A, B} \|(W - AB)X\|F^2 = \min{A, B} \|(W - AB)X\|_F^2A,Bmin∥(W−AB)X∥F2=A,Bmin∥(W−AB)X∥F2

定理 4.2(数据感知分解的最优解) :设 H=XXTH = XX^TH=XXT 是输入的协方差矩阵,则最优分解为:

Wrdata=Ur(H)(Σr(H))1/2Q⋅Q−1(Σr(H))1/2(Vr(H))TW_{r}^{\text{data}} = U_r^{(H)} (\Sigma_r^{(H)})^{1/2} Q \cdot Q^{-1} (\Sigma_r^{(H)})^{1/2} (V_r^{(H)})^TWrdata=Ur(H)(Σr(H))1/2Q⋅Q−1(Σr(H))1/2(Vr(H))T

其中 Ur(H),Σr(H),Vr(H)U_r^{(H)}, \Sigma_r^{(H)}, V_r^{(H)}Ur(H),Σr(H),Vr(H) 是 WWW 在 HHH-加权范数下的 SVD。

直觉:数据感知分解优先保留那些对输入数据影响最大的方向。

4.3 逐层分解 vs 全局分解

4.3.1 逐层分解

方法:独立地对每一层的权重矩阵进行 SVD 分解。

优点:简单,每层可以独立优化秩。

缺点:没有考虑层间误差传播。

4.3.2 全局分解

方法:联合优化所有层的分解,最小化全局输出误差。

目标

min⁡{Al,Bl}∑l=1L∥(Wl−AlBl)Xl∥F2\min_{\{A_l, B_l\}} \sum_{l=1}^{L} \|(W_l - A_l B_l) X_l\|_F^2{Al,Bl}minl=1∑L∥(Wl−AlBl)Xl∥F2

挑战:这是一个非凸优化问题,需要迭代求解。


第五章:卷积层的低秩分解

5.1 卷积的矩阵表示

5.1.1 卷积核的展开

定义 5.1(卷积核矩阵) :对于卷积层的权重张量 W∈RCout×Cin×K×K\mathcal{W} \in \mathbb{R}^{C_{\text{out}} \times C_{\text{in}} \times K \times K}W∈RCout×Cin×K×K,可以将其展开为矩阵:

W(2)∈RCin×(CoutK2)W_{(2)} \in \mathbb{R}^{C_{\text{in}} \times (C_{\text{out}} K^2)}W(2)∈RCin×(CoutK2)

5.1.2 卷积的低秩分解

方法 1:空间分解

W≈W1∗W2\mathcal{W} \approx \mathcal{W}_1 * \mathcal{W}_2W≈W1∗W2

将 K×KK \times KK×K 卷积分解为 K×1K \times 1K×1 和 1×K1 \times K1×K 卷积的级联。

方法 2:通道分解

W≈A∗B\mathcal{W} \approx \mathcal{A} * \mathcal{B}W≈A∗B

其中 A∈RCout×r×1×1\mathcal{A} \in \mathbb{R}^{C_{\text{out}} \times r \times 1 \times 1}A∈RCout×r×1×1,B∈Rr×Cin×K×K\mathcal{B} \in \mathbb{R}^{r \times C_{\text{in}} \times K \times K}B∈Rr×Cin×K×K。

5.2 卷积分解的理论分析

定理 5.1(卷积分解的计算节省) :将 K×KK \times KK×K 卷积分解为 K×1K \times 1K×1 和 1×K1 \times K1×K 卷积:

  • 原始 FLOPs:O(CoutCinK2HW)O(C_{\text{out}} C_{\text{in}} K^2 HW)O(CoutCinK2HW)
  • 分解后 FLOPs:O(CoutCinK⋅2HW)O(C_{\text{out}} C_{\text{in}} K \cdot 2 HW)O(CoutCinK⋅2HW)
  • 节省比:K/2K/2K/2

对于 K=3K = 3K=3,节省 1.5 倍;对于 K=5K = 5K=5,节省 2.5 倍;对于 K=7K = 7K=7,节省 3.5 倍。


第六章:注意力机制的低秩近似

6.1 自注意力的计算复杂度

6.1.1 标准自注意力

Attention(Q,K,V)=softmax(QKTdk)V\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right) VAttention(Q,K,V)=softmax(dk QKT)V

其中 Q,K,V∈Rn×dQ, K, V \in \mathbb{R}^{n \times d}Q,K,V∈Rn×d。

计算复杂度

  • QKTQK^TQKT:O(n2d)O(n^2 d)O(n2d)
  • softmax\text{softmax}softmax:O(n2)O(n^2)O(n2)
  • 乘以 VVV:O(n2d)O(n^2 d)O(n2d)
  • 总复杂度 :O(n2d)O(n^2 d)O(n2d)

6.1.2 低秩注意力

思想 :将 QQQ 和 KKK 投影到低维空间:

Q~=QPQ,K~=KPK\tilde{Q} = Q P_Q, \quad \tilde{K} = K P_KQ~=QPQ,K~=KPK

其中 PQ,PK∈Rd×rP_Q, P_K \in \mathbb{R}^{d \times r}PQ,PK∈Rd×r,r≪dr \ll dr≪d。

低秩注意力

Attentionlow-rank=softmax(Q~K~Tr)V\text{Attention}_{\text{low-rank}} = \text{softmax}\left(\frac{\tilde{Q} \tilde{K}^T}{\sqrt{r}}\right) VAttentionlow-rank=softmax(r Q~K~T)V

复杂度 :O(n2r+nrd)O(n^2 r + nrd)O(n2r+nrd)------当 r<dr < dr<d 时,计算量减少。

6.2 Linformer

6.2.1 核心思想

Linformer(Wang et al., 2020)将 Key 和 Value 投影到低维:

K~=EK,F~=FV\tilde{K} = E K, \quad \tilde{F} = F VK~=EK,F~=FV

其中 E,F∈Rk×nE, F \in \mathbb{R}^{k \times n}E,F∈Rk×n,k≪nk \ll nk≪n。

Linformer 注意力

AttentionLinformer=softmax(QK~Td)F~\text{Attention}_{\text{Linformer}} = \text{softmax}\left(\frac{Q \tilde{K}^T}{\sqrt{d}}\right) \tilde{F}AttentionLinformer=softmax(d QK~T)F~

复杂度 :O(nkd)O(nkd)O(nkd)------线性复杂度!

6.2.2 Linformer 的理论分析

定理 6.1(Linformer 的近似误差):在一定假设下,Linformer 的近似误差满足:

∥Attention−AttentionLinformer∥F≤O(nk)\|\text{Attention} - \text{Attention}_{\text{Linformer}}\|_F \leq O\left(\frac{n}{\sqrt{k}}\right)∥Attention−AttentionLinformer∥F≤O(k n)

推论 :当 k=O(n/ϵ2)k = O(n/\epsilon^2)k=O(n/ϵ2) 时,近似误差不超过 ϵ\epsilonϵ。

6.3 Performer

6.3.1 核心思想

Performer(Choromanski et al., 2021)使用随机特征映射来近似 softmax 核:

softmax(QKT)≈ϕ(Q)ϕ(K)T\text{softmax}(QK^T) \approx \phi(Q) \phi(K)^Tsoftmax(QKT)≈ϕ(Q)ϕ(K)T

其中 ϕ:Rd→Rm\phi: \mathbb{R}^d \to \mathbb{R}^mϕ:Rd→Rm 是随机特征映射。

Performer 注意力

AttentionPerformer=ϕ(Q)(ϕ(K)TV)ϕ(Q)(ϕ(K)T1)\text{Attention}_{\text{Performer}} = \frac{\phi(Q) (\phi(K)^T V)}{\phi(Q) (\phi(K)^T \mathbf{1})}AttentionPerformer=ϕ(Q)(ϕ(K)T1)ϕ(Q)(ϕ(K)TV)

复杂度 :O(nmd)O(nmd)O(nmd)------通过改变计算顺序避免 O(n2)O(n^2)O(n2) 的注意力矩阵。


第三部分:低秩适配(LoRA)与参数高效微调


第七章:LoRA 的数学原理

7.1 LoRA 的核心思想

7.1.1 问题形式化

全参数微调:对预训练模型的所有参数进行微调。

W′=W+ΔWW' = W + \Delta WW′=W+ΔW

其中 ΔW∈Rm×n\Delta W \in \mathbb{R}^{m \times n}ΔW∈Rm×n 是微调更新。

问题 :对于大模型,ΔW\Delta WΔW 的参数量与原始模型相同------存储和计算成本很高。

7.1.2 LoRA 的低秩假设

核心假设 :微调更新 ΔW\Delta WΔW 是低秩的。

LoRA 分解

ΔW=BA,B∈Rm×r,A∈Rr×n\Delta W = BA, \quad B \in \mathbb{R}^{m \times r}, \quad A \in \mathbb{R}^{r \times n}ΔW=BA,B∈Rm×r,A∈Rr×n

其中 r≪min⁡(m,n)r \ll \min(m, n)r≪min(m,n)。

参数量 :r(m+n)r(m+n)r(m+n) vs mnmnmn------当 r=16r = 16r=16,m=n=4096m = n = 4096m=n=4096 时,参数量减少 4096/(2×16)=1284096/(2 \times 16) = 1284096/(2×16)=128 倍。

7.1.3 LoRA 的前向传播

标准前向传播

h=Wxh = Wxh=Wx

LoRA 前向传播

h=Wx+ΔWx=Wx+BAx=Wx+B(Ax)h = Wx + \Delta Wx = Wx + BAx = Wx + B(Ax)h=Wx+ΔWx=Wx+BAx=Wx+B(Ax)

计算步骤

  1. 计算 AxAxAx:O(rn)O(rn)O(rn)
  2. 计算 B(Ax)B(Ax)B(Ax):O(mr)O(mr)O(mr)
  3. 加上 WxWxWx:O(m)O(m)O(m)

总计算量 :O(mn+(m+n)r)O(mn + (m+n)r)O(mn+(m+n)r)------当 rrr 很小时,额外开销可以忽略。

7.2 LoRA 的训练

7.2.1 初始化策略

定理 7.1(LoRA 的初始化)

策略 1(标准初始化)

  • A∼N(0,σ2)A \sim \mathcal{N}(0, \sigma^2)A∼N(0,σ2)
  • B=0B = 0B=0

策略 2(Kaiming 初始化)

  • A∼N(0,2/n)A \sim \mathcal{N}(0, 2/n)A∼N(0,2/n)
  • B=0B = 0B=0

为什么 B=0B = 0B=0? 确保训练开始时 ΔW=BA=0\Delta W = BA = 0ΔW=BA=0------LoRA 不改变预训练模型的行为。

7.2.2 缩放因子

定义 7.1(LoRA 缩放)

h=Wx+αrBAxh = Wx + \frac{\alpha}{r} BAxh=Wx+rαBAx

其中 α\alphaα 是缩放超参数。

定理 7.2(缩放因子的作用) :缩放因子 α/r\alpha/rα/r 确保了 LoRA 更新的梯度量级与全参数微调一致。

证明 :设损失为 L\mathcal{L}L,对 BBB 的梯度为:

∂L∂B=∂L∂h⋅αr(Ax)T\frac{\partial \mathcal{L}}{\partial B} = \frac{\partial \mathcal{L}}{\partial h} \cdot \frac{\alpha}{r} (Ax)^T∂B∂L=∂h∂L⋅rα(Ax)T

缩放因子 α/r\alpha/rα/r 使得梯度量级不随 rrr 变化。□\square□

7.2.3 LoRA 的训练算法

算法 7.1(LoRA 训练)

复制代码
输入:预训练权重 W,训练数据,秩 r,缩放 α
输出:训练好的 A, B

1. 初始化:A ~ N(0, σ²), B = 0
2. 冻结 W,只训练 A 和 B
3. 对每个训练步:
   a. 前向传播:h = Wx + (α/r) BAx
   b. 计算损失
   c. 反向传播:计算 ∂L/∂A 和 ∂L/∂B
   d. 更新:A = A - lr * ∂L/∂A, B = B - lr * ∂L/∂B
4. 合并:W' = W + (α/r) BA

7.3 LoRA 的理论分析

7.3.1 低秩假设的合理性

定理 7.3(微调更新的低秩性) :在一定假设下,预训练模型的微调更新 ΔW\Delta WΔW 的有效秩满足:

reff(ΔW)≤O(dtaskdmodel)⋅min⁡(m,n)r_{\text{eff}}(\Delta W) \leq O\left(\frac{d_{\text{task}}}{d_{\text{model}}}\right) \cdot \min(m, n)reff(ΔW)≤O(dmodeldtask)⋅min(m,n)

其中 dtaskd_{\text{task}}dtask 是任务的内在维度。

直觉:微调只需要调整模型的一小部分"方向"------对应于任务相关的低维子空间。

7.3.2 LoRA 的泛化分析

定理 7.4(LoRA 的泛化界) :设 LoRA 的秩为 rrr,训练样本数为 TTT,则泛化误差满足:

ELtest≤ELtrain+O(r(m+n)log⁡(mn)T)\mathbb{E}\\mathcal{L}_{\\text{test}} \leq \mathbb{E}\\mathcal{L}_{\\text{train}} + O\left(\frac{r(m+n) \log(mn)}{T}\right)ELtest≤ELtrain+O(Tr(m+n)log(mn))

推论 :LoRA 的泛化误差上界与 r(m+n)r(m+n)r(m+n) 成正比------参数量越少,泛化越好。


第八章:LoRA 的变体与扩展

8.1 LoRA 的架构变体

8.1.1 标准 LoRA

ΔW=BA\Delta W = BAΔW=BA

8.1.2 多头 LoRA

将 rrr 个 LoRA 分成 hhh 个头,每个头 r/hr/hr/h 维:

ΔW=B1A1,B2A2,...,BhAh\Delta W = B_1 A_1, B_2 A_2, \\dots, B_h A_hΔW=B1A1,B2A2,...,BhAh

8.1.3 共享 LoRA

多个层共享同一组 AAA 和 BBB:

ΔWl=BlA,l=1,...,L\Delta W_l = B_l A, \quad l = 1, \dots, LΔWl=BlA,l=1,...,L

8.2 LoRA 的应用变体

8.2.1 LoRA 的应用位置

问题:在 Transformer 中,LoRA 应该应用到哪些权重矩阵?

常见选择

位置 权重矩阵 说明
自注意力 WQ,WK,WV,WOW_Q, W_K, W_V, W_OWQ,WK,WV,WO Query, Key, Value, Output
FFN Wup,Wdown,WgateW_{\text{up}}, W_{\text{down}}, W_{\text{gate}}Wup,Wdown,Wgate 上投影、下投影、门控

经验 :WQW_QWQ 和 WVW_VWV 通常最有效。

8.2.2 LoRA+

LoRA+ (Hayou et al., 2024)为 AAA 和 BBB 使用不同的学习率:

A←A−ηA∂L∂A,B←B−ηB∂L∂BA \leftarrow A - \eta_A \frac{\partial \mathcal{L}}{\partial A}, \quad B \leftarrow B - \eta_B \frac{\partial \mathcal{L}}{\partial B}A←A−ηA∂A∂L,B←B−ηB∂B∂L

其中 ηB≫ηA\eta_B \gg \eta_AηB≫ηA(通常 ηB/ηA=16\eta_B / \eta_A = 16ηB/ηA=16)。

定理 8.1(LoRA+ 的最优学习率比):在一定假设下,最优学习率比为:

ηB∗ηA∗=λmax⁡(ATA)λmax⁡(BTB)\frac{\eta_B^*}{\eta_A^*} = \frac{\lambda_{\max}(A^T A)}{\lambda_{\max}(B^T B)}ηA∗ηB∗=λmax(BTB)λmax(ATA)

其中 λmax⁡\lambda_{\max}λmax 是最大特征值。


第九章:QLoRA------量化低秩适配

9.1 QLoRA 的核心思想

9.1.1 动机

问题:LoRA 虽然减少了可训练参数,但预训练权重仍然需要以 FP16 存储------对于 70B 模型需要 140GB 显存。

QLoRA(Dettmers et al., 2023)将预训练权重量化为 4 比特,进一步减少显存。

9.1.2 QLoRA 的架构

QLoRA 的前向传播

h=Dequant(W4bit)⋅x+αrBAxh = \text{Dequant}(W_{\text{4bit}}) \cdot x + \frac{\alpha}{r} BAxh=Dequant(W4bit)⋅x+rαBAx

其中:

  • W4bitW_{\text{4bit}}W4bit:4 比特量化的预训练权重(冻结)
  • B,AB, AB,A:LoRA 参数(可训练,FP16/BF16)

9.1.3 NF4 量化

QLoRA 使用 NF4(4-bit NormalFloat) 量化------专为正态分布设计的 4 比特格式。

NF4 的量化点是标准正态分布的等概率分位数。

9.2 QLoRA 的技术细节

9.2.1 双重量化

问题:量化缩放因子本身也需要存储。

双重量化(Double Quantization):对缩放因子再进行一次量化:

squant=Quant(sFP32)s_{\text{quant}} = \text{Quant}(s_{\text{FP32}})squant=Quant(sFP32)

存储节省:从每 64 个参数 32 bit 缩放因子,减少到每 64 个参数约 4 bit。

9.2.2 分页优化

问题:GPU 显存不足时需要将数据换出到 CPU 内存。

分页优化(Paged Optimizers):使用 NVIDIA 的统一内存管理,在 GPU 显存不足时自动将优化器状态换出到 CPU。

9.3 QLoRA 的理论分析

定理 9.1(QLoRA 的精度损失) :设预训练权重的量化误差为 ϵQ\epsilon_QϵQ,LoRA 的近似误差为 ϵL\epsilon_LϵL,则 QLoRA 的总误差满足:

∥W+ΔW−(Dequant(W4bit)+BA)∥F≤ϵQ+ϵL\|W + \Delta W - (\text{Dequant}(W_{\text{4bit}}) + BA)\|_F \leq \epsilon_Q + \epsilon_L∥W+ΔW−(Dequant(W4bit)+BA)∥F≤ϵQ+ϵL

推论 :QLoRA 的精度损失主要来自量化,LoRA 部分的损失可以忽略(因为 BBB 和 AAA 是以高精度训练的)。


第十章:DoRA、AdaLoRA 与自适应秩选择

10.1 DoRA------权重分解 LoRA

10.1.1 核心思想

DoRA (Weight-Decomposed Low-Rank Adaptation)将权重分解为幅度(magnitude)方向(direction)

W=m⋅V∥V∥cW = m \cdot \frac{V}{\|V\|_c}W=m⋅∥V∥cV

其中 m∈R1×nm \in \mathbb{R}^{1 \times n}m∈R1×n 是幅度,V∈Rm×nV \in \mathbb{R}^{m \times n}V∈Rm×n 是方向矩阵,∥⋅∥c\|\cdot\|_c∥⋅∥c 是列范数。

DoRA 的微调

W′=(m+Δm)⋅V+BA∥V+BA∥cW' = (m + \Delta m) \cdot \frac{V + BA}{\|V + BA\|_c}W′=(m+Δm)⋅∥V+BA∥cV+BA

其中 Δm\Delta mΔm 是幅度更新,BABABA 是方向更新(LoRA)。

10.1.2 DoRA 的理论优势

定理 10.1(DoRA 的表达能力):DoRA 可以表示标准 LoRA 无法表示的某些权重更新模式------特别是仅改变幅度而不改变方向的更新。

证明 :标准 LoRA 的更新 ΔW=BA\Delta W = BAΔW=BA 不能独立控制幅度和方向。DoRA 通过分离幅度和方向,提供了更大的灵活性。□\square□

10.2 AdaLoRA------自适应秩分配

10.2.1 核心思想

AdaLoRA (Zhang et al., 2023)为不同的权重矩阵自适应地分配不同的秩

观察:不同层、不同注意力头对任务的重要性不同------应该为重要的层分配更高的秩。

10.2.2 SVD 参数化

AdaLoRA 将 ΔW\Delta WΔW 参数化为 SVD 形式:

ΔW=PΛQ\Delta W = P \Lambda QΔW=PΛQ

其中 P∈Rm×rP \in \mathbb{R}^{m \times r}P∈Rm×r,Λ∈Rr×r\Lambda \in \mathbb{R}^{r \times r}Λ∈Rr×r(对角),Q∈Rr×nQ \in \mathbb{R}^{r \times n}Q∈Rr×n。

秩的自适应:通过训练过程中逐步将不重要的奇异值设为零,实现秩的自适应调整。

10.2.3 重要性度量

定义 10.1(奇异值重要性)

I(σi)=∣σi∣⋅∥∂L∂σi∥I(\sigma_i) = |\sigma_i| \cdot \left\|\frac{\partial \mathcal{L}}{\partial \sigma_i}\right\|I(σi)=∣σi∣⋅ ∂σi∂L

算法 10.1(AdaLoRA 的秩调整)

复制代码
1. 初始化:所有层的秩为 r_max
2. 训练若干步
3. 计算每个奇异值的重要性
4. 将最不重要的奇异值设为零(降低秩)
5. 重新分配预算到更重要的层
6. 重复 2-5

10.3 秩选择的理论指导

10.3.1 基于奇异值衰减的秩选择

定理 10.2(最优秩的选择) :设权重矩阵 WWW 的奇异值为 σ1,...,σp\sigma_1, \dots, \sigma_pσ1,...,σp。最优秩 r∗r^*r∗ 应满足:

∑i=1r∗σi2∑i=1pσi2≥1−ϵ\frac{\sum_{i=1}^{r^*} \sigma_i^2}{\sum_{i=1}^{p} \sigma_i^2} \geq 1 - \epsilon∑i=1pσi2∑i=1r∗σi2≥1−ϵ

其中 ϵ\epsilonϵ 是允许的信息损失比例(通常 ϵ=0.01\epsilon = 0.01ϵ=0.01 或 0.050.050.05)。

10.3.2 基于任务性能的秩选择

方法:在验证集上搜索最优秩:

r∗=arg⁡min⁡rLval(r)+λ⋅cost(r)r^* = \arg\min_r \mathcal{L}_{\text{val}}(r) + \lambda \cdot \text{cost}(r)r∗=argrminLval(r)+λ⋅cost(r)


第四部分:低秩结构的理论分析


第十一章:内在维度假说与低秩结构

11.1 内在维度假说

11.1.1 定义

定义 11.1(内在维度) :数据或任务的内在维度(intrinsic dimension) 是表示数据/解决任务所需的最小维度。

形式化 :设 M⊂RD\mathcal{M} \subset \mathbb{R}^DM⊂RD 是数据流形,其内在维度 ddd 满足:

M≅Rd\mathcal{M} \cong \mathbb{R}^dM≅Rd

(局部同胚于 ddd 维欧氏空间)

11.1.2 神经网络的内在维度

定理 11.1(神经网络的内在维度) :对于参数量为 NNN 的神经网络,其有效参数维度(内在维度)dintd_{\text{int}}dint 满足:

dint≪Nd_{\text{int}} \ll Ndint≪N

实验支持 :Aghajanyan et al. (2021) 发现,微调大型语言模型只需要 dint≈100−1000d_{\text{int}} \approx 100-1000dint≈100−1000 维------远小于模型的参数量。

11.1.3 低秩与内在维度的关系

定理 11.2(低秩分解与内在维度) :如果权重矩阵的微调更新 ΔW\Delta WΔW 的内在维度为 dintd_{\text{int}}dint,则存在秩 r=O(dint)r = O(d_{\text{int}})r=O(dint) 的分解 ΔW=BA\Delta W = BAΔW=BA 使得:

∥ΔW−BA∥F≤ϵ\|\Delta W - BA\|_F \leq \epsilon∥ΔW−BA∥F≤ϵ

证明 :由 Eckart-Young 定理,ΔW\Delta WΔW 的最佳秩 rrr 近似误差为 ∑i=r+1pσi2\sqrt{\sum_{i=r+1}^{p} \sigma_i^2}∑i=r+1pσi2 。如果 ΔW\Delta WΔW 的有效秩为 dintd_{\text{int}}dint,则当 r≥dintr \geq d_{\text{int}}r≥dint 时,误差很小。□\square□

11.2 低秩结构的普遍性

11.2.1 不同层的低秩程度

观察:不同层的权重矩阵具有不同的低秩程度。

定理 11.3(层间低秩差异) :设 WlW_lWl 是第 lll 层的权重,其有效秩为 reff(Wl)r_{\text{eff}}(W_l)reff(Wl)。则:

  • 浅层 :reffr_{\text{eff}}reff 较大(特征提取需要高秩)
  • 深层 :reffr_{\text{eff}}reff 较小(任务相关特征是低秩的)

11.2.2 不同任务的低秩程度

观察:不同任务对低秩程度的要求不同。

定理 11.4(任务复杂度与秩的关系) :设任务的内在维度为 dtaskd_{\text{task}}dtask,则微调所需的最小秩满足:

rmin⁡=Ω(dtask)r_{\min} = \Omega(d_{\text{task}})rmin=Ω(dtask)

推论:简单任务(如情感分析)可以用更低的秩,复杂任务(如数学推理)需要更高的秩。


第十二章:神经网络的低秩偏置

12.1 训练过程中的低秩演化

12.1.1 奇异值的动态

定理 12.1(训练过程中奇异值的演化) :在梯度下降训练中,权重矩阵 WWW 的奇异值满足:

dσidt=−ησi⋅E∥∂L∂σi∥2\frac{d\sigma_i}{dt} = -\eta \sigma_i \cdot \mathbb{E}\left\\left\\\|\\frac{\\partial \\mathcal{L}}{\\partial \\sigma_i}\\right\\\|\^2\\rightdtdσi=−ησi⋅E ∂σi∂L 2

推论:梯度大的奇异值衰减快,梯度小的奇异值衰减慢。这导致奇异值谱变得"尖锐"------大奇异值保持,小奇异值衰减。

12.1.2 隐式正则化

定理 12.2(梯度下降的低秩偏置):梯度下降训练隐式地偏好低秩解------这是因为低秩解对应的损失函数景观更"平坦",更容易被梯度下降找到。

12.2 过参数化与低秩

12.2.1 过参数化的低秩效应

定理 12.3(过参数化导致低秩):当模型参数量远大于训练样本数时,训练后的权重矩阵倾向于低秩。

证明思路 :过参数化意味着 Hessian 矩阵是低秩的。梯度下降沿着 Hessian 的非零特征值方向更新,导致权重矩阵的有效秩降低。□\square□


第十三章:低秩与泛化理论

13.1 低秩约束的正则化效应

13.1.1 秩约束与泛化

定理 13.1(低秩约束的泛化界) :设模型使用秩 rrr 的低秩分解,训练样本数为 TTT,则泛化误差满足:

ELtest≤ELtrain+O(r(m+n)log⁡(mn)T)\mathbb{E}\\mathcal{L}_{\\text{test}} \leq \mathbb{E}\\mathcal{L}_{\\text{train}} + O\left(\sqrt{\frac{r(m+n) \log(mn)}{T}}\right)ELtest≤ELtrain+O(Tr(m+n)log(mn) )

推论 :秩 rrr 越小,泛化误差上界越小------低秩约束起到正则化作用。

13.1.2 低秩与 PAC-Bayes 界

定理 13.2(低秩模型的 PAC-Bayes 界)

ELtest≤ELtrain+rlog⁡(mn/r)+log⁡(T/δ)2T\mathbb{E}\\mathcal{L}_{\\text{test}} \leq \mathbb{E}\\mathcal{L}_{\\text{train}} + \sqrt{\frac{r \log(mn/r) + \log(T/\delta)}{2T}}ELtest≤ELtrain+2Trlog(mn/r)+log(T/δ)

以概率 1−δ1 - \delta1−δ 成立。

13.2 低秩分解的偏差-方差权衡

13.2.1 偏差增加

定理 13.3(低秩分解的偏差) :使用秩 rrr 的低秩近似,偏差为:

Bias2=∥W−Wr∥F2=∑i=r+1pσi2\text{Bias}^2 = \|W - W_r\|F^2 = \sum{i=r+1}^{p} \sigma_i^2Bias2=∥W−Wr∥F2=i=r+1∑pσi2

13.2.2 方差减少

定理 13.4(低秩分解的方差减少):低秩分解减少了模型的有效参数量,从而减少方差:

Var∝r(m+n)T\text{Var} \propto \frac{r(m+n)}{T}Var∝Tr(m+n)

13.2.3 最优秩

定理 13.5(最优秩的偏差-方差权衡)

r∗=arg⁡min⁡r(∑i=r+1pσi2+λ⋅r(m+n)T)r^* = \arg\min_r \left(\sum_{i=r+1}^{p} \sigma_i^2 + \lambda \cdot \frac{r(m+n)}{T}\right)r∗=argrmin(i=r+1∑pσi2+λ⋅Tr(m+n))

推论 :训练样本越多(TTT 越大),最优秩 r∗r^*r∗ 越大。


第五部分:完整可运行代码实现


第十四章:从零实现 SVD 分解与低秩近似

python 复制代码
"""
SVD 分解与低秩近似的完整实现。
包含:SVD 计算、截断 SVD、误差分析。
"""

import numpy as np
from typing import Tuple


def compute_svd(W: np.ndarray) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
    """计算矩阵的 SVD。

    Args:
        W: 输入矩阵 (m, n)

    Returns:
        U: 左奇异向量 (m, m)
        sigma: 奇异值 (min(m,n),)
        Vt: 右奇异向量的转置 (n, n)
    """
    U, sigma, Vt = np.linalg.svd(W, full_matrices=True)
    return U, sigma, Vt


def truncated_svd(W: np.ndarray, r: int) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
    """截断 SVD(最佳秩 r 近似)。

    Args:
        W: 输入矩阵 (m, n)
        r: 目标秩

    Returns:
        U_r: 左奇异向量 (m, r)
        sigma_r: 奇异值 (r,)
        Vt_r: 右奇异向量的转置 (r, n)
    """
    U, sigma, Vt = np.linalg.svd(W, full_matrices=False)

    # 截断到秩 r
    U_r = U[:, :r]
    sigma_r = sigma[:r]
    Vt_r = Vt[:r, :]

    return U_r, sigma_r, Vt_r


def low_rank_approximate(W: np.ndarray, r: int) -> Tuple[np.ndarray, np.ndarray, float]:
    """低秩近似。

    Args:
        W: 输入矩阵 (m, n)
        r: 目标秩

    Returns:
        W_r: 秩 r 近似矩阵 (m, n)
        factors: (A, B) 使得 W_r = A @ B
        error: 相对误差
    """
    U_r, sigma_r, Vt_r = truncated_svd(W, r)

    # 重构
    W_r = U_r @ np.diag(sigma_r) @ Vt_r

    # 分解为 A @ B
    A = U_r @ np.diag(np.sqrt(sigma_r))
    B = np.diag(np.sqrt(sigma_r)) @ Vt_r

    # 计算误差
    error = np.linalg.norm(W - W_r, 'fro') / np.linalg.norm(W, 'fro')

    return W_r, (A, B), error


def analyze_singular_values(W: np.ndarray) -> dict:
    """分析奇异值分布。

    Args:
        W: 输入矩阵

    Returns:
        info: 奇异值分析信息
    """
    _, sigma, _ = np.linalg.svd(W, full_matrices=False)

    # 基本统计
    total_energy = np.sum(sigma ** 2)
    cumulative_energy = np.cumsum(sigma ** 2) / total_energy

    # 有效秩
    p = sigma / sigma.sum()
    eff_rank = np.exp(-np.sum(p * np.log(np.clip(p, 1e-10, 1.0))))

    # 达到不同能量比例所需的秩
    ranks_for_energy = {}
    for threshold in [0.9, 0.95, 0.99, 0.999]:
        rank = np.searchsorted(cumulative_energy, threshold) + 1
        ranks_for_energy[f"{threshold:.1%}"] = rank

    return {
        "singular_values": sigma,
        "total_energy": total_energy,
        "cumulative_energy": cumulative_energy,
        "effective_rank": eff_rank,
        "ranks_for_energy": ranks_for_energy,
        "max_singular": sigma[0],
        "min_singular": sigma[-1],
        "condition_number": sigma[0] / (sigma[-1] + 1e-10),
    }


def demonstrate_svd():
    """演示 SVD 分解与低秩近似。"""
    np.random.seed(42)

    print("=" * 70)
    print("SVD 分解与低秩近似演示")
    print("=" * 70)

    # 创建测试矩阵
    m, n = 64, 128

    # 构造一个有低秩结构的矩阵
    r_true = 10
    U_true = np.random.randn(m, r_true)
    V_true = np.random.randn(r_true, n)
    W_low_rank = U_true @ V_true

    # 添加噪声
    noise = np.random.randn(m, n) * 0.1
    W = W_low_rank + noise

    print(f"\n  矩阵大小: {m}x{n}")
    print(f"  真实秩: {r_true}")
    print(f"  矩阵 Frobenius 范数: {np.linalg.norm(W, 'fro'):.4f}")

    # 奇异值分析
    print("\n  1. 奇异值分析")
    print("  " + "-" * 40)

    info = analyze_singular_values(W)
    print(f"    最大奇异值: {info['max_singular']:.4f}")
    print(f"    最小奇异值: {info['min_singular']:.6f}")
    print(f"    条件数: {info['condition_number']:.2f}")
    print(f"    有效秩: {info['effective_rank']:.2f}")

    print(f"\n    达到不同能量比例所需的秩:")
    for threshold, rank in info["ranks_for_energy"].items():
        print(f"      {threshold}: 秩 {rank}")

    # 不同秩的近似误差
    print("\n  2. 不同秩的近似误差")
    print("  " + "-" * 40)

    print(f"  {'秩':>6} {'相对误差':>12} {'参数量':>12} {'压缩比':>10}")
    print(f"  {'-'*6} {'-'*12} {'-'*12} {'-'*10}")

    original_params = m * n

    for r in [1, 2, 5, 10, 15, 20, 30, 50]:
        W_r, (A, B), error = low_rank_approximate(W, r)
        params = r * (m + n)
        compression = original_params / params

        print(f"  {r:>6} {error:>12.6f} {params:>12} {compression:>10.2f}x")

    # Eckart-Young 定理验证
    print("\n  3. Eckart-Young 定理验证")
    print("  " + "-" * 40)

    U, sigma, Vt = np.linalg.svd(W, full_matrices=False)

    print(f"  {'秩':>6} {'SVD 误差':>12} {'理论误差':>12} {'匹配':>8}")
    print(f"  {'-'*6} {'-'*12} {'-'*12} {'-'*8}")

    for r in [5, 10, 15, 20]:
        # SVD 近似误差
        W_r, _, error_svd = low_rank_approximate(W, r)

        # 理论误差
        error_theory = np.sqrt(np.sum(sigma[r:] ** 2)) / np.linalg.norm(W, 'fro')

        match = "✓" if abs(error_svd - error_theory) < 1e-10 else "✗"
        print(f"  {r:>6} {error_svd:>12.8f} {error_theory:>12.8f} {match:>8}")


if __name__ == "__main__":
    demonstrate_svd()

第十五章:从零实现权重低秩分解压缩

python 复制代码
"""
权重低秩分解压缩的完整实现。
包含:SVD 分解、数据感知分解、微调。
"""

import numpy as np
from typing import Tuple, List


def svd_compress(W: np.ndarray, rank: int) -> Tuple[np.ndarray, np.ndarray]:
    """SVD 低秩压缩。

    Args:
        W: 权重矩阵 (m, n)
        rank: 目标秩

    Returns:
        A: (m, rank)
        B: (rank, n)
    """
    U, sigma, Vt = np.linalg.svd(W, full_matrices=False)

    A = U[:, :rank] @ np.diag(np.sqrt(sigma[:rank]))
    B = np.diag(np.sqrt(sigma[:rank])) @ Vt[:rank, :]

    return A, B


def data_aware_compress(
    W: np.ndarray,
    X: np.ndarray,
    rank: int,
) -> Tuple[np.ndarray, np.ndarray]:
    """数据感知的低秩压缩。

    优先保留对输入数据影响最大的方向。

    Args:
        W: 权重矩阵 (m, n)
        X: 输入数据 (T, n)
        rank: 目标秩

    Returns:
        A: (m, rank)
        B: (rank, n)
    """
    # 计算输入协方差
    H = X.T @ X / X.shape[0]  # (n, n)

    # 对 H 进行特征分解
    eigenvalues, eigenvectors = np.linalg.eigh(H)

    # H^{1/2}
    H_sqrt = eigenvectors @ np.diag(np.sqrt(np.maximum(eigenvalues, 0))) @ eigenvectors.T
    H_sqrt_inv = eigenvectors @ np.diag(1.0 / np.sqrt(np.maximum(eigenvalues, 1e-8))) @ eigenvectors.T

    # 在 H-加权空间中进行 SVD
    W_weighted = W @ H_sqrt
    A, B_weighted = svd_compress(W_weighted, rank)

    # 逆变换
    B = H_sqrt_inv @ B_weighted.T  # 实际上需要更仔细的处理

    # 简化:直接在加权空间分解后逆变换
    U, sigma, Vt = np.linalg.svd(W_weighted, full_matrices=False)

    A = U[:, :rank] @ np.diag(np.sqrt(sigma[:rank]))
    B_tilde = np.diag(np.sqrt(sigma[:rank])) @ Vt[:rank, :]
    B = H_sqrt_inv @ B_tilde.T
    B = B.T  # 转置回来

    return A, B


def finetune_low_rank(
    W: np.ndarray,
    X: np.ndarray,
    Y: np.ndarray,
    A: np.ndarray,
    B: np.ndarray,
    n_steps: int = 100,
    lr: float = 0.001,
) -> Tuple[np.ndarray, np.ndarray, list]:
    """微调低秩分解。

    Args:
        W: 原始权重(冻结)
        X: 输入数据
        Y: 目标输出
        A: 初始 A 矩阵
        B: 初始 B 矩阵
        n_steps: 微调步数
        lr: 学习率

    Returns:
        A_finetuned: 微调后的 A
        B_finetuned: 微调后的 B
        losses: 损失历史
    """
    A = A.copy()
    B = B.copy()
    losses = []

    for step in range(n_steps):
        # 前向传播
        W_approx = A @ B
        Y_pred = X @ W_approx.T

        # 损失
        loss = np.mean((Y - Y_pred) ** 2)
        losses.append(loss)

        # 反向传播
        grad_Y = 2 / X.shape[0] * (Y_pred - Y)
        grad_W = grad_Y.T @ X  # (m, n)

        # 对 A 和 B 的梯度
        grad_A = grad_W @ B.T  # (m, rank)
        grad_B = A.T @ grad_W  # (rank, n)

        # 更新
        A = A - lr * grad_A
        B = B - lr * grad_B

    return A, B, losses


def demonstrate_weight_compression():
    """演示权重低秩分解压缩。"""
    np.random.seed(42)

    print("=" * 70)
    print("权重低秩分解压缩演示")
    print("=" * 70)

    # 创建测试数据
    m, n = 128, 256
    T = 500

    # 权重矩阵(低秩 + 噪声)
    r_true = 15
    U = np.random.randn(m, r_true) * 0.5
    V = np.random.randn(r_true, n) * 0.5
    W = U @ V + np.random.randn(m, n) * 0.01

    # 输入数据
    X = np.random.randn(T, n) * 0.5
    Y = X @ W.T + np.random.randn(T, m) * 0.001

    # 参考输出
    Y_ref = X @ W.T

    print(f"\n  权重矩阵: {m}x{n}")
    print(f"  真实秩: {r_true}")
    print(f"  训练样本: {T}")

    # 不同方法对比
    print("\n  1. 不同压缩方法对比")
    print("  " + "-" * 40)

    print(f"  {'秩':>6} {'SVD MSE':>12} {'数据感知 MSE':>15} {'微调后 MSE':>12}")
    print(f"  {'-'*6} {'-'*12} {'-'*15} {'-'*12}")

    for rank in [5, 10, 15, 20, 30]:
        # SVD 分解
        A_svd, B_svd = svd_compress(W, rank)
        Y_svd = X @ (A_svd @ B_svd).T
        mse_svd = np.mean((Y_ref - Y_svd) ** 2)

        # 数据感知分解
        A_da, B_da = data_aware_compress(W, X, rank)
        Y_da = X @ (A_da @ B_da).T
        mse_da = np.mean((Y_ref - Y_da) ** 2)

        # 微调
        A_ft, B_ft, _ = finetune_low_rank(W, X, Y, A_svd, B_svd, n_steps=50, lr=0.0001)
        Y_ft = X @ (A_ft @ B_ft).T
        mse_ft = np.mean((Y_ref - Y_ft) ** 2)

        print(f"  {rank:>6} {mse_svd:>12.8f} {mse_da:>15.8f} {mse_ft:>12.8f}")

    # 压缩比分析
    print("\n  2. 压缩比分析")
    print("  " + "-" * 40)

    original_params = m * n
    print(f"  原始参数量: {original_params}")
    print()

    for rank in [5, 10, 15, 20]:
        compressed_params = rank * (m + n)
        ratio = original_params / compressed_params
        A, B = svd_compress(W, rank)
        _, _, error = low_rank_approximate(W, rank)

        print(f"  秩 {rank}: {compressed_params} 参数, "
              f"压缩比 {ratio:.1f}x, 相对误差 {error:.6f}")


if __name__ == "__main__":
    demonstrate_weight_compression()

第十六章:从零实现 LoRA 与 QLoRA

python 复制代码
"""
LoRA 和 QLoRA 的完整实现。
包含:LoRA 层、QLoRA 量化、训练模拟。
"""

import numpy as np
from typing import Tuple, Optional


def uniform_quantize(x: np.ndarray, num_bits: int) -> Tuple[np.ndarray, float, int]:
    """均匀量化。"""
    abs_max = max(np.max(np.abs(x)), 1e-8)
    qmax = 2 ** (num_bits - 1) - 1
    scale = abs_max / qmax
    zero_point = 0
    x_int = np.clip(np.round(x / scale) + zero_point, -qmax, qmax).astype(int)
    x_q = (x_int - zero_point).astype(float) * scale
    return x_q, scale, zero_point


class LoRALayer:
    """LoRA 层。

    实现 h = Wx + (α/r) BAx

    Attributes:
        W: 预训练权重 (m, n),冻结
        A: LoRA 矩阵 A (r, n),可训练
        B: LoRA 矩阵 B (m, r),可训练
        rank: LoRA 秩
        alpha: 缩放因子
    """

    def __init__(
        self,
        W: np.ndarray,
        rank: int = 8,
        alpha: float = 16.0,
        init_scale: float = 0.01,
    ):
        """初始化 LoRA 层。

        Args:
            W: 预训练权重 (m, n)
            rank: LoRA 秩
            alpha: 缩放因子
            init_scale: 初始化尺度
        """
        m, n = W.shape
        self.W = W.copy()  # 冻结
        self.rank = rank
        self.alpha = alpha
        self.scaling = alpha / rank

        # 初始化 A(随机)和 B(零)
        self.A = np.random.randn(rank, n) * init_scale
        self.B = np.zeros((m, rank))

    def forward(self, x: np.ndarray) -> np.ndarray:
        """前向传播。

        Args:
            x: 输入 (..., n)

        Returns:
            h: 输出 (..., m)
        """
        # 基础前向传播
        h_base = x @ self.W.T

        # LoRA 更新
        lora_out = (x @ self.A.T) @ self.B.T  # (..., r) -> (..., m)
        h_lora = self.scaling * lora_out

        return h_base + h_lora

    def backward(
        self,
        x: np.ndarray,
        grad_output: np.ndarray,
    ) -> Tuple[np.ndarray, np.ndarray]:
        """反向传播。

        Args:
            x: 输入 (B, n)
            grad_output: 上游梯度 (B, m)

        Returns:
            grad_A: 对 A 的梯度 (r, n)
            grad_B: 对 B 的梯度 (m, r)
        """
        B_batch = x.shape[0]

        # 对 B 的梯度:∂L/∂B = (∂L/∂h) * scaling * (xA^T)^T
        lora_intermediate = x @ self.A.T  # (B, r)
        grad_B = (grad_output.T @ lora_intermediate) * self.scaling / B_batch

        # 对 A 的梯度:∂L/∂A = (∂L/∂h * B)^T * x
        grad_h_lora = grad_output * self.scaling  # (B, m)
        grad_A = (grad_h_lora @ self.B).T @ x / B_batch

        return grad_A, grad_B

    def update(self, grad_A: np.ndarray, grad_B: np.ndarray, lr: float):
        """更新参数。"""
        self.A = self.A - lr * grad_A
        self.B = self.B - lr * grad_B

    def merge(self) -> np.ndarray:
        """合并 LoRA 到原始权重。"""
        return self.W + self.scaling * self.B @ self.A


class QLoRALayer:
    """QLoRA 层。

    实现 h = Dequant(W_4bit) * x + (α/r) BAx

    Attributes:
        W_quant: 4 比特量化的权重
        scale: 量化缩放因子
        A: LoRA 矩阵 A (r, n)
        B: LoRA 矩阵 B (m, r)
    """

    def __init__(
        self,
        W: np.ndarray,
        rank: int = 8,
        alpha: float = 16.0,
        quant_bits: int = 4,
        init_scale: float = 0.01,
    ):
        """初始化 QLoRA 层。"""
        m, n = W.shape
        self.rank = rank
        self.alpha = alpha
        self.scaling = alpha / rank
        self.quant_bits = quant_bits

        # 量化预训练权重
        self.W_quant, self.scale, self.zero_point = uniform_quantize(W, quant_bits)

        # LoRA 参数
        self.A = np.random.randn(rank, n) * init_scale
        self.B = np.zeros((m, rank))

    def dequantize(self) -> np.ndarray:
        """反量化权重。"""
        return self.W_quant  # 已经在 uniform_quantize 中反量化了

    def forward(self, x: np.ndarray) -> np.ndarray:
        """前向传播。"""
        # 量化权重的前向传播
        W_deq = self.dequantize()
        h_base = x @ W_deq.T

        # LoRA 更新
        lora_out = (x @ self.A.T) @ self.B.T
        h_lora = self.scaling * lora_out

        return h_base + h_lora

    def backward(
        self,
        x: np.ndarray,
        grad_output: np.ndarray,
    ) -> Tuple[np.ndarray, np.ndarray]:
        """反向传播(只对 LoRA 参数)。"""
        B_batch = x.shape[0]

        lora_intermediate = x @ self.A.T
        grad_B = (grad_output.T @ lora_intermediate) * self.scaling / B_batch

        grad_h_lora = grad_output * self.scaling
        grad_A = (grad_h_lora @ self.B).T @ x / B_batch

        return grad_A, grad_B

    def update(self, grad_A: np.ndarray, grad_B: np.ndarray, lr: float):
        """更新 LoRA 参数。"""
        self.A = self.A - lr * grad_A
        self.B = self.B - lr * grad_B


def demonstrate_lora():
    """演示 LoRA。"""
    np.random.seed(42)

    print("=" * 70)
    print("LoRA 与 QLoRA 演示")
    print("=" * 70)

    # 设置
    m, n = 128, 256
    T = 500
    rank = 8

    # 预训练权重
    W = np.random.randn(m, n) * 0.02

    # 训练数据
    X = np.random.randn(T, n) * 0.5
    Y = X @ W.T + np.random.randn(T, m) * 0.001

    print(f"\n  权重矩阵: {m}x{n}")
    print(f"  LoRA 秩: {rank}")
    print(f"  训练样本: {T}")

    # LoRA 训练
    print("\n  1. LoRA 训练")
    print("  " + "-" * 40)

    lora = LoRALayer(W, rank=rank, alpha=16.0)
    lr = 0.001
    n_steps = 200

    losses = []
    for step in range(n_steps):
        # 前向传播
        Y_pred = lora.forward(X)
        loss = np.mean((Y_pred - Y) ** 2)
        losses.append(loss)

        # 反向传播
        grad_output = 2 / T * (Y_pred - Y)
        grad_A, grad_B = lora.backward(X, grad_output)

        # 更新
        lora.update(grad_A, grad_B, lr)

        if (step + 1) % 50 == 0:
            print(f"    Step {step + 1}: loss = {loss:.8f}")

    # 最终精度
    Y_final = lora.forward(X)
    mse_final = np.mean((Y_final - Y) ** 2)
    print(f"\n    最终 MSE: {mse_final:.8f}")

    # 合并后的权重误差
    W_merged = lora.merge()
    weight_error = np.linalg.norm(W - W_merged, 'fro') / np.linalg.norm(W, 'fro')
    print(f"    权重相对误差: {weight_error:.6f}")

    # 参数量分析
    lora_params = rank * (m + n)
    original_params = m * n
    print(f"\n    原始参数量: {original_params}")
    print(f"    LoRA 参数量: {lora_params}")
    print(f"    压缩比: {original_params / lora_params:.1f}x")

    # QLoRA 对比
    print("\n  2. QLoRA 对比")
    print("  " + "-" * 40)

    for bits in [4, 3, 2]:
        qlora = QLoRALayer(W, rank=rank, alpha=16.0, quant_bits=bits)

        # 微调 LoRA 部分
        for step in range(200):
            Y_pred = qlora.forward(X)
            grad_output = 2 / T * (Y_pred - Y)
            grad_A, grad_B = qlora.backward(X, grad_output)
            qlora.update(grad_A, grad_B, lr)

        # 评估
        Y_qlora = qlora.forward(X)
        mse_qlora = np.mean((Y_qlora - Y) ** 2)

        # 量化误差
        W_deq = qlora.dequantize()
        quant_error = np.linalg.norm(W - W_deq, 'fro') / np.linalg.norm(W, 'fro')

        print(f"    {bits}-bit QLoRA: MSE = {mse_qlora:.8f}, 量化误差 = {quant_error:.6f}")

    # 不同秩的对比
    print("\n  3. 不同秩的 LoRA 对比")
    print("  " + "-" * 40)

    print(f"  {'秩':>6} {'参数量':>10} {'压缩比':>10} {'最终 MSE':>12}")
    print(f"  {'-'*6} {'-'*10} {'-'*10} {'-'*12}")

    for r in [1, 2, 4, 8, 16, 32]:
        lora_r = LoRALayer(W, rank=r, alpha=16.0)

        for step in range(200):
            Y_pred = lora_r.forward(X)
            grad_output = 2 / T * (Y_pred - Y)
            grad_A, grad_B = lora_r.backward(X, grad_output)
            lora_r.update(grad_A, grad_B, lr)

        Y_r = lora_r.forward(X)
        mse_r = np.mean((Y_r - Y) ** 2)
        params_r = r * (m + n)
        ratio_r = original_params / params_r

        print(f"  {r:>6} {params_r:>10} {ratio_r:>10.1f}x {mse_r:>12.8f}")


if __name__ == "__main__":
    demonstrate_lora()

第十七章:完整低秩压缩 Pipeline 与精度对比

python 复制代码
"""
完整的低秩压缩 Pipeline。
对比 SVD 分解、LoRA、QLoRA 等方法。
"""

import numpy as np
from typing import Tuple


def run_full_comparison():
    """运行完整的低秩方法对比。"""
    np.random.seed(42)

    print("=" * 70)
    print("低秩压缩方法综合对比")
    print("=" * 70)

    # 设置
    m, n = 128, 256
    T = 1000

    # 权重矩阵
    r_true = 20
    U = np.random.randn(m, r_true) * 0.5
    V = np.random.randn(r_true, n) * 0.5
    W = U @ V + np.random.randn(m, n) * 0.01

    # 数据
    X = np.random.randn(T, n) * 0.5
    Y = X @ W.T + np.random.randn(T, m) * 0.001
    Y_ref = X @ W.T

    print(f"\n  权重: {m}x{n}, 真实秩: {r_true}")
    print(f"  训练样本: {T}")
    print(f"  原始参数量: {m * n}")

    # 方法定义
    def svd_compress(W, rank):
        U, s, Vt = np.linalg.svd(W, full_matrices=False)
        A = U[:, :rank] @ np.diag(np.sqrt(s[:rank]))
        B = np.diag(np.sqrt(s[:rank])) @ Vt[:rank, :]
        return A, B

    def train_lora(W, X, Y, rank, n_steps=300, lr=0.001):
        m, n = W.shape
        A = np.random.randn(rank, n) * 0.01
        B = np.zeros((m, rank))
        scaling = 16.0 / rank

        for step in range(n_steps):
            W_approx = W + scaling * B @ A
            Y_pred = X @ W_approx.T
            grad_output = 2 / X.shape[0] * (Y_pred - Y)

            grad_B = scaling * (grad_output.T @ (X @ A.T)) / X.shape[0]
            grad_A = scaling * ((grad_output @ B).T @ X) / X.shape[0]

            A = A - lr * grad_A
            B = B - lr * grad_B

        return A, B, scaling

    # 测试不同秩
    print(f"\n  {'方法':>15} {'秩':>6} {'MSE':>15} {'参数量':>10} {'压缩比':>10}")
    print(f"  {'-'*15} {'-'*6} {'-'*15} {'-'*10} {'-'*10}")

    for rank in [5, 10, 15, 20, 30, 50]:
        # SVD 分解
        A_svd, B_svd = svd_compress(W, rank)
        Y_svd = X @ (A_svd @ B_svd).T
        mse_svd = np.mean((Y_ref - Y_svd) ** 2)
        params_svd = rank * (m + n)
        ratio_svd = (m * n) / params_svd

        print(f"  {'SVD':>15} {rank:>6} {mse_svd:>15.10f} {params_svd:>10} {ratio_svd:>10.1f}x")

        # LoRA
        A_lora, B_lora, scaling = train_lora(W, X, Y, rank, n_steps=300, lr=0.001)
        W_lora = W + scaling * B_lora @ A_lora
        Y_lora = X @ W_lora.T
        mse_lora = np.mean((Y_ref - Y_lora) ** 2)
        params_lora = rank * (m + n)

        print(f"  {'LoRA':>15} {rank:>6} {mse_lora:>15.10f} {params_lora:>10} {'':>10}")

    # 综合分析
    print(f"\n  综合分析(秩 = 15):")
    print("  " + "-" * 40)

    rank = 15

    # SVD
    A_svd, B_svd = svd_compress(W, rank)
    Y_svd = X @ (A_svd @ B_svd).T
    mse_svd = np.mean((Y_ref - Y_svd) ** 2)

    # SVD + 微调
    A_ft = A_svd.copy()
    B_ft = B_svd.copy()
    for step in range(100):
        Y_pred = X @ (A_ft @ B_ft).T
        grad = 2 / T * (Y_pred - Y).T @ X
        A_ft = A_ft - 0.0001 * grad @ B_ft.T
        B_ft = B_ft - 0.0001 * A_ft.T @ grad
    Y_ft = X @ (A_ft @ B_ft).T
    mse_ft = np.mean((Y_ref - Y_ft) ** 2)

    # LoRA
    A_lora, B_lora, scaling = train_lora(W, X, Y, rank)
    W_lora = W + scaling * B_lora @ A_lora
    Y_lora = X @ W_lora.T
    mse_lora = np.mean((Y_ref - Y_lora) ** 2)

    print(f"    SVD 分解:     MSE = {mse_svd:.10f}")
    print(f"    SVD + 微调:   MSE = {mse_ft:.10f}")
    print(f"    LoRA:         MSE = {mse_lora:.10f}")


if __name__ == "__main__":
    run_full_comparison()

附录:关键公式汇总

A.1 SVD 与低秩近似

公式 表达式
SVD W=UΣVTW = U \Sigma V^TW=UΣVT
最佳秩 rrr 近似 Wr=UrΣrVrTW_r = U_r \Sigma_r V_r^TWr=UrΣrVrT
Frobenius 误差 ∣W−Wr∣F=∑i=r+1pσi2|W - W_r|F = \sqrt{\sum{i=r+1}^{p} \sigma_i^2}∣W−Wr∣F=∑i=r+1pσi2
谱范数误差 ∣W−Wr∣2=σr+1|W - W_r|2 = \sigma{r+1}∣W−Wr∣2=σr+1
有效秩 reff=exp⁡(−∑ipilog⁡pi)r_{\text{eff}} = \exp(-\sum_i p_i \log p_i)reff=exp(−∑ipilogpi)

A.2 低秩分解

方法 分解形式 参数量
SVD W=UrΣrVrTW = U_r \Sigma_r V_r^TW=UrΣrVrT r(m+n)+rr(m+n) + rr(m+n)+r
非对称 W≈ABW \approx ABW≈AB r(m+n)r(m+n)r(m+n)
CP 分解 T≈∑rar∘br∘cr\mathcal{T} \approx \sum_r \mathbf{a}_r \circ \mathbf{b}_r \circ \mathbf{c}_rT≈∑rar∘br∘cr R(m+n+p)R(m+n+p)R(m+n+p)
Tucker T≈G×1U×2V×3W\mathcal{T} \approx \mathcal{G} \times_1 U \times_2 V \times_3 WT≈G×1U×2V×3W R1R2R3+mR1+nR2+pR3R_1 R_2 R_3 + mR_1 + nR_2 + pR_3R1R2R3+mR1+nR2+pR3

A.3 LoRA

公式 表达式
前向传播 h=Wx+αrBAxh = Wx + \frac{\alpha}{r} BAxh=Wx+rαBAx
参数量 r(m+n)r(m+n)r(m+n)
初始化 A∼N(0,σ2)A \sim \mathcal{N}(0, \sigma^2)A∼N(0,σ2), B=0B = 0B=0
缩放 scaling=α/r\text{scaling} = \alpha / rscaling=α/r
合并 W′=W+αrBAW' = W + \frac{\alpha}{r} BAW′=W+rαBA

A.4 QLoRA

公式 表达式
前向传播 h=Dequant(W4bit)⋅x+αrBAxh = \text{Dequant}(W_{4\text{bit}}) \cdot x + \frac{\alpha}{r} BAxh=Dequant(W4bit)⋅x+rαBAx
量化 W4bit=NF4-Quant(W)W_{4\text{bit}} = \text{NF4-Quant}(W)W4bit=NF4-Quant(W)
双重量化 squant=Quant(sFP32)s_{\text{quant}} = \text{Quant}(s_{\text{FP32}})squant=Quant(sFP32)

参考文献

  1. Eckart, C., & Young, G. (1936). The approximation of one matrix by another of lower rank. Psychometrika.
  2. Mirsky, L. (1960). Symmetric gauge functions and unitarily invariant norms. The Quarterly Journal of Mathematics.
  3. Hu, E., et al. (2022). LoRA: Low-rank adaptation of large language models. ICLR.
  4. Dettmers, T., et al. (2023). QLoRA: Efficient finetuning of quantized language models. NeurIPS.
  5. Liu, H., et al. (2024). DoRA: Weight-decomposed low-rank adaptation. ICML.
  6. Zhang, Q., et al. (2023). AdaLoRA: Adaptive budget allocation for parameter-efficient fine-tuning. ICLR.
  7. Hayou, S., et al. (2024). LoRA+: Efficient low rank adaptation of large models. ICML.
  8. Wang, S., et al. (2020). Linformer: Self-attention with linear complexity. arXiv.
  9. Choromanski, K., et al. (2021). Rethinking attention with performers. ICLR.
  10. Aghajanyan, A., et al. (2021). Intrinsic dimensionality explains the effectiveness of language model fine-tuning. ACL.
  11. Kolda, T., & Bader, B. (2009). Tensor decompositions and applications. SIAM Review.
  12. Marchenko, V., & Pastur, L. (1967). Distribution of eigenvalues for some sets of random matrices. Mathematics of the USSR-Sbornik.
相关推荐
高洁012 小时前
设备故障?数字孪生提前预警
深度学习·机器学习·数据挖掘·transformer·知识图谱
weixin_468466852 小时前
机器学习与深度学习新手区分指南
人工智能·python·深度学习·机器学习·计算机视觉·ai·机器视觉
AI算法沐枫2 小时前
基于YOLO26深度学习的【果园荔枝检测与计数】系统设计与实现【python源码+Pyqt5界面+数据集+训练代码】
开发语言·人工智能·python·深度学习·qt·学习·机器学习
逻辑君2 小时前
Foresight研究报告【20260010】
人工智能·算法·机器学习
追光者♂2 小时前
【测评系列3】CSDN AI数字营销实测体验官:测试 开源项目——Superpowers 游戏引擎从零开发实战指南
人工智能·深度学习·机器学习·typescript·开源·游戏引擎·superpowers
hunteritself2 小时前
智博会上的国产芯:重新定义 Token 价值链路
人工智能·chrome·深度学习·机器学习·信息可视化
m沐沐2 小时前
【机器学习】聚类算法-K-means聚类
人工智能·python·算法·机器学习·pycharm·kmeans·聚类
水木流年追梦2 小时前
大模型入门-大模型优化方法3
人工智能·分布式·python·深度学习·机器学习
2601_957888563 小时前
短视频矩阵获客系统的设计与实践:提升企业数字营销效率的路径
大数据·人工智能·矩阵·企业增长