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


12

값의 0.1 % 미만이 0이 아닌 큰 대칭 사각형 희소 행렬 (최대 30000x30000)의 가장 작은 고유 값에 해당하는 소수 (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에는 근본적인 차이점이 있지만 아직 알 수 없습니다.


2
1-옥타브 코드에서 [evals, evecs] 주위에 괄호를 넣으려고한다고 가정합니까? 2-M에 대한 작은 예를 포함시킬 수 있습니까? 또는 가능하다면 생성기 스크립트가 있습니까?
Nick J

1-예, 정확하게 게시물을 편집했습니다. 2-내 데이터의 일부 하위 행렬에 대한 성능을 시험해 보았지만 Octave가 항상 더 빠른 것 같지만 5000x5000 미만의 작은 행렬의 경우 2-5 배에 불과하여 실제로는 추악합니다. 그리고 "실제 데이터"이므로 생성기 스크립트를 제공 할 수 없습니다. 어떻게 든 예제를 업로드하는 표준 방법이 있습니까? 희소성 때문에 npz 파일은 상당히 작습니다.
Spacekiller23

모든 클라우드 스토리지 시설에 대한 링크를 공유 할 수 있다고 생각합니다.
Patol75

고마워. 원래 게시물에 보관 용 링크를 포함시키고 코드를 실제 예제로 업데이트했습니다.
Spacekiller23

1
요점을 강화하기 위해 Juts는 Matlab R2019b에서 테스트했으며 Python 3.7, Scipy 1.2.1 (26 배 빠름)에서 36.5 분 대 84 초를 얻었습니다.
Bill

답변:


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()인버트 변환 보다 빠릅니다 . 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하거나 작거나 희소 를 생성 할 수 있습니까 ?

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


의견을 보내 주셔서 감사합니다. (매우) 늦은 답변에 대해 죄송합니다. 내가 사용한 프로젝트는 이미 완료되었지만 여전히 궁금해서 확인했습니다. 슬프게도, 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 (제안 포함)가 쿼리 된 값의 수에 따라 다른 작은 값을 생성하는 것이 이상하다고 생각하지 않습니까?
Spacekiller

@ Spacekiller23, 이것은 버그 1.4.1에서 수정되었습니다 ( scipy / issues / 11198 참조 ). 버전을 확인할 수 있습니까? 또한 tol작은 고유 값에는 지저분합니다. 원하는 경우 알려주세요.
데니스

1

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

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


이것이 실제로 성능을 변경 했습니까? 그것은 저에게 놀라운 일이 될 것입니다. 나는 당신이 postet 링크를 알고 있으며 내 예제에서 which = 'LM'을 암시 적으로 지정했습니다. 설정 해제의 기본값 인 'LM'이 있기 때문입니다. 그래도 확인했지만 성능은 변경되지 않았습니다.
Spacekiller

실제로 파이썬에서 옥타브로 비슷한 차이점이있는 것 같습니다. 또한 분해하려고하는 큰 행렬이 있었고 eigsh (matrix, k = 7, which = 'LM', sigma = 1e-10)를 사용했습니다. 원래 나는 가장 작은 고유 값을 얻기 위해 어떤 = 'SM'사고를해야한다고 잘못 지정했으며, 답을 얻기 위해 영원히 걸리고있었습니다. 그런 다음 그 기사를 찾아서 더 빠른 'LM'으로 지정하고 k를 원하는 것으로 설정하면 속도가 빨라진다는 것을 깨달았습니다. 당신의 매트릭스는 실제로 은자입니까?
Anthony Gatti

0

먼저 귀하와 @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]

귀하의 의견과 피드백에 감사드립니다. 나는 당신의 요점에 알맞은 대답을하기 위해 몇 가지를 시도했습니다. 1. 내 임무는 k 개의 가장 작은 고유 값 / 벡터를 찾는 것입니다. 따라서 sigma = 0을 사용하는 접근 방식은 SciPy 문서에도 제공됩니다. docs.scipy.org/doc/scipy/reference/tutorial/arpack.html 2. 더 많은 옵션을 시도해 보았습니다. 원래 질문으로 편집했습니다. 3. Octave 및 SciPy, eigs (M, 6,0) 및 k = 6의 다큐멘터리를 이해하면 simga = 0은 같아야합니다.
Spacekiller23

4. 내 행렬은 실수와 제곱이므로 SA와 SM 간에는 옵션이 없어야한다고 생각했지만 적어도 계산에는 분명히 있습니다. 내가 잘못된 길을 가고 있습니까? 전반적으로 그것은 더 많은 질문을 의미하지만 실제 답변이나 해결책은 없습니다.
Spacekiller23
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.