我在python中用numpy
反转协方差矩阵。协方差矩阵是对称的,半正定的。
我想知道是否存在针对对称正半定矩阵优化的算法,比numpy.linalg.inv()
更快(当然如果可以从python中轻松获得它的实现!)。我无法找到numpy.linalg
中的内容或搜索网页。
修改
正如@yixuan所观察到的,正半正定矩阵通常不是严格可逆的。我检查过,在我的情况下,我只是得到了正定矩阵,所以我接受了一个有正确定义的答案。无论如何,在LAPACK低级例程中,我找到了DSY*
例程,这些例程仅针对simmetric / hermitian矩阵进行了优化,虽然scipy
中似乎缺少它们(可能只是安装版本的问题)
答案 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
。