@amoeba tuvo excelentes respuestas a las preguntas del PCA, incluyendo este sobre la relación de SVD con PCA. Respondiendo a su pregunta exacta voy a hacer tres puntos:
- matemáticamente no hay diferencia si se calcula el ACP sobre la matriz de datos directamente o sobre su matriz de covarianza
- la diferencia se debe puramente a la precisión y complejidad numérica. Aplicar la SVD directamente a la matriz de datos es numéricamente más estable que a la matriz de covarianza
- La SVD puede aplicarse a la matriz de covarianza para realizar el PCA u obtener los valores propios, de hecho, es mi método favorito para resolver los problemas propios
Resulta que la 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 acabar con regresores muy colineales. La SVD funciona mejor en estos casos.
Aquí está el código de Python para demostrar el punto. He creado una matriz de datos altamente colineal, he obtenido su matriz de covarianza y he intentado obtener los valores propios de esta última. La SVD sigue funcionando, mientras que la descomposición eigen ordinaria 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)
La salida:
Cov: [[ 0.08311516 0.08311516]
[ 0.08311516 0.08311516]]
SVDs: [ 1.66230312e-01 5.66687522e-18]
eigen vals: [ 0. 0.16623031]
Actualización
Respondiendo al comentario de Federico Poloni, aquí está el código con la prueba 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í. SVD es aproximadamente dos veces más preciso en la determinación del valor propio 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)
La 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
Aquí el código funciona. En lugar de generar la matriz de covarianza aleatoria para probar las rutinas, estoy generando la matriz de datos aleatorios con dos variables: $$x_1=u\\ x_2=u+\varepsilon v$$ donde $u,v$ - variables aleatorias uniformes independientes. Así, la matriz de covarianza es $$\begin{pmatrix} \sigma_1^2 & \sigma_1^2 + \varepsilon \rho \sigma_1 \sigma_2\\ \sigma_1^2 + \varepsilon \rho \sigma_1 \sigma_2 & \sigma_1^2 + 2 \varepsilon \rho \sigma_1 \sigma_2 + \varepsilon^2 \sigma_2^2\sigma^2\end{pmatrix}$$ donde $\sigma_1^2,\sigma_2^2,\rho$ - varianzas de los uniformes y el coeficiente de correlación entre ellos.
Su valor propio más pequeño: $$\lambda= \frac 1 2 \left(\sigma_2^2 \varepsilon^2 - \sqrt{\sigma_2^4 \varepsilon^4 + 4 \sigma_2^3 \rho \sigma_1 \varepsilon^3 + 8 \sigma_2^2 \rho^2 \sigma_1^2 \varepsilon^2 + 8 \sigma_2 \rho \sigma_1^3 \varepsilon + 4 \sigma_1^4} + 2 \sigma_2 \rho \sigma_1 \varepsilon + 2 \sigma_1^2\right)$$ El valor propio pequeño no se puede calcular simplemente enchufando el $\varepsilon$ en la fórmula debido a su limitada precisión, por lo que es necesario que Taylor la amplíe: $$\lambda\approx \sigma_2^2 \varepsilon^2 (1-\rho^2)/2$$
Corro $j=1,\dots,m$ simulaciones de las realizaciones de la matriz de datos, calcular los valores propios de la matriz de covarianza simulada $\hat\lambda_j$ y obtener los errores $e_j=\lambda-\hat\lambda_j$ .
14 votos
Para las matrices semidefinidas positivas simétricas cuadradas (como la matriz de covarianza), las descomposiciones de valores propios y de valores singulares son exactamente las mismas.
9 votos
Quiero decir que son matemáticamente lo mismo. Numéricamente efectivamente podrían utilizar algoritmos diferentes y uno podría ser más estable que otro (como dice Ng). Sería interesante saber más sobre esto, +1.
5 votos
Aquí encontrará información al respecto: de.mathworks.com/matlabcentral/newsreader/view_thread/21268 . Pero ten en cuenta que cualquier explicación sobre por qué un algoritmo sería más estable que otro va a ser muy técnica.
2 votos
En Matlab
x=randn(10000); x=x'*x; tic; eig(x); toc; tic; svd(x); toc;
en mi máquina produce 12s para eig() y 26s para svd(). Si es mucho más lento, ¡al menos debe ser más estable! :-)5 votos
Eso podría estar basado en una comprensión incorrecta: hacer una SVD de la matriz de datos es más estable que el uso de
eig
osvd
en la matriz de covarianza, pero hasta donde yo sé no hay gran diferencia entre usareig
osvd
en la matriz de covarianza --- ambos son algoritmos estables hacia atrás. En todo caso, yo apostaría por que eig es más estable, ya que realiza menos cálculos (suponiendo que ambos se implementen con algoritmos de última generación).1 votos
@FedericoPoloni En realidad, creo que A. Ng puede tener razón. Aparentemente, la implementación de LAPACK del SVD utiliza un enfoque de dividir y conquistar como se describe aquí: es.wikipedia.org/wiki/Divide-and-conquer_eigenvalue_algorithm a diferencia del algoritmo QR convencional utilizado por el código de eigendecomposición. El primero parece ser más estable, aunque no he leído una explicación detallada de por qué.
0 votos
@FedericoPoloni He encontrado un documento relevante. Mira mi respuesta actualizada si te interesa.
1 votos
TL;DR La respuesta está en su pregunta: SVD es mucho más estable numéricamente que EIG
2 votos
Como mencionó @amoeba en los comentarios, la pregunta no es sobre la comparación de la SVD en los datos con la descomposición eigen en la matriz de covarianza, es sobre por qué se utilizó la SVD en la matriz de "covarianza".
0 votos
@broncoAbierto Interesante no conocía estos detalles, en particular que se utilizan dos algoritmos diferentes para estas dos tareas en Lapack actualmente.
0 votos
A veces obtengo diferentes descomposiciones. Por ejemplo
C = [ 0.236553 -0.020460 0.029987;-0.020460 0.366393 0.018122;0.029987 0.018122 0.330042]
y en Octava, obtengo un reflejo con[Ve,D] = eig(C)
y una rotación con[U,S,V] = svd(C)
. ¿Cómo se hace cuando necesito una rotación para visualizar el elipsoide de confianza?