查找大型稀疏矩阵的最小特征向量,在SciPy中比在八度中慢100倍以上


12

我正在尝试计算与大型对称方形稀疏矩阵(最大30000x30000)的最小特征值相对应的少数(5-500)特征向量,其中小于0.1%的特征值为非零。

我目前正在Shift-invert模式(sigma = 0.0)中使用scipy.sparse.linalg.eigsh,我通过有关该主题的各种帖子发现这是首选的解决方案。但是,在大多数情况下,最多需要1小时才能解决该问题。另一方面,如果我要求最大的特征值(系统中的秒数),则该功能非常快,这是文档中所期望的。

由于我对工作中的Matlab更加熟悉,因此我尝试在Octave中解决问题,这在短短几秒钟(不到10秒)内使用eigs(sigma = 0)给出了相同的结果。由于我想对包括特征向量计算在内的算法进行参数扫描,因此在python中也要具有这种时间增益。

我首先更改了参数(尤其是公差),但是在时间尺度上并没有太大变化。

我在Windows上使用Anaconda,但尝试将scipy(这是一个很大的痛苦)使用的LAPACK / BLAS从mkl(默认为Anaconda)切换为OpenBlas(根据文档由Octave使用),但看不到更改性能。

我无法弄清楚所使用的ARPACK是否需要更改(以及如何更改)?

我将以下代码的测试用例上传到了以下Dropbox文件夹:https ://www.dropbox.com/sh/l6aa6izufzyzqr3/AABqij95hZOvRpnnjRaETQmka?dl =0

在Python中

import numpy as np
from scipy.sparse import csr_matrix, csc_matrix, linalg, load_npz   
M = load_npz('M.npz')
evals, evecs = linalg.eigsh(M,k=6,sigma=0.0)

在八度音阶中:

M=dlmread('M.txt');
M=spconvert(M);
[evecs,evals] = eigs(M,6,0);

任何帮助都在申请中!

我根据评论和建议尝试了一些其他选项:

八度: eigs(M,6,0)eigs(M,6,'sm')给我相同的结果:

[1.8725e-05 1.0189e-05 7.5622e-06 7.5420e-07 -1.2239e-18 -2.5674e-16]

同时eigs(M,6,'sa',struct('tol',2))收敛到

[1.0423 2.7604 6.1548 11.1310 18.0207 25.3933] 

快得多,但是仅当公差值大于2时,否则它根本不会收敛,并且值有很大差异。

Python的: eigsh(M,k=6,which='SA')eigsh(M,k=6,which='SM')两个不收敛(ARPACK上没有收敛误差达到)。仅eigsh(M,k=6,sigma=0.0)给出一些特征值(近一个小时后),这些特征值与最小特征值的倍频程不同(甚至找到另外一个小值):

[3.82923317e-17 3.32269886e-16 2.78039665e-10 7.54202273e-07 7.56251500e-06 1.01893934e-05]

如果公差足够高,我也会从中获得结果eigsh(M,k=6,which='SA',tol='1'),该结果与其他获得的值相近

[4.28732218e-14 7.54194948e-07 7.56220703e-06 1.01889544e-05, 1.87247350e-05 2.02652719e-05]

再次使用不同数量的小特征值。计算时间仍然接近30分钟。尽管不同的很小的值可能是可以理解的,但由于它们可能表示0的倍数,所以不同的多重性使我感到困惑。

此外,SciPy和Octave似乎存在一些根本差异,但我无法弄清楚。


2
1-我假设您要在八度代码中的[evals,evecs]周围加上方括号?2-您可以列举一个M的小例子吗?或者如果可能的话,生成一个发电机脚本?
尼克J

1-是的,我编辑了帖子。2-我对数据的某些子矩阵进行了性能测试,似乎Octave总是更快,但是对于5000x5000以下的较小矩阵,它只有2-5倍,在此之上,它确实很难看。而且由于它的“真实数据”,我无法给出生成器脚本。是否有以某种方式上传示例的标准方法?由于稀疏性,npz文件相当小。
Spacekiller23 '19

