Tengo 65 muestras de datos de 21 dimensiones (pegados aquí ) y estoy construyendo la matriz de covarianza a partir de ellos. Cuando se calcula en C ++, obtengo la matriz de covarianza pegada aquí . Y cuando se calcula en matlab a partir de los datos (como se muestra a continuación) obtengo la matriz de covarianza pegada aquí
Código de Matlab para calcular cov a partir de datos:
data = csvread('path/to/data');
matlab_cov = cov(data);
Como puede ver, la diferencia en las matrices de covarianza es minuto (~ e-07), lo que probablemente se deba a problemas numéricos en el compilador que usa aritmética de coma flotante.
Sin embargo, cuando calculo la matriz de covarianza pseudo-inversa de la matriz de covarianza producida por matlab y la producida por mi código C ++, obtengo resultados muy diferentes. Los estoy computando de la misma manera, es decir:
data = csvread('path/to/data');
matlab_cov = cov(data);
my_cov = csvread('path/to/cov_file');
matlab_inv = pinv(matlab_cov);
my_inv = pinv(my_cov);
La diferencia es tan grande que cuando calculo la distancia mahalanobis de una muestra (pegada aquí ) a la distribución de las 65 muestras por:
usando las diferentes matrices de covarianza inversa ( ) obtengo resultados muy diferentes, es decir:
(65/(64^2))*((sample-sample_mean)*my_inv*(sample-sample_mean)')
ans =
1.0167e+05
(65/(64^2))*((sample-sample_mean)*matlab_inv*(sample-sample_mean)')
ans =
109.9612
¿Es normal que las pequeñas diferencias (e-7) en la matriz de covarianza tengan tal efecto en el cálculo de la matriz pseudo-inversa? Y si es así, ¿qué puedo hacer para mitigar este efecto?
De lo contrario, ¿hay alguna otra métrica de distancia que pueda usar que no implique la covarianza inversa? Utilizo la distancia de Mahalanobis como sabemos para n muestras, sigue una distribución beta, que utilizo para la prueba de hipótesis
Muchas gracias de antemano
EDIT: Adición de código C ++ para el cálculo de matriz de covarianza a continuación:
El vector<vector<double> >
representa la colección de filas desde el archivo de pegado.
Mat covariance_matrix = Mat(21, 21, CV_32FC1, cv::Scalar(0));
for(int j = 0; j < 21; j++){
for(int k = 0; k < 21; k++){
for(std::vector<vector<double> >::iterator it = data.begin(); it!= data.end(); it++){
covariance_matrix.at<float>(j,k) += (it->at(j) - mean.at(j)) * (it->at(k) - mean[k]);
}
covariance_matrix.at<float>(j,k) /= 64;
}
}
Respuestas:
Las matrices que está buscando invertir no son matrices de covarianzas "válidas" porque no son definitivas positivas; numéricamente incluso tienen algunos valores propios que son negativos (pero cercanos a cero). Esto probablemente se deba a ceros de máquina, por ejemplo, el último valor propio de su matriz "matlab_covarnce" es -0.000000016313723. Para corregir a definitivo positivo, puede hacer dos cosas:
Una matriz no negativa no tiene un inverso, pero tiene un pseudo inverso (todas las matrices con entradas reales o complejas tienen un pseudo inverso), sin embargo, el pseudo inverso de Moore-Penrose es más costoso desde el punto de vista computacional que un inverso verdadero y si lo inverso existe es igual a lo pseudoinverso. Así que solo ve por el inverso :)
Ambos métodos prácticamente intentan manejar los valores propios que se evalúan a cero (o por debajo de cero). El primer método es un poco ondulado pero probablemente mucho más rápido de implementar. Para algo un poco más estable, es posible que desee calcular el SVD y luego configurarλ igual al absoluto del valor propio más pequeño (para que no sea negativo) más algo muy pequeño (para que sea positivo). Solo tenga cuidado de no imponer positividad a una matriz que es obviamente negativa (o ya positiva). Ambos métodos alterarán el número de acondicionamiento de su matriz.
En términos estadísticos, qué haces al agregar queλ través de la diagonal de su matriz de covarianza agrega ruido a sus mediciones. (Debido a que la diagonal de la matriz de covarianza es la varianza de cada punto y al agregar algo a esos valores, simplemente dice "la varianza en los puntos para los que tengo lecturas es en realidad un poco más grande de lo que pensé originalmente").
Una prueba rápida para la definición positiva de una matriz es la existencia (o no) de la descomposición de Cholesky.
También como una nota computacional:
EDITAR: dado que tiene una descomposición Cholesky de su matriz manera que (debe hacer eso para verificar que tiene una matriz Pos.Def.), Debería poder resolver inmediatamente el sistema . Simplemente resuelve Ly = b para y mediante sustitución hacia adelante, y luego L ^ Tx = y para x mediante sustitución hacia atrás. (En eigen simplemente use el método .solve (x) de su objeto Cholesky) Gracias a bnaul y Zen por señalar que me enfoqué tanto en hacer que sea Pos.Def. que olvidé por qué nos preocupamos por eso en primer lugar :)K LLT Kx=b K
fuente
Las respuestas publicadas y los comentarios tienen buenos puntos sobre los peligros de invertir matrices casi singulares. Sin embargo, hasta donde puedo decir, nadie ha mencionado que calcular la distancia de Mahalanobis en realidad no requiere invertir la covarianza de la muestra. Consulte esta pregunta de StackOverflow para obtener una descripción de cómo hacerlo utilizando la descomposición de .LU
El principio es el mismo que resolver un sistema lineal: cuando se trata de resolver para tal que , existen métodos mucho más eficientes y numéricamente estables que tomar .x Ax=b x=A−1b
Editar: probablemente no hace falta decirlo, pero este método produce el valor exacto de la distancia, mientras que al agregar a e invertir solo se obtiene una aproximación.λI S
fuente
LU
descomposición tampoco funcionará. Agregaré un comentario sobre esto en mi respuesta.(Años después) un pequeño ejemplo: con rango deficiente, valores propios de serán 0 dentro de la precisión de la máquina - y aproximadamente la mitad de estos "ceros" pueden ser :A r<n, n−r ATA <0
fuente