¿Por qué el Lik scikit-learn de Python no funciona correctamente y cómo calcula LDA a través de SVD?

26

Estaba utilizando el Análisis discriminante lineal (LDA) de la scikit-learnbiblioteca de aprendizaje automático (Python) para la reducción de la dimensionalidad y tenía un poco de curiosidad por los resultados. Ahora me pregunto qué scikit-learnestá haciendo la LDA para que los resultados se vean diferentes de, por ejemplo, un enfoque manual o una LDA realizada en R. Sería genial si alguien pudiera darme algunas ideas aquí.

Lo que básicamente es más preocupante es que scikit-plotmuestra una correlación entre las dos variables donde debería haber una correlación 0.

Para una prueba, utilicé el conjunto de datos de Iris y los primeros 2 discriminantes lineales se veían así:

IMG-1. LDA a través de scikit-learn

ingrese la descripción de la imagen aquí

Esto es básicamente consistente con los resultados que encontré en la documentación de scikit-learn aquí.

Ahora, revisé el LDA paso a paso y obtuve una proyección diferente. Intenté diferentes enfoques para averiguar qué estaba pasando:

IMG-2. LDA en datos sin procesar (sin centrado, sin estandarización)

ingrese la descripción de la imagen aquí

Y aquí estaría el enfoque paso a paso si estandarizara (normalización del puntaje z; varianza de la unidad) los datos primero. Hice lo mismo solo con centrado en la media, lo que debería conducir a la misma imagen de proyección relativa (y que de hecho lo hizo).

IMG-3. LDA paso a paso después del centrado medio o estandarización

ingrese la descripción de la imagen aquí

IMG-4. LDA en R (configuración predeterminada)

LDA en IMG-3, donde centré los datos (que sería el enfoque preferido) también se ve exactamente igual al que encontré en una publicación de alguien que hizo el LDA en R ingrese la descripción de la imagen aquí


Código de referencia

No quería pegar todo el código aquí, pero lo he subido como un cuaderno de IPython desglosado en varios pasos que utilicé (ver más abajo) para la proyección LDA.

  1. Paso 1: Calcular los vectores medios d-dimensionales
    mi=1nixDinxk
  2. Paso 2: Calcular las matrices de dispersión

    2.1 La matriz de dispersión dentro de clase se calcula mediante la siguiente ecuación:SW

    SW=i=1cSi=i=1cxDin(xmi)(xmi)T

    2.2 La matriz de dispersión entre clases se calcula mediante la siguiente ecuación: donde es la media general.SB

    SB=i=1cni(mim)(mim)T
    m
  3. Paso 3. Resolver el problema de valor propio generalizado para la matrizSW1SB

    3.1. Ordenar los vectores propios disminuyendo los valores propios

    3.2. Elección de k vectores propios con los valores propios más grandes. Combinando los dos vectores propios con los valores propios más altos para construir nuestra matriz de vectores propios -dimensionald×kW

  4. Paso 5: Transformando las muestras en el nuevo subespacio

    y=WT×x.
ameba dice Reinstate Monica
fuente
No he revisado las diferencias, pero puedes ver exactamente lo que hace scikit-learn en la fuente .
Dougal
Parece que también están estandarizando (centrando y luego escalando a través de la división por la desviación estándar). Esto, esperaría un resultado similar al de mi tercera trama (y la trama R) ... hmm
Extraño: el argumento que obtuviste con scikit (y el que muestran en su documentación) no tiene sentido. LDA siempre produce proyecciones que tienen correlación cero, pero obviamente hay una correlación muy fuerte entre las proyecciones de scikit en los ejes discriminantes 1 y 2. Algo está claramente mal allí.
ameba dice Reinstate Monica
@ameoba Sí, yo también lo creo. Lo que también es extraño es que la misma trama que estoy mostrando para scikit está en la documentación de ejemplo: scikit-learn.org/stable/auto_examples/decomposition/... Eso me hace pensar que mi uso de scikit es correcto, pero que hay algo extraño sobre la función LDA
@SebastianRaschka: Sí, lo noté. Es raro de hecho. Sin embargo, tenga en cuenta que la primera de sus propias gráficas LDA (no scikit) también muestra una correlación distinta de cero y, por lo tanto, algo debe estar mal también. ¿Centraste los datos? La proyección en el segundo eje no parece tener media cero.
ameba dice Reinstate Monica

Respuestas:

20

