解决超定系统最小二乘法的最快方法

时间:2017-08-06 14:20:02

标签: python numpy scipy linear-algebra

我有一个大小为m * n(m级为~100K且n~500)的矩阵A和一个向量b。此外,我的矩阵病态和缺乏秩。现在我想找出Ax = b的最小二乘解,为此我比较了一些方法:

  • scipy.linalg.lstsq(时间/残差):14s,626.982
  • scipy.sparse.linalg.lsmr(时间/残差):4.5s,626.982(相同准确度)

现在我已经观察到,当我没有排序缺陷的情况形成正规方程并用cholesky分解求解它是解决我问题的最快方法。所以我的问题是如果我对最小范数解决方案不感兴趣,那么当A ^ TA是单数时,有一种方法可以得到(A ^ TAx = b)的解(任何)。我已经尝试过scipy.linalg.solve,但它给出了奇异矩阵的LinAlgError。此外,我想知道A是否是m>> n,错误的,可能不是完整的col-rank,那么在时间,剩余准确度(或任何其他度量)方面应该使用哪种方法。非常感谢任何想法和帮助。谢谢!

1 个答案:

答案 0 :(得分:3)

我说"正确"解决这个问题的方法是使用SVD,查看您的奇异值谱,并计算出您想要保留多少个奇异值,即计算出A^T xb的接近程度。 }。这些方面的东西:

def svd_solve(a, b):
    [U, s, Vt] = la.svd(a, full_matrices=False)
    r = max(np.where(s >= 1e-12)[0])
    temp = np.dot(U[:, :r].T, b) / s[:r]
    return np.dot(Vt[:r, :].T, temp)

但是,对于大小为(100000, 500)的矩阵,这只会太慢。我建议你自己实现最小二乘法,并加入少量正则化以避免矩阵问题变得奇异。

def naive_solve(a, b, lamda):
    return la.solve(np.dot(a.T, a) + lamda * np.identity(a.shape[1]),
                    np.dot(a.T, b))

def pos_solve(a, b, lamda):
    return la.solve(np.dot(a.T, a) + lamda * np.identity(a.shape[1]),
                    np.dot(a.T, b), assume_a='pos')

这是我工作站的时间分析*:

>>> %timeit la.lstsq(a, b)
1.84 s ± 39.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
>>> %timeit naive_solve(a, b, 1e-25)
140 ms ± 4.15 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
>>> %timeit pos_solve(a, b, 1e-25)
135 ms ± 768 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

* 我似乎在我的机器上似乎没有scipy.sparse.linalg.lsmr,所以我无法与之进行比较。

这里似乎没什么用,但我在其他地方看到过添加assume_a='pos'标志实际上可以带来很多好处。你当然可以在这里做到这一点,因为A^T A保证是正半正定的,而lamda使它成为正定的。您可能需要稍微使用lamda来使您的错误充分低。

就错误而言:

>>> xhat_lstsq = la.lstsq(a, b)[0]
>>> la.norm(np.dot(a, xhat_lstsq) - b)
1.4628232073579952e-13
>>> xhat_naive = naive_solve(a, b, 1e-25)
>>> la.norm(np.dot(a, xhat_naive) - b)
7.474566255470176e-13
>>> xhat_pos = pos_solve(a, b, 1e-25)
>>> la.norm(np.dot(a, xhat_pos) - b)
7.476075564322223e-13

PS:我自己生成了ab这样的人:

s = np.logspace(1, -20, 500)
u = np.random.randn(100000, 500)
u /= la.norm(u, axis=0)[np.newaxis, :]
a = np.dot(u, np.diag(s))
x = np.random.randn(500)
b = np.dot(a, x)

我的a并非完全单数,但接近单数。

对评论的回应

我想你要做的是在一些线性等式约束下找到一个可行点。这里的麻烦在于你不知道哪些约束很重要。 A的100,000行中的每一行都给你一个新的约束,其中最多500个,但可能少得多(因为欠定),实际上很重要。 SVD为您提供了一种确定哪些尺寸很重要的方法。我不知道另一种方法:你可能会在凸优化或线性规划文献中找到一些东西。如果您事先知道A的等级为r,那么您可以尝试仅查找第一个r奇异值和相应的向量,这可能会节省r << n的时间。

关于你的另一个问题,最小的规范解决方案不是最好的&#34;甚至是&#34;纠正&#34;解。由于您的系统不确定,您需要引入一些额外的约束或假设,这将有助于您找到一个独特的解决方案。最小范数约束就是这样。最小规范解决方案通常被认为是好的&#34;,因为如果x是您尝试设计的某些物理信号,那么具有较低范数的x通常对应于具有较低能量的物理信号,然后转化为成本节约等。或者,如果x是您尝试估算的某个系统的参数,那么选择最小规范解决方案意味着您是假设系统在某种程度上是有效的,并且只使用产生结果所需的最小能量b。希望一切都有意义。