En sklearn.decomposition.PCA, ¿por qué los componentes son negativos?

Estoy intentando seguir junto con Abdi & Williams – Principal Component Analysis (2010) y crear componentes principales a través de SVD, usando numpy.linalg.svd .

Cuando muestro el atributo components_ de un PCA equipado con sklearn, son de la misma magnitud que los que he calculado manualmente, pero algunos (no todos) son de signo opuesto. ¿Qué está causando esto?

Actualización : mi respuesta (parcial) a continuación contiene información adicional.

Tomemos los siguientes datos de ejemplo:

 from pandas_datareader.data import DataReader as dr import numpy as np from sklearn.decomposition import PCA from sklearn.preprocessing import scale # sample data - shape (20, 3), each column standardized to N~(0,1) rates = scale(dr(['DGS5', 'DGS10', 'DGS30'], 'fred', start='2017-01-01', end='2017-02-01').pct_change().dropna()) # with sklearn PCA: pca = PCA().fit(rates) print(pca.components_) [[-0.58365629 -0.58614003 -0.56194768] [-0.43328092 -0.36048659 0.82602486] [-0.68674084 0.72559581 -0.04356302]] # compare to the manual method via SVD: u, s, Vh = np.linalg.svd(np.asmatrix(rates), full_matrices=False) print(Vh) [[ 0.58365629 0.58614003 0.56194768] [ 0.43328092 0.36048659 -0.82602486] [-0.68674084 0.72559581 -0.04356302]] # odd: some, but not all signs reversed print(np.isclose(Vh, -1 * pca.components_)) [[ True True True] [ True True True] [False False False]] 

Como se dio cuenta en su respuesta, los resultados de una descomposición de valor singular (SVD) no son únicos en términos de vectores singulares. De hecho, si la SVD de X es \ sum_1 ^ r \ s_i u_i v_i ^ \ top: introduzca la descripción de la imagen aquí

con el s_i ordenado en forma decreciente, entonces puede ver que puede cambiar el signo (es decir, “voltear”) de decir u_1 y v_1, los signos de menos se cancelarán, por lo que la fórmula aún se mantendrá.

Esto muestra que la SVD es única hasta un cambio en el signo en pares de vectores singulares izquierdo y derecho .

Dado que la PCA es solo una SVD de X (o una descomposición de valores propios de X ^ \ top X), no hay garantía de que no devuelva resultados diferentes en la misma X cada vez que se realice. Comprensiblemente, la implementación de scikit learn quiere evitar esto: garantizan que los vectores singulares izquierdo y derecho devueltos (almacenados en U y V) son siempre los mismos, al imponer (lo que es arbitrario) que el mayor coeficiente de u_i en valor absoluto es positivo .

Como puede ver leyendo la fuente : primero calculan U y V con linalg.svd() . Luego, para cada vector u_i (es decir, fila de U), si su elemento más grande en valor absoluto es positivo, no hacen nada. De lo contrario, cambian u_i a – u_i y el vector singular izquierdo correspondiente, v_i, a – v_i. Como se dijo anteriormente, esto no cambia la fórmula SVD ya que el signo menos se cancela. Sin embargo, ahora se garantiza que la U y la V devueltas después de este procesamiento son siempre las mismas, ya que se ha eliminado la indeterminación en el signo.

Con el PCA aquí en 3 dimensiones, básicamente encontrará iterativamente: 1) El eje de proyección 1D con la varianza máxima conservada 2) El eje de preservación de la varianza máxima perpendicular al uno en 1). El tercer eje es automáticamente el que es perpendicular a los dos primeros.

Los componentes_ se enumeran de acuerdo con la varianza explicada. Así que el primero explica la mayor variación, y así sucesivamente. Tenga en cuenta que, según la definición de la operación de PCA, mientras intenta encontrar el vector para la proyección en el primer paso, que maximiza la varianza conservada, el signo del vector no importa: Sea M su matriz de datos (en su caso) Con la forma de (20,3)). Sea v1 el vector para preservar la varianza máxima, cuando se proyectan los datos. Cuando selecciona -v1 en lugar de v1, obtiene la misma varianza. (Puedes ver esto). Luego, al seleccionar el segundo vector, sea v2 el que es perpendicular a v1 y conserva la varianza máxima. Nuevamente, seleccionar -v2 en lugar de v2 conservará la misma cantidad de variación. v3 luego se puede seleccionar como -v3 o v3. Aquí, lo único que importa es que v1, v2, v3 constituyen una base ortonormal, para los datos M. Los signos dependen principalmente de cómo el algoritmo resuelve el problema del vector propio que subyace en la operación de PCA. La descomposición de valores propios o las soluciones de EVD pueden diferir en los signos.

Después de algunas excavaciones, he aclarado algo, pero no toda, mi confusión sobre esto. Este problema ha sido cubierto en stats.stackexchange aquí . La respuesta matemática es que “PCA es una transformación matemática simple. Si cambia los signos de los componentes, no modificará la varianza contenida en el primer componente”. Sin embargo , en este caso (con sklearn.PCA ), la fuente de ambigüedad es mucho más específica: en la fuente ( línea 391 ) para PCA tiene:

 U, S, V = linalg.svd(X, full_matrices=False) # flip eigenvectors' sign to enforce deterministic output U, V = svd_flip(U, V) components_ = V 

svd_flip , a su vez, se define aquí . Pero, ¿por qué se están volteando las señales para “garantizar un resultado determinista “? No estoy seguro. ( U, S, V ya se han encontrado en este punto …). Entonces, si sklearn la implementación de sklearn no es incorrecta, no creo que sea tan intuitiva. Cualquier persona en finanzas que esté familiarizada con el concepto de un beta (coeficiente) sabrá que el primer componente principal es probablemente algo similar a un índice de mercado amplio. El problema es que la implementación de sklearn le dará fuertes cargas negativas en el primer componente principal.

Mi solución es una versión svd_flip que no implementa svd_flip . Es bastante básico en el sentido de que no tiene parámetros de sklearn como svd_solver , pero tiene una serie de métodos específicamente orientados a este propósito.

Este es un breve aviso para aquellos que se preocupan por el propósito y no por la parte de matemáticas.

Aunque el signo es opuesto para algunos de los componentes, eso no debe considerarse como un problema. De hecho, lo que sí nos importa (al menos a mi entender) son las direcciones de los ejes. Los componentes, en última instancia, son vectores que identifican estos ejes después de transformar los datos de entrada utilizando pca. Por lo tanto, no importa a qué dirección apunta cada componente, los nuevos ejes en los que se encuentran nuestros datos serán los mismos.