SciPy'de Octave'den 100 kat daha yavaş, büyük seyrek matrisin en küçük özvektörlerini bulmak


12

Büyük simetrik kare seyrek matrislerin (30000x30000'e kadar) en küçük özdeğerlerine karşılık gelen az sayıda (5-500) özvektörü, değerlerin% 0.1'inden daha azının sıfır olmadığı hesaplamaya çalışıyorum.

Şu anda konu hakkında çeşitli mesajlar aracılığıyla anladım shift-invert modunda (sigma = 0.0) scipy.sparse.linalg.eigsh kullanıyorum tercih edilen çözümdür. Ancak, çoğu durumda sorunu çözmek 1 saate kadar sürebilir. Öte yandan, dokümantasyondan beklenen en büyük özdeğerleri (sistemimde alt saniye) istersem işlev çok hızlıdır.

Matlab'a işten daha aşina olduğum için, Octave'deki sorunu çözmeyi denedim, bu da bana aynı sonucu birkaç saniye içinde (sigma = 0) birkaç saniye içinde verdi (alt 10s). Özvektör hesaplamasını içeren algoritmanın bir parametre taramasını yapmak istediğim için, bu tür zaman kazancı python'da da olması harika olurdu.

Öncelikle parametreleri değiştirdim (özellikle tolerans), ancak zaman ölçeklerinde çok fazla değişmedi.

Windows'da Anaconda kullanıyorum, ancak scipy (büyük bir acıydı) tarafından kullanılan LAPACK / BLAS'ı mkl'den (varsayılan Anaconda) OpenBlas'a (belgelere göre Octave tarafından kullanılan) değiştirmeye çalıştım, ancak bir değişiklik göremedim verim.

Kullanılan ARPACK (ve nasıl) hakkında değişecek bir şey olup olmadığını anlayamadım?

Aşağıdaki kod için bir testcase'i aşağıdaki dropbox klasörüne yükledim: https://www.dropbox.com/sh/l6aa6izufzyzqr3/AABqij95hZOvRpnnjRaETQmka?dl=0

Python bölgesinde

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)

Octave'de:

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

Herhangi bir yardım appriciated!

Yorum ve önerilere dayanarak denediğim bazı ek seçenekler:

Oktav: eigs(M,6,0)ve eigs(M,6,'sm')bana aynı sonucu ver:

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

iken eigs(M,6,'sa',struct('tol',2))için yakınsak

[1.0423 2.7604 6.1548 11.1310 18.0207 25.3933] 

çok daha hızlı, ancak yalnızca tolerans değerleri 2'nin üzerindeyse, aksi takdirde hiç bir şekilde birleşmez ve değerler oldukça farklıdır.

Python: eigsh(M,k=6,which='SA')ve eigsh(M,k=6,which='SM')her ikisi de yakınsama yapmıyor (yakınsamada ARPACK hatası oluşmadı). Sadece eigsh(M,k=6,sigma=0.0)en küçük olanlar için oktavdan farklı olan bazı özdeğerler (neredeyse bir saat sonra) verir (1 ek küçük değer bile bulunur):

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

Tolerans yeterince yüksekse eigsh(M,k=6,which='SA',tol='1'), elde edilen diğer değerlere yaklaşan sonuçlar da alırım

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

yine farklı sayıda küçük özdeğer ile. Hesaplama süresi hala yaklaşık 30 dakikadır. Farklı çok küçük değerler anlaşılabilir olsa da, 0'ın katlarını temsil edebildikleri için, farklı çokluk beni şaşırtıyor.

Ek olarak, henüz çözemediğim SciPy ve Octave'de bazı temel farklılıklar var gibi görünüyor.


2
1 - Oktav kodunda [evals, evecs] etrafına parantez koymak istediğinizi varsayıyorum? 2 - M için küçük bir örnek verebilir misiniz? veya mümkünse biri için bir jeneratör komut dosyası olabilir mi?
Nick J

