@amoeba отримав відмінні відповіді на питання PCA, включаючи цю щодо відношення SVD до PCA. Відповідаючи на ваше точне запитання, я зазначу три моменти:
- математично немає різниці, чи обчислюєте ви PCA на матриці даних безпосередньо чи на її коваріаційній матриці
- різниця обумовлена чисельною точністю і складністю. Застосування застосування SVD безпосередньо до матриці даних чисельно стабільніше, ніж до матриці коваріації
- SVD можна застосувати до матриці коваріації для виконання PCA або отримання власних значень, насправді це мій улюблений метод вирішення власних задач
Виявляється, SVD більш стійкий, ніж типові процедури декомпозиції власного значення, особливо для машинного навчання. У машинному навчанні легко покінчити з висококолинеарними регресорами. SVD працює в цих випадках краще.
Ось код Python для демонстрації суті. Я створив високолінійну матрицю даних, отримав її коваріаційну матрицю і спробував отримати власні значення останньої. SVD все ще працює, в той час як звичайне розкладання власних властивостей не вдається.
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)
Вихід:
Cov: [[ 0.08311516 0.08311516]
[ 0.08311516 0.08311516]]
SVDs: [ 1.66230312e-01 5.66687522e-18]
eigen vals: [ 0. 0.16623031]
Оновлення
Відповідаючи на коментар Федеріко Полоні, ось код із тестуванням на стабільність SVD проти Eig на 1000 випадкових вибірках тієї ж матриці вище. У багатьох випадках Eig показує 0 малих власних значень, що призведе до сингулярності матриці, і SVD цього не робить. SVD приблизно вдвічі точніший при визначенні невеликого значення власного значення, що може бути або не бути важливим залежно від вашої проблеми.
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)
Вихід:
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
x1=ux2=u+εv
u,v(σ21σ21+ερσ1σ2σ21+ερσ1σ2σ21+2ερσ1σ2+ε2σ22σ2)
σ21,σ22,ρ
λ=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
j=1,…,mλ^jej=λ−λ^j