O @amoeba teve excelentes respostas às perguntas da PCA, incluindo esta em relação ao SVD e à PCA. Respondendo à sua pergunta exata, farei três pontos:
- matematicamente, não há diferença se você calcula o PCA diretamente na matriz de dados ou em sua matriz de covariância
- a diferença se deve exclusivamente à precisão e complexidade numéricas. A aplicação de SVD diretamente à matriz de dados é numericamente mais estável do que à matriz de covariância
- O SVD pode ser aplicado à matriz de covariância para executar PCA ou obter valores de eigen, na verdade, é o meu método favorito de resolver problemas de eigen
Acontece que o SVD é mais estável do que os procedimentos típicos de decomposição de autovalor, especialmente para aprendizado de máquina. No aprendizado de máquina, é fácil acabar com regressores altamente colineares. SVD funciona melhor nesses casos.
Aqui está o código Python para demonstrar o ponto. Criei uma matriz de dados altamente colinear, obtive sua matriz de covariância e tentei obter os valores próprios deste último. O SVD ainda está funcionando, enquanto a decomposição do eigen comum falha nesse caso.
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)
Saída:
Cov: [[ 0.08311516 0.08311516]
[ 0.08311516 0.08311516]]
SVDs: [ 1.66230312e-01 5.66687522e-18]
eigen vals: [ 0. 0.16623031]
Atualizar
Respondendo ao comentário de Federico Poloni, aqui está o código com testes de estabilidade de SVD vs Eig em 1000 amostras aleatórias da mesma matriz acima. Em muitos casos, Eig mostra 0 pequeno valor de eigen, o que levaria à singularidade da matriz, e o SVD não faz isso aqui. O SVD é cerca de duas vezes mais preciso em uma pequena determinação de valor próprio, que pode ou não ser importante, dependendo do seu problema.
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)
Saída:
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
Aqui codifique o código funciona. Em vez de gerar a matriz de covariância aleatória para testar as rotinas, estou gerando a matriz de dados aleatórios com duas variáveis:
onde - variáveis aleatórias uniformes independentes independentes. Portanto, a matriz de covariância é
que - variâncias dos uniformes e coeficiente de correlação entre eles.
x1=ux2=u+εv
u,v(σ21σ21+ερσ1σ2σ21+ερσ1σ2σ21+2ερσ1σ2+ε2σ22σ2)
σ21,σ22,ρ
Seu menor valor próprio:
O valor próprio pequeno não pode ser calculado simplesmente conectando o na fórmula devido à precisão limitada; portanto, você precisa expandi-lo por Taylor:
λ=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
Eu corro simulações das realizações da matriz de dados, calculo os autovalores da matriz de covariância simulada e obtenho os erros .λ j e j = λ - λ jj=1,…,mλ^jej=λ−λ^j