我想您可以共享一个指向任何云存储设施的链接。
Patol75

谢谢。我在原始帖子中添加了一个保管箱链接,并将代码更新为一个有效的示例。
Spacekiller19年

1
为了增强您的观点,我在Matlab R2019b上进行了测试,并在Python 3.7,Scipy 1.2.1中获得了84秒vs 36.5分钟的速度(速度提高了26倍)。
条例草案

Answers:


1

一个猜想和一些评论,因为我没有Matlab / Octave:

为了找到特征值> = 0的对称矩阵的小特征值,像您一样,以下方法比平移快得多:

# flip eigenvalues e.g.
# A:     0 0 0 ... 200 463
# Aflip: 0 163 ... 463 463 463
maxeval = eigsh( A, k=1 )[0]  # biggest, fast
Aflip = maxeval * sparse.eye(n) - A
bigevals, evecs = eigsh( Aflip, which="LM", sigma=None ... )  # biggest, near 463
evals = maxeval - bigevals  # flip back, near 463 -> near 0
# evecs are the same

eigsh( Aflip )大特征对的速度比小特征对的速度快,因为A * x它比solve()移位反作用所必须的速度快。Aflip在使用Cholesky对正定性进行快速测试之后,Matlab / Octave可以自动执行此操作。
您可以eigsh( Aflip )在Matlab / Octave中运行吗?

其他可能影响准确性/速度的因素:

起始向量的Arpack默认值v0是随机向量。我使用v0 = np.ones(n),这对于某些人来说可能很糟糕,A但是可以重现:)

这个A矩阵几乎是正整数,〜0 A * ones

多核:带有openblas的scipy-arpack / Lapack在我的iMac上使用约3.9个4核;Matlab / Octave是否使用所有内核?


这是gist.github 下的日志文件中grepk和grep的几个scipy-Arpack特征值:tol

k 10  tol 1e-05:    8 sec  eigvals [0 8.5e-05 0.00043 0.0014 0.0026 0.0047 0.0071 0.0097 0.013 0.018] 
k 10  tol 1e-06:   44 sec  eigvals [0 3.4e-06 2.8e-05 8.1e-05 0.00015 0.00025 0.00044 0.00058 0.00079 0.0011] 
k 10  tol 1e-07:  348 sec  eigvals [0 3e-10 7.5e-07 7.6e-06 1.2e-05 1.9e-05 2.1e-05 4.2e-05 5.7e-05 6.4e-05] 

