Виявлення найменших власних векторів великої розрідженої матриці, на 100 разів повільніше в SciPy, ніж в Октаві


12

Я намагаюся обчислити декілька (5-500) власних векторів, що відповідають найменшим власним значенням великих симетричних квадратних розріджених матриць (до 30000x30000), причому менше 0,1% значень є ненульовими.

В даний час я використовую scipy.sparse.linalg.eigsh у режимі зсуву-інвертування (сигма = 0,0), що я з'ясував, використовуючи різні пости в темі, є кращим рішенням. Однак для вирішення проблеми в більшості випадків потрібно до 1 год. З іншого боку, функція дуже швидка, якщо я запитую найбільші власні значення (підсекунди в моїй системі), що очікувалося з документації.

Оскільки я більше знайомий з Матлабом з роботи, я спробував вирішити проблему в Octave, що дало мені той же результат, використовуючи eigs (sigma = 0) протягом декількох секунд (суб 10с). Оскільки я хочу зробити розгортку параметрів алгоритму, включаючи обчислення власного вектора, такий виграш часу буде чудово мати і в python.

Я спершу змінив параметри (особливо толерантність), але це не сильно змінилося на шкалі часу.

Я використовую Anaconda в Windows, але спробував переключити LAPACK / BLAS, використовуваний scipy (що був величезним болем) з mkl (анаконда за замовчуванням) на OpenBlas (використовується Octave згідно з документацією), але не зміг змінити виконання.

Я не зміг зрозуміти, чи щось змінити вживаний ARPACK (і як)?

Я завантажив тестовий зразок коду нижче до такої папки-папки: 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)Дає лише деякі власні значення (майже через годину), які відрізняються октавою для найменших (навіть 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? а може, сценарій генератора для одного, якщо це можливо?
Нік Дж.

1 - Так, я редагував свою публікацію. 2 - Я випробував продуктивність для деяких субматриць моїх даних, і здається, що Octave завжди швидше, але для менших матриць нижче 5000x5000 це лише коефіцієнт у 2-5 разів, вище, ніж він стає дійсно потворним. А оскільки його «реальні дані» я не можу дати сценарій генератора. Чи є стандартний спосіб якось завантажити приклад? У зв'язку з рідкістю npz-файлу досить мало.
Spacekiller23

Я думаю, ви можете поділитися посиланням на будь-який хмарний сховище.
Patol75

Дякую. Я включив посилання на папку в початковій публікації та оновив код на робочому прикладі.
Spacekiller23

1
Перевіривши свою думку, я перевірив Matlab R2019b і отримав 84 секунди проти 36,5 хв у Python 3.7, Scipy 1.2.1 (у 26 разів швидше).
Білл

Відповіді:


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автоматично, після швидкого тесту на позитивне з Чолеським.
Чи можете ви бігти eigsh( Aflip )в Matlab / Octave?

Інші фактори, які можуть впливати на точність / швидкість:

За замовчуванням для початкового вектора Arpack v0є випадковий вектор. Я використовую v0 = np.ones(n), що для когось може бути страшним, Aале відтворюється :)

Ця Aматриця є майже точно знаковою, A * ones~ 0.

Multicore: scipy-arpack з openblas / Lapack використовує ~ 3,9 з 4 ядер на моєму iMac; чи застосовують Matlab / Octave всі ядра?


Ось власні значення scipy-Arpack для кількох kта tol, зібрані з logfiles під 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 приблизно однакові? Якщо ні, всі ставки відключені - спочатку перевірте правильність, а потім швидкість.

Чому власне значення так хитаються? Крихітні <0 для нібито матриці, що не має негативного значення, є ознакою помилки округлення , але звичайний трюк крихітного зсуву A += n * eps * sparse.eye(n)не допомагає.


Звідки це Aпоходить, яка проблемна область? Чи можете ви генерувати подібні A, менші чи менші розміри?

Сподіваюсь, це допомагає.


Дякуємо за ваш внесок і вибачте за (дуже) пізню відповідь. Проект, для якого я цим скористався, вже завершений, але мені все одно цікаво, тому я перевірив. На жаль, власні значення в Окатві виходять різними, бо 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. Тож тут є ще одна відмінність. Ви не вважаєте, що дивно, що наука (включаючи вашу пропозицію) дає різні невеликі значення залежно від кількості запитуваних значень?
Spacekiller23

@ Spacekiller23, це була помилка, тепер виправлена ​​в scipy 1.4.1 (див. Scipy / issues / 11198 ); Ви можете перевірити свою версію? Також tolбезлад для невеликих власних цінностей - задайте нове запитання щодо цього, якщо вам подобається, дайте мені знати.
деніс

1

Я знаю, що це вже давно, але в мене була така ж проблема. Ви тут переглянули ( https://docs.scipy.org/doc/scipy/reference/tutorial/arpack.html )?

Схоже, коли ви встановлюєте sigma на низьке число (0), ви повинні встановити, що = 'LM', навіть якщо ви хочете низьких значень. Це тому, що встановлення сигми перетворює значення, які ви хочете (низькі в цьому випадку), здаються високими, і ви все ще можете скористатися методами 'LM', які набагато швидше отримати те, що ви хочете (низькі власні значення) ).


Це насправді змінило продуктивність для вас? Це було б для мене сюрпризом. Я знав посилання, яке ви розміщуєте, і я неявно вказав, що = 'LM' у своєму прикладі. Тому що значення за замовчуванням для скидання - "LM". Я все-таки перевірив, але продуктивність не змінюється для мого прикладу.
Spacekiller23

Дійсно, схоже, для вас схожа різниця, як від Python до октави. У мене також була велика матриця, яку я намагався розкласти і в кінцевому підсумку використовував eigsh (матриця, k = 7, яка = 'LM', sigma = 1e-10). Спочатку я неправильно вказував, що = "SM", думаючи, що мені потрібно було зробити це, щоб отримати найменші власні значення, і мені потрібно було відповісти назавжди. Потім я знайшов цю статтю і зрозумів, що вам потрібно просто вказати її на швидший "LM", і встановив k таким, що ви хочете, і це прискорить роботу. Ваша матриця насправді герміця?
Ентоні Гатті

0

Я спершу хочу сказати, що я не маю поняття, чому результати, про які ви повідомили, і @Bill такі, якими вони є. Мені просто цікаво, чи eigs(M,6,0)відповідає Octave k=6 & sigma=0, чи, можливо, це щось інше?

З наукою, якщо я не надаю сигми, я можу отримати результат у гідний час таким чином.

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]

Єдиний спосіб, коли я знайшов використовувати сигму та отримати результат у гідний час, - це забезпечити М як LinearOperator. Я не надто знайомий з цією справою, але з того, що я зрозумів, моя реалізація являє собою матрицю ідентичності, якою має бути 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)))

Знову ж таки, поняття не має, чи це правильно, але, безумовно, відрізняється від раніше. Це було б чудово, щоб мати внесок когось із науковців.

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.