@amoeba miał doskonałe odpowiedzi na pytania PCA, w tym na temat stosunku SVD do PCA. Odpowiadając na twoje dokładne pytanie, podniosę trzy punkty:
- matematycznie nie ma różnicy, czy PCA oblicza się bezpośrednio na macierzy danych, czy na jej macierzy kowariancji
- różnica wynika wyłącznie z precyzji liczbowej i złożoności. Zastosowanie zastosowania SVD bezpośrednio do macierzy danych jest liczbowo bardziej stabilne niż do macierzy kowariancji
- SVD można zastosować do macierzy kowariancji w celu wykonania PCA lub uzyskania wartości własnych, w rzeczywistości jest to moja ulubiona metoda rozwiązywania problemów własnych
Okazuje się, że SVD jest bardziej stabilny niż typowe procedury dekompozycji wartości własnej, szczególnie w przypadku uczenia maszynowego. W uczeniu maszynowym łatwo jest uzyskać bardzo kolinearne regresory. SVD działa lepiej w tych przypadkach.
Oto kod Pythona, aby pokazać punkt. Stworzyłem wysoce współliniową macierz danych, uzyskałem jej macierz kowariancji i próbowałem uzyskać wartości własne tej ostatniej. SVD nadal działa, podczas gdy zwykły rozkład własny nie udaje się w tym przypadku.
import numpy as np
import math
from numpy import linalg as LA
np.random.seed(1)
# create the highly collinear series
T = 1000
X = np.random.rand(T,2)
eps = 1e-11
X[:,1] = X[:,0] + eps*X[:,1]
C = np.cov(np.transpose(X))
print('Cov: ',C)
U, s, V = LA.svd(C)
print('SVDs: ',s)
w, v = LA.eig(C)
print('eigen vals: ',w)
Wydajność:
Cov: [[ 0.08311516 0.08311516]
[ 0.08311516 0.08311516]]
SVDs: [ 1.66230312e-01 5.66687522e-18]
eigen vals: [ 0. 0.16623031]
Aktualizacja
Odpowiadając na komentarz Federico Poloni, oto kod z testami stabilności SVD vs Eig na 1000 losowych próbkach tej samej matrycy powyżej. W wielu przypadkach Eig wykazuje 0 małych wartości własnych, co prowadziłoby do osobliwości macierzy, a SVD nie robi tego tutaj. SVD jest około dwa razy bardziej precyzyjny przy niewielkim określaniu wartości własnej, co może, ale nie musi być ważne, w zależności od twojego problemu.
import numpy as np
import math
from scipy.linalg import toeplitz
from numpy import linalg as LA
np.random.seed(1)
# create the highly collinear series
T = 100
p = 2
eps = 1e-8
m = 1000 # simulations
err = np.ones((m,2)) # accuracy of small eig value
for j in range(m):
u = np.random.rand(T,p)
X = np.ones(u.shape)
X[:,0] = u[:,0]
for i in range(1,p):
X[:,i] = eps*u[:,i]+u[:,0]
C = np.cov(np.transpose(X))
U, s, V = LA.svd(C)
w, v = LA.eig(C)
# true eigen values
te = eps**2/2 * np.var(u[:,1])*(1-np.corrcoef(u,rowvar=False)[0,1]**2)
err[j,0] = s[p-1] - te
err[j,1] = np.amin(w) - te
print('Cov: ',C)
print('SVDs: ',s)
print('eigen vals: ',w)
print('true small eigenvals: ',te)
acc = np.mean(np.abs(err),axis=0)
print("small eigenval, accuracy SVD, Eig: ",acc[0]/te,acc[1]/te)
Wydajność:
Cov: [[ 0.09189421 0.09189421]
[ 0.09189421 0.09189421]]
SVDs: [ 0.18378843 0. ]
eigen vals: [ 1.38777878e-17 1.83788428e-01]
true small eigenvals: 4.02633695086e-18
small eigenval, accuracy SVD, Eig: 2.43114702041 3.31970128319
Tutaj kod działa kod. Zamiast generować losową macierz kowariancji do testowania procedur, generuję macierz losowych danych z dwiema zmiennymi:
gdzie - niezależne jednolite zmienne losowe. Zatem macierz kowariancji to
gdzie - wariancje mundurów i współczynnik korelacji między im.u , v ( σ 2 1 σ 2 1
x1=ux2=u+εv
u,v(σ21σ21+ερσ1σ2σ21+ερσ1σ2σ21+2ερσ1σ2+ε2σ22σ2)
σ21,σ22,ρ
Jego najmniejsza wartość własna:
Mała wartość własna nie może być obliczona po prostu podłączając do formuły ze względu na ograniczoną precyzję, więc musisz go rozwinąć:
λ=12(σ22ε2−σ42ε4+4σ32ρσ1ε3+8σ22ρ2σ21ε2+8σ2ρσ31ε+4σ41−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−√+2σ2ρσ1ε+2σ21)
ελ≈σ22ε2(1−ρ2)/2
Wykonuję symulacje realizacji macierzy danych, obliczam wartości własne symulowanej macierzy kowariancji i otrzymuję błędy .λ j e j = λ - λ jj=1,…,mλ^jej=λ−λ^j