k 20  tol 1e-05:   18 sec  eigvals [0 5.1e-06 4.5e-05 0.00014 0.00023 0.00042 0.00056 0.00079 0.0011 0.0015 0.0017 0.0021 0.0026 0.003 0.0037 0.0042 0.0047 0.0054 0.006
k 20  tol 1e-06:   73 sec  eigvals [0 5.5e-07 7.4e-06 2e-05 3.5e-05 5.1e-05 6.8e-05 0.00011 0.00014 0.00016 0.0002 0.00025 0.00027 0.0004 0.00045 0.00051 0.00057 0.00066
k 20  tol 1e-07:  267 sec  eigvals [-4.8e-11 0 7.5e-07 7.6e-06 1e-05 1.9e-05 2e-05 2.2e-05 4.2e-05 5.1e-05 5.8e-05 6.4e-05 6.9e-05 8.3e-05 0.00011 0.00012 0.00013 0.00015

k 50  tol 1e-05:   82 sec  eigvals [-4e-13 9.7e-07 1e-05 2.8e-05 5.9e-05 0.00011 0.00015 0.00019 0.00026 0.00039 ... 0.0079 0.0083 0.0087 0.0092 0.0096 0.01 0.011 0.011 0.012
k 50  tol 1e-06:  432 sec  eigvals [-1.4e-11 -4e-13 7.5e-07 7.6e-06 1e-05 1.9e-05 2e-05 2.2e-05 4.2e-05 5.1e-05 ... 0.00081 0.00087 0.00089 0.00096 0.001 0.001 0.0011 0.0011
k 50  tol 1e-07: 3711 sec  eigvals [-5.2e-10 -4e-13 7.5e-07 7.6e-06 1e-05 1.9e-05 2e-05 2.2e-05 4.2e-05 5.1e-05 ... 0.00058 0.0006 0.00063 0.00066 0.00069 0.00071 0.00075

versions: numpy 1.18.1  scipy 1.4.1  umfpack 0.3.2  python 3.7.6  mac 10.10.5 

Matlab / Octave差不多吗?如果不是,则所有投注均无效-首先检查正确性,然后检查速度。

为什么特征值会如此摇摆?对于假定为非负定矩阵的Tiny <0,表示出现 舍入误差,但通常会发生微小变化的技巧A += n * eps * sparse.eye(n)无济于事。


A是哪里来的,什么问题地区?您可以生成相似的A,较小的还是稀疏的?

希望这可以帮助。


感谢您的输入,对于(很晚)的回复表示抱歉。我用于此的项目已经完成,但是我仍然很好奇,所以我检查了一下。可悲的是,Ocatve的特征值不同,因为k = 10我发现[-2.5673e-16 -1.2239e-18 7.5420e-07 7.5622e-06 1.0189e-05 1.8725e-05 2.0265e-05 2.1568e- [05 4.2458e-05 5.1030e-05]也与1e-5至1e-7范围内的公差值无关。因此,这里还有另一个区别。您是否认为scipy(包括您的建议)根据所查询的值的数量产生不同的小值感到奇怪?
Spacekiller23

@ Spacekiller23,这是一个错误,现已在scipy 1.4.1中修复(请参见scipy / issues / 11198);你能检查你的版本吗?tol小特征值也很麻烦-如果您愿意,请问一个新问题,让我知道。
denis

1

我知道现在已经很老了,但是我遇到了同样的问题。您是否在这里查看过(https://docs.scipy.org/doc/scipy/reference/tutorial/arpack.html)?

似乎将sigma设置为较低的数字(0)时,即使您希望将其设置为较低的值,也应设置which ='LM'。这是因为设置sigma会使所需的值(在这种情况下为低)变换为较高的值,因此您仍然可以利用“ LM”方法,该方法可以更快地获得所需的值(低特征值) )。


这确实改变了您的表现吗?对我来说这是一个惊喜。我知道您发布的链接,并且在我的示例中也隐式指定了哪个=“ LM”。因为未设置的默认值是'LM'。我仍然检查了一下,对于我的示例,性能没有变化。
Spacekiller23

确实,似乎从Python到八度都有相似的区别。我还尝试分解一个大矩阵,并最终使用eigsh(matrix,k = 7,which ='LM',sigma = 1e-10)。最初,我以错误的方式指定哪个='SM',以获取最小的特征值,这是我永远需要的答案。然后,我找到了该文章,并意识到您只需要将其指定为更快的“ LM”,然后将k设置为您想要的任何值即可加快速度。您的矩阵实际上是Hermitian吗?
安东尼·加蒂

0

我首先要说的是,我不知道您和@Bill报告的结果为何如此。我只是想知道eigs(M,6,0)Octave中是否对应于k=6 & sigma=0,或者还有其他东西吗?

使用scipy,如果我不提供sigma,则可以通过这种方式在适当的时间得到结果。

import numpy as np
from scipy.sparse import csr_matrix
from scipy.sparse.linalg import eigsh
from time import perf_counter
M = np.load('M.npz')
a = csr_matrix((M['data'], M['indices'], M['indptr']), shape=M['shape'])
t = perf_counter()
b, c = eigsh(a, k=50, which='SA', tol=1e1)
print(perf_counter() - t)
print(b)

我完全不确定这是否有意义。

0.4332823531003669
[4.99011753e-03 3.32467891e-02 8.81752215e-02 1.70463893e-01
 2.80811313e-01 4.14752072e-01 5.71103821e-01 7.53593653e-01
 9.79938915e-01 1.14003837e+00 1.40442848e+00 1.66899183e+00
 1.96461415e+00 2.29252666e+00 2.63050114e+00 2.98443218e+00
 3.38439528e+00 3.81181747e+00 4.26309942e+00 4.69832271e+00
 5.22864462e+00 5.74498014e+00 6.22743988e+00 6.83904055e+00
 7.42379697e+00 7.97206446e+00 8.62281827e+00 9.26615266e+00
 9.85483434e+00 1.05915030e+01 1.11986296e+01 1.18934953e+01
 1.26811461e+01 1.33727614e+01 1.41794599e+01 1.47585155e+01
 1.55702295e+01 1.63066947e+01 1.71564622e+01 1.78260727e+01
 1.85693454e+01 1.95125277e+01 2.01847294e+01 2.09302671e+01
 2.18860389e+01 2.25424795e+01 2.32907153e+01 2.37425085e+01
 2.50784800e+01 2.55119112e+01]

我发现使用sigma并在适当的时间内获得结果的唯一方法是将M提供为LinearOperator。我对此不太熟悉,但是据我了解,我的实现表示一个单位矩阵,如果未在调用中指定M,则应为M。这样做的原因是scipy不会执行直接求解(LU分解),而是使用迭代求解器,它可能更适合。作为比较,如果您提供M = np.identity(a.shape[0]),则应该完全相同,那么eigsh会花很多时间才能得出结果。请注意,如果sigma=0提供此方法,则该方法无效。但是我不确定是否sigma=0真的有用吗?

import numpy as np
from scipy.sparse import csr_matrix
from scipy.sparse.linalg import eigs, eigsh, LinearOperator
from time import perf_counter


def mv(v):
    return v


M = np.load('M.npz')
a = csr_matrix((M['data'], M['indices'], M['indptr']), shape=M['shape'])
t = perf_counter()
b, c = eigsh(a, M=LinearOperator(shape=a.shape, matvec=mv, dtype=np.float64),
             sigma=5, k=50, which='SA', tol=1e1, mode='cayley')
print(perf_counter() - t)
print(np.sort(-5 * (1 + b) / (1 - b)))

同样,不知道它是否正确,但绝对不同于以前。拥有某人的潜意识,那将是很棒的。

1.4079377939924598
[3.34420263 3.47938816 3.53019328 3.57981026 3.60457277 3.63996294
 3.66791416 3.68391585 3.69223712 3.7082205  3.7496456  3.76170023
 3.76923989 3.80811939 3.81337342 3.82848729 3.84137264 3.85648208
 3.88110869 3.91286153 3.9271108  3.94444577 3.97580798 3.98868207
 4.01677424 4.04341426 4.05915855 4.08910692 4.12238969 4.15283192
 4.16871081 4.1990492  4.21792125 4.24509036 4.26892806 4.29603036
 4.32282475 4.35839271 4.37934257 4.40343219 4.42782208 4.4477206
 4.47635849 4.51594603 4.54294049 4.56689989 4.58804775 4.59919363
 4.63700551 4.66638214]

感谢您的输入和反馈。我尝试了一些方法来为您的观点提供一个好的答案。1.我手头的任务需要找到k个最小的特征值/向量。因此,甚至在SciPy文档中也提供了使用sigma = 0的方法:docs.scipy.org/doc/scipy/reference/tutorial/arpack.html 2.我尝试了其他一些选项,并将其编辑为原始问题。3.据我了解,Octave和SciPy的纪录片eigs(M,6,0)和k = 6,simga = 0应该是相同的。
Spacekiller19年

4.由于我的矩阵是实数和平方,因此我认为SA和SM之间应该没有区别,但至少在计算上显然存在区别。我在这里走错了路吗?总体而言,这意味着还有更多问题,但我没有真正的答案或解决方案。
Spacekiller19年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.