为何SciPy的linalg.svd与linalg.lstsq返回的奇异值不匹配?
问题:lstsq与svd返回的奇异值不一致的原因
SciPy官方文档显示lstsq会返回观测矩阵X的奇异值,但使用scipy.linalg.svd直接计算时,得到的结果却和它不一致——二者趋势一致,但极值存在差异,这会影响条件数的估计,这是为什么?
复现代码
import numpy as np from scipy.linalg import svd, lstsq import matplotlib.pyplot as plt # 生成观测矩阵X X = np.arange(100*50, dtype=float).reshape(100,50) X = np.sin(X) + np.tan(X) + np.cos(X) X += np.random.normal(0,3, size=(100,50)) # 生成目标变量Y Y = np.sin(X) # 用lstsq获取奇异值 W, res, rank, s = lstsq(X, Y, cond=0) # cond=0 关闭奇异值截断 _, S, _ = svd(X.T @ X) # 绘图对比 plt.semilogy(S, label='from svd') plt.semilogy(s, label='from lstsq') plt.legend()

原因分析
核心问题出在对svd的使用逻辑上:
lstsq返回的s是观测矩阵X本身的奇异值,即对X直接做SVD分解得到的结果。- 原代码中
svd(X.T @ X)计算的是X的Gram矩阵(XᵀX)的奇异值,而Gram矩阵的奇异值实际上是X的奇异值的平方,这就导致了两者数值上的差异。
另外补充:即使修正为直接对X做SVD,由于lstsq内部调用的LAPACK接口(如_gelsd或_gelsy)与直接调用svd的接口(如_gesvd)存在细微的数值计算策略差异,可能会有极微小的浮点精度误差,但整体数值会完全对齐,不会影响条件数的估计。
修正后的代码
将svd(X.T @ X)改为直接对X做SVD并只获取奇异值:
import numpy as np from scipy.linalg import svd, lstsq import matplotlib.pyplot as plt # 生成观测矩阵X X = np.arange(100*50, dtype=float).reshape(100,50) X = np.sin(X) + np.tan(X) + np.cos(X) X += np.random.normal(0,3, size=(100,50)) # 生成目标变量Y Y = np.sin(X) # 用lstsq获取X的奇异值 W, res, rank, s_lstsq = lstsq(X, Y, cond=0) # 直接对X做SVD获取奇异值 s_svd = svd(X, compute_uv=False) # 绘图对比 plt.semilogy(s_svd, label='来自svd') plt.semilogy(s_lstsq, label='来自lstsq') plt.legend() plt.show()
修正后,两者的奇异值曲线会完全重合(或仅有可忽略的浮点精度差异),条件数的计算结果也会一致。
内容的提问来源于stack exchange,提问作者arash
相关产品推荐
相关产品推荐

