@amoeba tenía excelentes respuestas a las preguntas de PCA, incluida esta en relación con SVD a PCA. Respondiendo a su pregunta exacta, haré tres puntos:
- matemáticamente no hay diferencia si calcula PCA en la matriz de datos directamente o en su matriz de covarianza
- La diferencia se debe únicamente a la precisión numérica y la complejidad. Aplicar aplicando SVD directamente a la matriz de datos es numéricamente más estable que a la matriz de covarianza
- SVD se puede aplicar a la matriz de covarianza para realizar PCA u obtener valores propios, de hecho, es mi método favorito para resolver problemas propios.
Resulta que SVD es más estable que los procedimientos típicos de descomposición de valores propios, especialmente para el aprendizaje automático. En el aprendizaje automático, es fácil terminar con regresores altamente colineales. SVD funciona mejor en estos casos.
Aquí está el código de Python para demostrar el punto. Creé una matriz de datos altamente colineal, obtuve su matriz de covarianza y traté de obtener los valores propios de esta última. SVD todavía funciona, mientras que la descomposición del eigen ordinario falla en este 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)
Salida:
Cov: [[ 0.08311516 0.08311516]
[ 0.08311516 0.08311516]]
SVDs: [ 1.66230312e-01 5.66687522e-18]
eigen vals: [ 0. 0.16623031]
Actualizar
En respuesta al comentario de Federico Poloni, aquí está el código con pruebas de estabilidad de SVD vs Eig en 1000 muestras aleatorias de la misma matriz anterior. En muchos casos, Eig muestra 0 valor propio pequeño, lo que llevaría a la singularidad de la matriz, y SVD no lo hace aquí. La SVD es aproximadamente dos veces más precisa en una determinación de valor de eigen pequeño, que puede o no ser importante dependiendo de su 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)
Salida:
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