Tìm kiếm các hàm riêng nhỏ nhất của ma trận thưa thớt lớn, chậm hơn 100 lần trong SciPy so với Octave


12

Tôi đang cố gắng tính toán một vài (5-500) hàm riêng tương ứng với các giá trị riêng nhỏ nhất của ma trận thưa đối xứng vuông lớn (lên tới 30000x30000) với ít hơn 0,1% giá trị là khác không.

Tôi hiện đang sử dụng scipy.spzzy.linalg.eigsh trong chế độ shift-invert (sigma = 0.0), mà tôi đã tìm ra qua các bài đăng khác nhau về chủ đề này là giải pháp ưa thích. Tuy nhiên, phải mất tới 1h để giải quyết vấn đề trong hầu hết các trường hợp. Mặt khác, chức năng này rất nhanh, nếu tôi yêu cầu các giá trị riêng lớn nhất (giây phụ trên hệ thống của tôi), được mong đợi từ tài liệu này.

Vì tôi quen thuộc hơn với Matlab từ công việc, tôi đã thử giải quyết vấn đề trong Octave, điều này cho tôi kết quả tương tự bằng cách sử dụng eigs (sigma = 0) chỉ trong vài giây (phụ 10 giây). Vì tôi muốn thực hiện quét tham số của thuật toán bao gồm tính toán eigenvector, loại tăng thời gian đó cũng sẽ rất tuyệt khi có trong python.

Lần đầu tiên tôi thay đổi các tham số (đặc biệt là dung sai), nhưng điều đó không thay đổi nhiều về thời gian.

Tôi đang sử dụng Anaconda trên Windows, nhưng đã thử chuyển LAPACK / BLAS được sử dụng bởi scipy (đó là một nỗi đau rất lớn) từ mkl (Anaconda mặc định) sang OpenBlas (được sử dụng bởi Octave theo tài liệu), nhưng không thể thấy sự thay đổi trong hiệu suất.

Tôi không thể tìm ra, liệu có gì đó để thay đổi về ARPACK đã sử dụng (và làm thế nào) không?

Tôi đã tải lên một testcase cho mã bên dưới vào thư mục dropbox sau: https://www.dropbox.com/sh/l6aa6izufzyzqr3/AABqij95hZOvRpnnjRaETQmka?dl=0

Trong 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)

Trong Octave:

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

Bất kỳ trợ giúp đều được đánh giá cao!

Một số tùy chọn bổ sung tôi đã thử dựa trên các nhận xét và đề xuất:

Octave: eigs(M,6,0)eigs(M,6,'sm')cho tôi kết quả tương tự:

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

trong khi eigs(M,6,'sa',struct('tol',2))hội tụ đến

[1.0423 2.7604 6.1548 11.1310 18.0207 25.3933] 

nhanh hơn nhiều, nhưng chỉ khi các giá trị dung sai lớn hơn 2, nếu không nó hoàn toàn không hội tụ và các giá trị khác nhau mạnh.

Python: eigsh(M,k=6,which='SA')eigsh(M,k=6,which='SM')cả hai đều không hội tụ (lỗi ARPACK khi không đạt được độ hội tụ). Chỉ eigsh(M,k=6,sigma=0.0)đưa ra một số giá trị riêng (sau gần một giờ), khác với quãng tám cho những giá trị nhỏ nhất (thậm chí 1 giá trị nhỏ bổ sung được tìm thấy):

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

Nếu dung sai đủ cao, tôi cũng nhận được kết quả eigsh(M,k=6,which='SA',tol='1'), gần với các giá trị thu được khác

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

một lần nữa với một số lượng khác nhau nhỏ. Thời gian tính toán vẫn gần 30 phút. Mặc dù các giá trị rất nhỏ khác nhau có thể hiểu được, vì chúng có thể đại diện cho bội số của 0, bội số khác nhau gây trở ngại cho tôi.

Ngoài ra, dường như có một số khác biệt cơ bản trong SciPy và Octave, mà tôi chưa thể tìm ra.


2
1 - Tôi giả sử bạn có ý định đặt dấu ngoặc quanh [evals, evecs] trong mã quãng tám? 2 - bạn có thể bao gồm một ví dụ nhỏ cho M không? hoặc có thể là một kịch bản tạo cho một nếu có thể?
Nick J