Actualización: Gracias a esta discusión, scikit-learnse actualizó y ahora funciona correctamente. Su código fuente LDA se puede encontrar aquí . El problema original se debió a un error menor (vea esta discusión de github ) y mi respuesta en realidad no apuntaba correctamente (disculpas por cualquier confusión causada). Como todo eso ya no importa (el error está solucionado), edité mi respuesta para centrarme en cómo se puede resolver LDA a través de SVD, que es el algoritmo predeterminado en scikit-learn.


Después de definir las matrices de dispersión dentro y entre las clases y , el cálculo estándar de LDA, como se señala en su pregunta, es tomar vectores propios de como ejes discriminantes ( ver, por ejemplo, aquí ). Sin embargo, los mismos ejes se pueden calcular de una manera ligeramente diferente, explotando una matriz de blanqueamiento:ΣWΣBΣW1ΣB

  1. Calcule . Esta es una transformación de blanqueamiento con respecto a la covarianza agrupada dentro de la clase (vea mi respuesta vinculada para más detalles).ΣW1/2

    Tenga en cuenta que si tiene descomposición propia , entonces . Tenga en cuenta también que uno calcula lo mismo haciendo SVD de datos agrupados dentro de la clase: .ΣW=USUΣW1/2=US1/2UXW=ULVΣW1/2=UL1U

  2. Encuentre vectores propios de , llamémoslos .ΣW1/2ΣBΣW1/2A

    Una vez más, tenga en cuenta que se puede calcular haciendo SVD de datos entre clases , transformado con , es decir, datos entre clases blanqueados con respecto a dentro de la clase covarianzaXBΣW1/2

  3. Los ejes discriminantes estarán dados por , es decir, por los ejes principales de los datos transformados , transformados nuevamente .AΣW1/2A

    De hecho, si es un vector propio de la matriz anterior, entonces y multiplicando desde la izquierda por y definiendo , obtenemos inmediatamente :a

    ΣW1/2ΣBΣW1/2a=λa,
    ΣW1/2a=ΣW1/2a
    ΣW1ΣBa=λa.

En resumen, LDA es equivalente a blanquear la matriz de los medios de clase con respecto a la covarianza dentro de la clase, hacer PCA en los medios de clase y volver a transformar los ejes principales resultantes en el espacio original (sin blanquear).

Esto se señala, por ejemplo, en Los elementos del aprendizaje estadístico , sección 4.3.3. En scikit-learnesta es la forma predeterminada para calcular LDA porque SVD de una matriz de datos es numéricamente más estable que eigen-descomposición de su matriz de covarianza.

Tenga en cuenta que uno puede usar cualquier transformación de blanqueamiento en lugar de y todo seguirá funcionando exactamente igual. En se usa (en lugar de ), y funciona bien (al contrario de lo que se escribió originalmente en mi respuesta). L - 1 UU L - 1 UΣW1/2scikit-learn L1UUL1U

ameba dice Reinstate Monica
fuente
1
Gracias por esta buena respuesta. Le agradezco que se haya tomado el tiempo para escribirlo tan bien. Tal vez podría mencionarlo en la discusión sobre GitHub; Estoy seguro de que sería útil para arreglar el LDA en la próxima versión de sci-kit
@SebastianRaschka: No tengo una cuenta en GitHub. Pero si lo desea, puede darle un enlace a este hilo.
ameba dice Reinstate Monica
@amoeba: Los libros de texto generalmente describen la LDA como lo hizo usted: una descomposición de valor propio de . Curiosamente, una serie de implementaciones de LDA que conozco adoptan un enfoque diferente. Sus ejes son los vectores para los medios de clase transformados con . Su solución LDA es una base ortonormal de estos vectores. El LDA de Scikit-learn da los mismos resultados que estas implementaciones, por lo que no creo que haya un error. Σ - 1 WΣW1ΣBΣW1
kazemakase
2
@kazemakase: Bueno, por supuesto, si solo hay dos clases, entonces tiene rango 1, y todo se simplifica mucho, ya que el único vector propio de viene dado por , donde son medios de clase. ¿Supongo que eso es lo que quisiste decir antes? Esto está muy bien cubierto, por ejemplo, en el libro de texto Bishop's ML, sección 4.1.4. Pero la generalización a más clases requiere un análisis propio (Ibid., 4.1.6). Además, el código de scikit (que estamos hablando aquí!) Hace uso SVD, el doble realidad. Σ - 1 W Σ B Σ - 1 W ( μ 1 - μ 2 ) μ iΣBΣW1ΣBΣW1(μ1μ2)μi
ameba dice Reinstate Monica
3

Solo para cerrar esta pregunta, el problema discutido con la LDA se ha solucionado en scikit-learn 0.15.2 .


fuente