1 - Evet, yazımı tamamen düzenledim. 2 - Verilerimin bazı alt matrislerinin performansını denedim ve Octave her zaman daha hızlı görünüyor, ancak 5000x5000'in altındaki daha küçük matrisler için sadece 2-5 kez bir faktör, bunun üzerinde gerçekten çirkinleşiyor. Ve onun "gerçek veri" beri bir jeneratör komut veremiyorum. Bir şekilde örnek yüklemenin standart bir yolu var mı? Seyreklik nedeniyle, bir npz dosyası oldukça küçüktür.
Spacekiller23

Sanırım herhangi bir bulut depolama tesisine bağlantı paylaşabilirsiniz.
Patol75

Teşekkür. Orijinal yazıya bir dropbox bağlantısı ekledim ve kodu çalışan bir örneğe güncelledim.
Spacekiller23

1
Juts puanınızı güçlendirmek için, Matlab R2019b üzerinde test yaptım ve Python 3.7, Scipy 1.2.1'de (26 kat daha hızlı) 84 saniye vs 36.5 dakika aldım.
Bill

Yanıtlar:


1

Bir varsayım ve bazı yorumlar, çünkü Matlab / Octave yok:

Sizinki gibi öz => 0 olan simetrik matrislerin küçük özdeğerlerini bulmak için, aşağıdakiler kaydırma-ters çevirmeden daha hızlıdır:

# 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 )Çünkü büyük eigenpairs için, daha hızlı küçük için shift-invert daha A * xhızlı daha solve()vardiya-invert yapması gerektiğini. Matlab / Octave AflipCholesky ile pozitif tanımlı hızlı bir testten sonra bunu otomatik olarak yapabilirdi . Matlab / Octave'de
koşabilir misin eigsh( Aflip )?

Doğruluğu / hızı etkileyebilecek diğer faktörler:

Arpack'in başlangıç ​​vektörü için varsayılan v0değeri rastgele bir vektördür. Kullandığım v0 = np.ones(n)bazıları için korkunç olabilen Afakat tekrarlanabilir olduğunu :)

Bu Amatris neredeyse tam anlamıyla sinsidir, A * ones~ 0.

Çok çekirdekli: openblas / Lapack ile scipy-arpack, iMac'imdeki 4 çekirdeğin ~ 3.9'unu kullanıyor; / Octave tüm çekirdekleri kullanıyor mu?


İşte birkaç için scipy-Arpack özdeğerleri kve tolgist.github altındaki log dosyalarından çıkarıldı :

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 aynı mıdır? Değilse, tüm bahisler kapalıdır - önce doğruluğunu kontrol edin, ardından hızı kontrol edin.

Özdeğerler neden bu kadar çok sallanıyor? Olumsuz olduğu kesin olmayan bir matris için minik <0, yuvarlama hatasının bir işaretidir , ancak küçük bir kaymanın olağan hilesi A += n * eps * sparse.eye(n)yardımcı olmaz.


Bu Anereden geliyor, hangi problem alanı? Benzer A, daha küçük veya daha seyrek üretebilir misiniz?

Bu yardımcı olur umarım.


Girişiniz için teşekkürler ve (çok) geç cevapladığınız için üzgünüm. Bunu kullandığım proje zaten tamamlandı, ama hala merak ediyorum, bu yüzden kontrol ettim. Ne yazık ki, Ocatve'deki özdeğerler farklı çıkıyor, k = 10 için [-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] aynı zamanda 1e-5 ila 1e-7 aralığındaki tolerans değerinden bağımsızdır. Burada başka bir fark var. Scipy'nin (öneriniz dahil) sorgulanan değer sayısına bağlı olarak farklı küçük değerler vermesinin garip olduğunu düşünmüyor musunuz?
Spacekiller23

@ Spacekiller23, bu artık 1.4.1 scipy ile giderilen bir hataydı (bkz. Scipy / issue / 11198 ); sürümünüzü kontrol edebilir misiniz? Ayrıca tolküçük özdeğerler için dağınık - eğer isterseniz, bana bildirin.
denis

1