1 - Có chính xác, tôi đã chỉnh sửa bài viết của mình. 2 - Tôi đã thử hiệu năng cho một số ma trận phụ của dữ liệu của mình và có vẻ như Octave luôn nhanh hơn, nhưng đối với các ma trận nhỏ hơn dưới 5000x5000, nó chỉ là 2 đến 2 lần, trên đó nó thực sự xấu. Và vì "dữ liệu thực" của nó, tôi không thể đưa ra một kịch bản trình tạo. Có một cách tiêu chuẩn để tải lên một ví dụ bằng cách nào đó? Do tính thưa thớt, tệp npz khá nhỏ.
Spacekiller23

Tôi đoán bạn có thể chia sẻ một liên kết đến bất kỳ cơ sở lưu trữ đám mây nào.
Patol75

Cám ơn. Tôi đã bao gồm một liên kết dropbox trong bài viết gốc và cập nhật mã thành một ví dụ hoạt động.
Spacekiller23

1
Để củng cố quan điểm của bạn, tôi đã thử nghiệm trên Matlab R2019b và nhận được 84 giây so với 36,5 phút trong Python 3.7, Scipy 1.2.1 (nhanh hơn 26 lần).
Hóa đơn

Câu trả lời:


1

Một phỏng đoán và một số ý kiến, vì tôi không có Matlab / Octave:

Để tìm các giá trị riêng nhỏ của ma trận đối xứng có giá trị riêng> = 0, giống như của bạn, sau đây là waaay nhanh hơn so với shift-invert:

# 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 )đối với eigenpairs lớn thì nhanh hơn shift-invert, vì A * xnhanh hơn so với solve()shift-invert phải làm. Matlab / Octave có thể hình dung làm điều này một Aflipcách tự động, sau khi thử nghiệm nhanh để xác định dương tính với Cholesky.
Bạn có thể chạy eigsh( Aflip )trong Matlab / Octave không?

Các yếu tố khác có thể ảnh hưởng đến độ chính xác / tốc độ:

Mặc định của Arpack cho vector bắt đầu v0là một vector ngẫu nhiên. Tôi sử dụng v0 = np.ones(n), có thể là khủng khiếp đối với một số người Anhưng có thể tái sản xuất :)

Đây Ama trận là gần như chính xác sigular, A * ones~ 0.

Multicore: scipy-arpack với openblas / Lapack sử dụng ~ 3.9 trong số 4 lõi trên iMac của tôi; Matlab / Octave có sử dụng tất cả các lõi không?


Dưới đây là các giá trị bản địa scipy-Arpack cho một số ktol, được lấy từ logfiles dưới 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 có giống nhau không? Nếu không, tất cả các cược đã tắt - đầu tiên hãy kiểm tra tính chính xác, sau đó là tốc độ.

Tại sao các giá trị bản địa chao đảo rất nhiều? Tiny <0 cho một ma trận được cho là không âm xác định là dấu hiệu của lỗi vòng , nhưng thủ thuật thông thường của một ca nhỏ A += n * eps * sparse.eye(n), không giúp ích được gì.


Điều này Ađến từ đâu, vấn đề gì? Bạn có thể tạo tương tự A, nhỏ hơn hoặc sparser?

Hi vọng điêu nay co ich.


Cảm ơn cho đầu vào của bạn và xin lỗi vì trả lời (rất) muộn. Dự án tôi đã sử dụng này đã hoàn thành, nhưng tôi vẫn tò mò, vì vậy tôi đã kiểm tra. Đáng buồn thay, các giá trị riêng trong Ocatve lại khác nhau, vì k = 10 tôi tìm thấy [-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] cũng độc lập với giá trị dung sai trong phạm vi 1e-5 đến 1e-7. Vì vậy, có một sự khác biệt ở đây. Bạn có nghĩ là lạ không khi scipy (bao gồm cả gợi ý của bạn) mang lại các giá trị nhỏ khác nhau phụ thuộc vào số lượng giá trị được truy vấn?
Spacekiller23

@ Spacekiller23, đây là một lỗi, hiện đã được sửa trong scipy 1.4.1 (xem scipy / problems / 11198 ); bạn có thể kiểm tra phiên bản của bạn? Cũng tollộn xộn cho các giá trị riêng nhỏ - hãy hỏi một câu hỏi mới về điều đó nếu bạn thích, hãy cho tôi biết.
chối

1

