更有效的方法来反转矩阵,知道它是对称的和正半正定的

时间:2016-11-20 10:37:38

标签: python numpy scipy linear-algebra

我在python中用numpy反转协方差矩阵。协方差矩阵是对称的,半正定的。

我想知道是否存在针对对称正半定矩阵优化的算法,比numpy.linalg.inv()更快(当然如果可以从python中轻松获得它的实现!)。我无法找到numpy.linalg中的内容或搜索网页。

修改

正如@yixuan所观察到的,正半正定矩阵通常不是严格可逆的。我检查过,在我的情况下,我只是得到了正定矩阵,所以我接受了一个有正确定义的答案。无论如何,在LAPACK低级例程中,我找到了DSY*例程,这些例程仅针对simmetric / hermitian矩阵进行了优化,虽然scipy中似乎缺少它们(可能只是安装版本的问题)

4 个答案:

答案 0 :(得分:3)

据我所知,对称矩阵没有标准的矩阵求逆函数。一般来说,你需要更多的稀疏性约束来为你的求解器提供良好的加速。但是,如果你看一下scipy.linalg,你会发现有一些特征值例程针对Hermitian(对称)矩阵进行了优化。

例如,当我生成一个随机的200x200密集矩阵并求解特征值时,我得到:

from scipy.linalg import inv,pinvh,eig,eigh
B = np.rand(200,200)
B = B+B.T
%timeit inv(B)
1000 loops, best of 3: 915 µs per loop

%timeit pinvh(B)
100 loops, best of 3: 6.93 ms per loop

所以反过来没有优势,但是:

%timeit eig(B)
10 loops, best of 3: 39.1 ms per loop

%timeit eigh(B)
100 loops, best of 3: 4.9 ms per loop

特征值的8倍加速。

如果你的矩阵很稀疏,你应该看看scipy.sparse.linalg有一些解算器,其中一些(比如bicg和cg)需要Hermitian矩阵,所以可能更快。但是,如果您的矩阵是稀疏的,仅解决特定的解向量b并且实际上可能不会更快,这取决于矩阵结构,这是唯一合理的。你真的必须对它进行基准测试才能找到答案。

我问了一个关于C++ solvers的类似问题,并最终发现它确实依赖于应用程序,你必须选择最适合你问题的求解器。

答案 1 :(得分:2)

首先,您需要确保协方差矩阵正定(pd)而非 - 无限,否则矩阵不可逆。

没有p.d.假设,矩阵求逆通常由LU分解完成,而对于p.d.在矩阵中,可以使用Cholesky分解,这通常可以降低计算成本。

在Scipy中,MinMaxScaler()函数有一个参数linalg.solve(),假设矩阵是p.d ..下面是一个简单的例子:

sym_pos

在我的机器上,import numpy as np from scipy import linalg import time def pd_inv(a): n = a.shape[0] I = np.identity(n) return linalg.solve(a, I, sym_pos = True, overwrite_b = True) n = 50 A = np.random.rand(n, n) B = A.dot(A.T) start = time.clock() for i in xrange(0, 10000): res = linalg.inv(B) end = time.clock() print end - start ## 1.324752 start = time.clock() for i in xrange(0, 10000): res = pd_inv(B) end = time.clock() print end - start ## 1.109778 对于小矩阵(~100x100)有一些优势。对于大型矩阵,几乎没有任何差别,有时pd_inv()甚至更慢。

答案 2 :(得分:2)

API尚不存在,但您可以使用低级LAPACK ?POTRI例程系列。

sp.linalg.lapack.dpotri的文档字符串如下

Docstring:     
inv_a,info = dpotri(c,[lower,overwrite_c])

Wrapper for ``dpotri``.

Parameters
----------
c : input rank-2 array('d') with bounds (n,n)

Other Parameters
----------------
overwrite_c : input int, optional
    Default: 0
lower : input int, optional
    Default: 0

Returns
-------
inv_a : rank-2 array('d') with bounds (n,n) and c storage
info : int
Call signature: sp.linalg.lapack.dpotri(*args, **kwargs)

最重要的是info输出。如果它为零,则意味着它成功地解决了等式而不管正确性。因为这是低级别调用,所以不执行其他检查。

>>>> M = np.random.rand(10,10)
>>>> M = M + M.T
>>>> # Make it pos def
>>>> M += (1.5*np.abs(np.min(np.linalg.eigvals(M))) + 1) * np.eye(10)
>>>> zz , _ = sp.linalg.lapack.dpotrf(M, False, False)
>>>> inv_M , info = sp.linalg.lapack.dpotri(zz)
>>>> # lapack only returns the upper or lower triangular part 
>>>> inv_M = np.triu(inv_M) + np.triu(inv_M, k=1).T

另外,如果你比较速度

>>>> %timeit sp.linalg.lapack.dpotrf(M)
The slowest run took 17.86 times longer than the fastest. This could mean that an intermediate result is being cached.
1000000 loops, best of 3: 1.15 µs per loop

>>>> %timeit sp.linalg.lapack.dpotri(M)
The slowest run took 24.09 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 2.08 µs per loop

>>>> III = np.eye(10)

>>>> %timeit sp.linalg.solve(M,III, sym_pos=1,overwrite_b=1)
10000 loops, best of 3: 40.6 µs per loop

所以你获得了一个非常不可忽视的速度优势。如果您使用复数,则必须使用zpotri

问题是你是否需要反向。如果您需要计算B⁻¹ * A,则可能不需要,因为solve(B,A)对此更好。

答案 3 :(得分:1)

我尝试了@percusse的答案,但是在计时执行时间时,我发现它比np.linalg.inv慢33%(使用100K随机正定4x4 np.float64矩阵的样本)。这是我的实现:

from scipy.linalg import lapack

def upper_triangular_to_symmetric(ut):
    ut += np.triu(ut, k=1).T

def fast_positive_definite_inverse(m):
    cholesky, info = lapack.dpotrf(m)
    if info != 0:
        raise ValueError('dpotrf failed on input {}'.format(m))
    inv, info = lapack.dpotri(cholesky)
    if info != 0:
        raise ValueError('dpotri failed on input {}'.format(cholesky))
    upper_triangular_to_symmetric(inv)
    return inv

我尝试对其进行性能分析,但令我惊讶的是,它花费了大约82%的时间来调用upper_triangular_to_symmetric(这不是“困难”的部分)!我认为发生这种情况是因为它正在执行浮点加法以合并矩阵,而不是简单的副本。

我尝试了一种upper_triangular_to_symmetric的实现,该实现快了约87%(请参见this question and answer):

from scipy.linalg import lapack

inds_cache = {}

def upper_triangular_to_symmetric(ut):
    n = ut.shape[0]
    try:
        inds = inds_cache[n]
    except KeyError:
        inds = np.tri(n, k=-1, dtype=np.bool)
        inds_cache[n] = inds
    ut[inds] = ut.T[inds]


def fast_positive_definite_inverse(m):
    cholesky, info = lapack.dpotrf(m)
    if info != 0:
        raise ValueError('dpotrf failed on input {}'.format(m))
    inv, info = lapack.dpotri(cholesky)
    if info != 0:
        raise ValueError('dpotri failed on input {}'.format(cholesky))
    upper_triangular_to_symmetric(inv)
    return inv

此版本比np.linalg.inv快68%,并且仅花费其42%的时间来呼叫upper_triangular_to_symmetric