[python] SavePy에서 옥타브보다 100 배 이상 느린 큰 희소 행렬의 가장 작은 고유 벡터 찾기

값의 0.1 % 미만이 0이 아닌 큰 대칭 사각형 희소 행렬 (최대 30000×30000)의 가장 작은 고유 값에 해당하는 소수 (5-500) 고유 벡터를 계산하려고합니다.

나는 현재 scipy.sparse.linalg.eigsh를 shift-invert 모드 (sigma = 0.0)로 사용하고 있는데, 주제에 대한 다양한 게시물을 통해 알아 낸 해결책입니다. 그러나 대부분의 경우 문제를 해결하는 데 최대 1 시간이 걸립니다. 반면에 문서에서 예상 한 최대 고유 값 (시스템의 서브 초)을 요청하면 함수가 매우 빠릅니다.

직장에서 Matlab에 대해 더 잘 알고 있기 때문에 Octave에서 문제를 해결하려고 시도했는데 몇 초 만에 eigs (sigma = 0)를 사용하여 동일한 결과를 얻었습니다 (10 초 미만). 고유 벡터 계산을 포함하여 알고리즘의 매개 변수 스윕을 수행하고 싶기 때문에 이러한 종류의 시간 이득은 파이썬에서도 유용합니다.

먼저 매개 변수 (특히 공차)를 변경했지만 시간 척도에서는 그다지 바뀌지 않았습니다.

Windows에서 Anaconda를 사용하고 있지만 scipy가 사용하는 LAPACK / BLAS를 (매우 큰 Anaconda) mkl (기본 Anaconda)에서 OpenBlas (문서에 따라 Octave에서 사용)로 전환하려고했지만 변경 사항을 볼 수 없었습니다. 공연.

중고 ARPACK에 대해 변경할 것이 있었는지 여부와 그 방법을 알 수 없었습니까?

아래 코드에 대한 테스트 케이스를 다음 dropbox-folder에 업로드했습니다 :
https://www.dropbox.com/sh/l6aa6izufzyzqr3/AABqij95hZOvRpnnjRaETQmka?dl=0

파이썬에서

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를 초과하는 경우에만 훨씬 빠르지 만, 그렇지 않으면 전혀 수렴하지 않고 값이 크게 다릅니다.

파이썬 :
eigsh(M,k=6,which='SA')그리고 eigsh(M,k=6,which='SM')모두하지 수렴 (NO 융합에 ARPACK 오류에 도달). eigsh(M,k=6,sigma=0.0)가장 작은 값의 옥타브와 다른 고유 값 (약 1 시간 후) 만 제공합니다 (1 개의 추가 작은 값이 발견 된 경우에도).

[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에는 근본적인 차이점이 있지만 아직 알 수 없습니다.



답변

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()인버트 변환 보다 빠릅니다 . AflipCholesky로 양의 명확한 테스트를 마친 후에 Matlab / Octave는이를 자동으로 수행 할 수있었습니다 . Matlab / Octave에서
실행할 수 있습니까 eigsh( Aflip )?

정확도 / 속도에 영향을 줄 수있는 다른 요소들 :

시작 벡터에 대한 Arpack의 기본값 v0은 임의의 벡터입니다. 나는 v0 = np.ones(n)끔찍 A하지만 재현 할 수있는을 사용합니다 🙂

A행렬은 거의 정확히 A * ones~ 0 인 정사각형 입니다.

멀티 코어 : openblas / Lapack이 장착 된 scipy-arpack은 iMac에서 4 개의 코어 중 ~ 3.9를 사용합니다. Matlab / Octave는 모든 코어를 사용합니까?


여기에 scipy-Arpack의 몇 가지에 대한 고유 값 ktol아래에 로그 파일에서 grepped, gist.github는 :

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하거나 작거나 희소 를 생성 할 수 있습니까 ?

도움이 되었기를 바랍니다.


답변

나는 이것이 오래되었다는 것을 알고 있지만 같은 문제가있었습니다. 여기 ( https://docs.scipy.org/doc/scipy/reference/tutorial/arpack.html ) 를 검토 했습니까 ?

시그마를 낮은 숫자 (0)로 설정하면 값을 낮추려는 경우에도 which = ‘LM’을 설정해야합니다. 시그마를 설정하면 원하는 값 (이 경우에는 낮음)이 높은 것처럼 보이기 때문에 원하는 것 (낮은 고유 값)을 얻는 데 훨씬 빠른 ‘LM’방법을 여전히 이용할 수 있기 때문입니다. ).


답변

먼저 귀하와 @Bill이보고 한 결과가 왜 그런지 전혀 모른다고 말하고 싶습니다. eigs(M,6,0)옥타브에서에 해당 하는지 궁금 k=6 & sigma=0하거나 다른 것일 수 있습니까?

scipy를 사용하면 시그마를 제공하지 않으면 이런 식으로 적절한 시간에 결과를 얻을 수 있습니다.

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로 제공하는 것입니다. 나는이 일에 너무 익숙하지 않지만, 내가 이해 한 바에 따르면 구현에서 ID 매트릭스를 나타내며, 이는 호출에 지정되지 않은 경우 M이어야합니다. 그 이유는 직접 해결 (LU 분해)을 수행하는 대신 scipy는 잠재적으로 더 적합한 반복 솔버를 사용하기 때문입니다. 비교 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)))

다시 말하지만 그것이 정확하지만 확실히 이전과는 다른지 모릅니다. scipy에서 누군가의 의견을 얻는 것이 좋을 것입니다.

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]


답변