Eu tenho 65 amostras de dados 21-dimensionais (colados aqui ) e estou construindo a matriz de covariância a partir dele. Quando computado em C ++, recebo a matriz de covariância colada aqui . E quando computado no Matlab a partir dos dados (como mostrado abaixo), recebo a matriz de covariância colada aqui
Código Matlab para computação cov a partir de dados:
data = csvread('path/to/data');
matlab_cov = cov(data);
Como você pode ver, as diferenças nas matrizes de covariância são mínimas (~ e-07), o que provavelmente se deve a problemas numéricos no compilador usando aritmética de ponto flutuante.
No entanto, quando computo a matriz de covariância pseudo-inversa da matriz de covariância produzida pelo matlab e a produzida pelo meu código C ++, obtenho resultados amplamente diferentes. Estou computando-os da mesma maneira, ou seja:
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);
A diferença é tão grande que, quando estou computando a distância dos mahalanobis de uma amostra (colada aqui ) até a distribuição das 65 amostras por:
usando as diferentes matrizes de covariância inversa ( ), obtenho resultados amplamente diferentes, ou seja:
(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
É normal que as pequenas diferenças (e-7) na matriz de covariância tenham esse efeito no cálculo da matriz pseudo-inversa? E se sim, o que posso fazer para mitigar esse efeito?
Caso contrário, existem outras métricas de distância que eu possa usar que não envolvam a covariância inversa? Eu uso a distância de Mahalanobis como sabemos para n amostras segue uma distribuição beta, que eu uso para testes de hipóteses
Muito obrigado antecipadamente
EDIT: Adicionando código C ++ para calcular a matriz de covariância abaixo:
O vector<vector<double> >
representa a coleção de linhas do arquivo colado.
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;
}
}
Respostas:
As matrizes que você deseja inverter não são matrizes de covariâncias "válidas" porque não são definidas positivamente; numericamente, eles ainda têm alguns autovalores negativos (mas próximos de zero). Provavelmente devido a zeros da máquina, por exemplo, o último valor próprio da matriz "matlab_covariance" é -0,000000016313723. Para corrigir a definição positiva, você pode fazer duas coisas:
Uma matriz não negativa não tem um inverso, mas possui um pseudo inverso (todas as matrizes com entradas reais ou complexas têm um pseudo inverso); no entanto, o pseudo inverso de Moore – Penrose é mais computacionalmente caro do que um inverso verdadeiro e, se o inverso existe, é igual ao pseudo-inverso. Então, basta ir para o inverso :)
Ambos os métodos praticamente tentam manipular os valores próprios que são avaliados como zero (ou abaixo de zero). O primeiro método é um pouco ondulado, mas provavelmente muito mais rápido de implementar. Para algo um pouco mais estável, convém calcular o SVD e definir o igual ao absoluto do menor valor próprio (para que você não seja negativo) mais algo muito pequeno (para que seja positivo). Apenas tome cuidado para não impor positividade a uma matriz que é obviamente negativa (ou já positiva). Ambos os métodos alterarão o número de condicionamento da sua matriz.λ
Em termos estatísticos, o que você faz adicionando na diagonal de sua matriz de covariância adiciona ruído às suas medições. (Como a diagonal da matriz de covariância é a variação de cada ponto e, ao adicionar algo a esses valores, você apenas diz "a variação nos pontos em que tenho leituras é realmente um pouco maior do que eu pensava originalmente".)λ
Um teste rápido para a definição positiva de uma matriz é a existência (ou não) da decomposição de Cholesky.
Também como uma nota computacional:
EDIT: Dado que você tem uma decomposição de Cholesky da sua matriz tal forma que (você precisa fazer isso para verificar se está tendo uma matriz Pos.Def.), Você poderá resolver imediatamente o sistema . Você apenas resolve Ly = b para y por substituição direta e, em seguida, L ^ Tx = y para x por substituição inversa. (Em si, basta usar o método .solve (x) do seu objeto Cholesky) Obrigado a bnaul e Zen por apontar que eu me concentrei tanto em obter o ser Pos.Def. que eu esqueci por que nos importamos com isso em primeiro lugar :)K LLT Kx=b K
fonte
As respostas e comentários postados são bons pontos sobre os perigos da inversão de matrizes quase singulares. No entanto, até onde eu sei, ninguém mencionou que calcular a distância de Mahalanobis não requer, na verdade, inverter a covariância da amostra. Consulte esta pergunta StackOverflow para obter uma descrição de como fazer isso usando a decomposição da .LU
O princípio é o mesmo que resolver um sistema linear: ao tentar resolver modo que , existem métodos muito mais eficientes e numericamente estáveis do que usar .x Ax=b x=A−1b
Edit: provavelmente escusado será dizer, mas esse método produz o valor exato da distância, enquanto a adição de a e a inversão produz apenas uma aproximação.λI S
fonte
LU
decomposição também não funcionará. Vou adicionar um comentário sobre isso na minha resposta.(Anos depois), um pequeno exemplo: com deficiente em classificação, autovalores de serão de 0 a precisão da máquina - e cerca de metade desses "zeros" pode ser :r < n , n - r A T A < 0A r<n, n−r ATA <0
fonte