Tôi biết điều này bây giờ đã cũ, nhưng tôi đã có cùng một vấn đề. Bạn đã xem xét ở đây ( https://docs.scipy.org/doc/scipy/reference/tutorial/arpack.html )?

Có vẻ như khi bạn đặt sigma thành số thấp (0), bạn nên đặt which = 'LM', mặc dù bạn muốn giá trị thấp. Điều này là do cài đặt sigma biến đổi các giá trị bạn muốn (thấp trong trường hợp này) có vẻ cao và do đó bạn vẫn có thể tận dụng các phương thức 'LM', nhanh hơn nhiều để có được những gì bạn muốn (giá trị bản địa thấp ).


Điều này thực sự thay đổi hiệu suất cho bạn? Đó sẽ là một bất ngờ cho tôi. Tôi biết liên kết mà bạn đăng tải và tôi cũng ngầm định chỉ định which = 'LM' trong ví dụ của tôi. Bởi vì giá trị mặc định cho một unset là 'LM'. Tôi vẫn kiểm tra và hiệu suất không thay đổi trong ví dụ của tôi.
Spacekiller23

Thật vậy, dường như có một sự khác biệt tương tự như từ Python đến quãng tám cho bạn. Tôi cũng có một ma trận lớn mà tôi đang cố gắng phân hủy và kết thúc bằng eigsh (matrix, k = 7, which = 'LM', sigma = 1e-10). Ban đầu, tôi đã xác định không chính xác mà suy nghĩ = 'SM' tôi cần phải làm điều đó để có được giá trị bản địa nhỏ nhất và phải mất mãi để trả lời cho tôi. Sau đó, tôi tìm thấy bài báo đó và nhận ra rằng bạn chỉ cần xác định nó thành 'LM' nhanh hơn, và đặt k thành bất cứ điều gì bạn muốn và nó sẽ tăng tốc mọi thứ. Là ma trận của bạn thực sự ẩn sĩ?
Anthony Gatti

0

Tôi muốn nói trước rằng tôi không biết tại sao kết quả mà bạn và @Bill báo cáo lại là như vậy. Tôi chỉ đơn giản là tự hỏi nếu eigs(M,6,0)trong Octave tương ứng với k=6 & sigma=0, hoặc có lẽ nó là cái gì khác?

Với scipy, nếu tôi không cung cấp sigma, tôi có thể nhận được kết quả trong một thời gian tốt theo cách này.

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)

Tôi hoàn toàn không chắc chắn nếu điều này có ý nghĩa mặc dù.

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]

Cách duy nhất tôi tìm thấy để sử dụng sigma và để có được kết quả trong một thời gian thích hợp là cung cấp M làm Công cụ tuyến tính. Tôi không quá quen thuộc với điều này, nhưng từ những gì tôi hiểu thì việc triển khai của tôi đại diện cho một ma trận danh tính, đó là điều M nên làm nếu không được chỉ định trong cuộc gọi. Lý do cho điều này là vì thay vì thực hiện giải quyết trực tiếp (phân rã LU), scipy sẽ sử dụng một bộ giải lặp, có khả năng phù hợp hơn. Để so sánh, nếu bạn cung cấp M = np.identity(a.shape[0]), phải giống hệt nhau, thì eigsh sẽ mất mãi mãi để mang lại kết quả. Lưu ý rằng phương pháp này không hoạt động nếu sigma=0được cung cấp. Nhưng tôi không chắc sigma=0là nó thực sự hữu ích?

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)))

Một lần nữa, không biết nó có đúng không nhưng chắc chắn khác với trước đây. Đó sẽ là tuyệt vời để có đầu vào của một ai đó từ 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]

Cảm ơn bạn đã đóng góp và phản hồi của bạn. Tôi đã thử một số điều để đưa ra một câu trả lời đúng cho quan điểm của bạn. 1. Nhiệm vụ của tôi trong tay đòi hỏi phải tìm k giá trị / vectơ nhỏ nhất. Do đó, cách tiếp cận sử dụng sigma = 0 thậm chí còn được đưa ra trong các tài liệu SciPy: docs.scipy.org/doc/scipy/reference/tutorial/arpack.html 2. Tôi đã thử thêm một số tùy chọn mà tôi đã chỉnh sửa thành câu hỏi ban đầu. 3. Theo tôi hiểu các bộ phim tài liệu của Octave và SciPy, eigs (M, 6.0) và k = 6, simga = 0 nên giống nhau.
Spacekiller23

4. Vì ma trận của tôi là thực và vuông, tôi nghĩ không nên có sự khác biệt giữa SA và SM như một tùy chọn, nhưng rõ ràng, ít nhất là trong tính toán. Tôi đang đi sai đường ở đây? Nhìn chung, điều đó có nghĩa là nhiều câu hỏi hơn và không có câu trả lời hoặc giải pháp thực sự nào từ tôi.
Spacekiller23
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.