Bunun eski olduğunu biliyorum, ama aynı problemi yaşadım. Burayı incelediniz mi ( https://docs.scipy.org/doc/scipy/reference/tutorial/arpack.html )?

Sigma'yı düşük bir sayıya (0) ayarladığınızda, düşük değerlere sahip olsanız bile hangi = 'LM' ayarlamanız gerekir. Bunun nedeni sigma ayarının, istediğiniz değerleri (bu durumda düşük) yüksek görünmesini sağlaması ve böylece istediğinizi elde etmek için çok daha hızlı olan 'LM' yöntemlerinden yararlanabilmenizdir (düşük özdeğerler) ).


Bu sizin için performansı gerçekten değiştirdi mi? Bu benim için bir sürpriz olurdu. Postet bağlantıyı biliyordum ve örtük olarak benim örnekte hangi = 'LM' belirttim. Çünkü bir set için varsayılan değer olan 'LM'. Yine de kontrol ettim ve örneğim için performans değişmedi.
Spacekiller23

Gerçekten de, Python'dan oktava sizin için benzer bir fark var gibi görünüyor. Ayrıştırmaya çalıştığım büyük bir matrisim vardı ve eigsh (matris, k = 7, = 'LM', sigma = 1e-10) kullanarak sona erdi. Başlangıçta hangi = 'SM' düşüncesinin en küçük özdeğerleri elde etmek için yapmam gerektiğini yanlış bir şekilde belirtiyordum ve bana cevap vermek sonsuza dek sürüyordu. Sonra, bu makaleyi buldum ve sadece daha hızlı 'LM' olarak belirtmeniz gerektiğini fark ettim ve k'yi istediğiniz her şey olarak ayarlayın ve işleri hızlandıracaktı. Matrisiniz aslında münzevi mi?
Anthony Gatti

0

İlk olarak, siz ve @ Fatura'nın bildirdiğiniz sonuçların neden böyle olduklarına dair hiçbir fikrim olmadığını söylemek istiyorum. Basitçe eigs(M,6,0)Octave'de karşılık geliyor mu k=6 & sigma=0, yoksa belki de başka bir şey mi?

Scipy ile sigma sağlamazsam, bu şekilde iyi bir zamanda sonuç alabilirim.

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)

Bunun mantıklı olup olmadığından tamamen emin değilim.

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 kullanmanın ve iyi bir zamanda sonuç almanın tek yolu M'yi LinearOperator olarak sağlamaktır. Bu şeye çok aşina değilim, ama anladığım kadarıyla benim uygulama bir kimlik matrisini temsil ediyor, bu çağrıda belirtilmemişse M olması gereken şey. Bunun nedeni, doğrudan çözme (LU ayrışması) yerine, scipy'nin potansiyel olarak daha uygun olan yinelemeli bir çözücü kullanmasıdır. Bir karşılaştırma olarak, eğer M = np.identity(a.shape[0])tam olarak aynı olması gerekiyorsa, eigsh sonsuza kadar bir sonuç verir. Belirtilirse bu yaklaşımın işe yaramadığını unutmayın sigma=0. Ama sigma=0gerçekten faydalı olup olmadığından emin değilim ?

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

Yine, doğru ama kesinlikle öncekinden farklı olup olmadığı hakkında hiçbir fikir. Scipy'den birinin girdisine sahip olmak harika olurdu.

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]

Giriş ve geri bildiriminiz için teşekkür ederiz. Puanlarınıza iyi bir cevap vermek için bazı şeyler denedim. 1. Görevim en küçük özdeğerleri / vektörleri bulmayı gerektiriyor. Bu nedenle, sigma = 0 kullanan yaklaşım bile SciPy belgelerinde verilmiştir: docs.scipy.org/doc/scipy/reference/tutorial/arpack.html 2. Orijinal soruda düzenlediğim bazı seçenekleri denedim. 3. Octave ve SciPy'nin belgesellerini anladığım gibi, eigs (M, 6,0) ve k = 6, simga = 0 aynı olmalıdır.
Spacekiller23

4. Matrisim gerçek ve kare olduğundan, bir seçenek olarak SA ve SM arasında hiçbir fark olmaması gerektiğini düşündüm, ama en azından hesaplamada açıkçası var. Burada yanlış bir yolda mıyım? Genel olarak bu daha fazla soru anlamına geliyor ve benden gerçek cevaplar veya çözümler yok.
Spacekiller23
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.