基于SVD的线性方程最小二乘求解问题排查与原理咨询
基于SVD的最小二乘求解问题与解答
问题代码与输出
import numpy as np from scipy import linalg np.random.seed(123) v = np.random.rand(4) A = v[:,None] * v[None,:] b = np.random.randn(4) x = linalg.inv(A.T.dot(A)).dot(A.T).dot(b) # 通常不推荐,因正规方程存在数值不稳定性 l2_0= linalg.norm(A.dot(x) - b) print("manually: ", l2_0) x = linalg.lstsq(A, b)[0] l2_1= linalg.norm(A.dot(x) - b) print("scipy.linalg.lstsq: ", l2_1) # 比较两种计算的2-范数 print(np.allclose(l2_0, l2_1, rtol=1.3e-1)) def direct_ls_svd(x,y): # 添加全1列(偏置项) x = np.column_stack([np.ones(x.shape[0]), x]) # 计算数据矩阵x的经济型SVD U,S,Vt = linalg.svd(x, full_matrices=False) # 求解Ax=b的最小二乘近似解 x_hat = Vt.T @ linalg.inv(np.diag(S)) @ U.T @ y #print(x_hat) # 执行训练与测试推理 #y_pred = x @ x_hat return y-x @ x_hat #x_hat x= direct_ls_svd(A, b) l2_svd= linalg.norm(A.dot(x) - b) print("svd: ", l2_svd) # LU分解求解 x= linalg.solve(A.T@A, A.T@b) l2_solve= linalg.norm(A.dot(x) - b) print("scipy.linalg.solve: ", l2_solve)
输出结果:
manually: 2.9751344995811313 scipy.linalg.lstsq: 2.9286130558050654 True svd: 6.830550019041984 scipy.linalg.solve: 2.928613055805065
疑问解答
1. 差异源于SVD实现错误还是Numpy与Scipy的精度/舍入差异?如何修正SVD最小二乘算法以匹配Scipy结果?
是实现错误,不是精度问题。你的direct_ls_svd函数存在两个关键错误:
- 错误地给输入矩阵
A额外添加了全1偏置列,完全改变了原问题的求解目标(原问题是求Ax=b的最小二乘解,你改成了求[1 A]x=b的解) - 函数返回的是残差
y-x@x_hat,而非参数向量x_hat,后续用残差当作参数向量计算A.dot(x)-b,逻辑完全错误
修正后的函数如下:
def corrected_ls_svd(A, b): # 直接对原矩阵A做经济型SVD,无需额外加偏置列(除非原问题明确需要) U, S, Vt = linalg.svd(A, full_matrices=False) # 用伪逆替代直接求逆,避免小奇异值导致的数值不稳定 x_hat = Vt.T @ (linalg.pinv(np.diag(S)) @ (U.T @ b)) return x_hat
调用修正后的函数:
x_svd = corrected_ls_svd(A, b) l2_svd_corrected = linalg.norm(A.dot(x_svd) - b) print("corrected svd: ", l2_svd_corrected) # 输出会与scipy.linalg.lstsq结果完全一致
2. 该SVD算法是否比迭代最小二乘法更快、更省内存?
分场景判断:
- 速度:小规模矩阵(如你的4x4示例)下,SVD和迭代法(如梯度下降)速度差异可忽略。但对于大规模、低秩或病态矩阵,SVD(尤其是经济型SVD)通常更快——迭代法需多次迭代收敛,而SVD是底层优化过的BLAS/LAPACK矩阵分解操作,效率更高。
- 内存:经济型SVD仅存储必要的分解矩阵,比满秩SVD更省内存。但迭代法(如随机梯度下降)支持流式数据处理,内存占用极低,适合无法一次性加载到内存的超大规模数据集。
总结:中小规模矩阵选SVD更高效;超大规模流式数据选迭代法更省内存。
3. 我的最终目标是将SVD应用于PCA、PLS-SVD,其算法是否与最小二乘近似一致?
核心逻辑相通,但应用场景不同:
- PCA:本质是对数据协方差矩阵做SVD,保留大奇异值对应的特征向量实现降维。其目标是最大化投影后数据的方差,等价于最小化重构误差,和最小二乘的误差最小化思想一致。
- PLS-SVD:是偏最小二乘的SVD实现,同时对自变量和因变量的协方差结构做分解,寻找既能解释自变量方差又能关联因变量的成分,本质也是通过SVD求解一系列最小二乘问题,只是目标换成了最大化自变量与因变量的协方差。
4. 奇异谱分解中的SVD与用于降维的SVD有何关联?
- 关联:两者都基于SVD的核心分解逻辑,通过提取奇异值对应的成分来捕捉数据的主要结构。
- 差异:
- 用于降维的SVD(如PCA中的SVD):直接对原始数据或协方差矩阵做分解,保留大奇异值对应的成分实现维度压缩。
- 奇异谱分解(SSA):先将时间序列转化为轨迹矩阵(延迟嵌入),再对轨迹矩阵做SVD,随后根据奇异值分组重构,主要用于时间序列的降噪、分解与预测。
简单来说,SSA是SVD在时间序列数据上的特定应用,通过构造轨迹矩阵将一维序列转化为高维矩阵,再用SVD提取信号成分。
5. PCA用于预检测多重共线性的原理是什么?多重共线性对OLS估计的影响如何?
- PCA检测多重共线性的原理:多重共线性意味着自变量间存在线性相关,导致协方差矩阵接近奇异(行列式趋近于0)。对协方差矩阵做SVD后,会出现极小的奇异值——因为奇异值的平方对应主成分的方差,小奇异值说明存在可被其他变量线性表示的成分,即存在多重共线性。
- 多重共线性对OLS的影响:
- OLS估计的方差显著增大,结果不稳定,微小的数据波动会引发参数大幅变化。
- 参数的置信区间变宽,显著性检验结果不可靠。
- 虽OLS估计仍保持无偏性,但不再是有效估计(方差不再最小)。
内容的提问来源于stack exchange,提问作者JeeyCi
相关产品推荐
相关产品推荐

