Tính không ổn định của phép tính ma trận hiệp phương sai


8

Tôi có 65 mẫu dữ liệu 21 chiều (được dán ở đây ) và tôi đang xây dựng ma trận hiệp phương sai từ nó. Khi được tính trong C ++, tôi nhận được ma trận hiệp phương sai được dán ở đây . Và khi được tính toán trong MATLAB từ dữ liệu (như được hiển thị bên dưới), tôi nhận được ma trận hiệp phương sai được dán ở đây

Mã Matlab để tính toán cov từ dữ liệu:

data = csvread('path/to/data');
matlab_cov = cov(data);

Như bạn có thể thấy sự khác biệt trong ma trận hiệp phương sai là phút (~ e-07), điều này có thể là do các vấn đề về số trong trình biên dịch sử dụng số học dấu phẩy động.

Tuy nhiên, khi tôi tính ma trận hiệp phương sai giả từ ma trận hiệp phương sai được tạo bởi matlab và ma trận được tạo bởi mã C ++ của tôi, tôi nhận được kết quả rất khác nhau. Tôi đang tính toán chúng theo cùng một cách tức là:

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);

Sự khác biệt là rất lớn đến nỗi khi tôi tính toán khoảng cách mahalanobis từ một mẫu (dán ở đây ) đến phân phối của 65 mẫu bằng cách:

(65/642)×((samplemean)×1×(samplemean))

sử dụng các ma trận hiệp phương sai khác nhau ( ) Tôi nhận được các kết quả khác nhau rộng rãi, ví dụ:1

 (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

Có phải bình thường đối với sự khác biệt nhỏ (e-7) trong ma trận hiệp phương sai có ảnh hưởng như vậy đến việc tính toán ma trận nghịch đảo giả? Và nếu vậy, tôi có thể làm gì để giảm thiểu hiệu ứng này?

Không thực hiện được điều này, có bất kỳ số liệu khoảng cách nào khác mà tôi có thể sử dụng không liên quan đến hiệp phương sai không? Tôi sử dụng khoảng cách Mahalanobis như chúng ta biết đối với n mẫu nó tuân theo phân phối beta, mà tôi sử dụng để kiểm tra giả thuyết

Rất cám ơn trước

EDIT: Thêm mã C ++ để tính ma trận hiệp phương sai bên dưới: Biểu vector<vector<double> >diễn tập hợp các hàng từ tệp được dán.

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; 
        }
    }

Đảo ngược ma trận ..... Đó là một điều nguy hiểm! Thông thường, tốt hơn là tìm giải pháp thay thế cho điều đó (ví dụ như giả)
Ander Biguri

1
@Aly: các ma trận bạn đang tìm cách đảo ngược không phải là ma trận hiệp phương sai "hợp lệ" vì chúng không xác định dương; về mặt số lượng, chúng thậm chí có một số giá trị riêng âm (nhưng gần bằng 0). Tôi có lẽ sẽ chỉ cần thêm một số hằng số rất nhỏ dọc theo đường chéo; đó là một hình thức chỉnh sửa Tikhonov thực sự ( ). Cũng không sử dụng phao, sử dụng gấp đôi để lưu trữ ma trận hiệp phương sai của bạn. (Và ngoài việc bạn đã sử dụng OpenCV, bạn cũng có thể sử dụng Eigen hoặc Armadillo ..)Χ+λI
usεr11852

1
@Aly: Wikipedia, thật đấy. (đó là bổ đề: Chính quy Tikhonov). Phương thức mà whuber đã đề cập bằng cách sử dụng SVD sẽ cung cấp cho bạn một ma trận xác định không âm nếu bạn đặt các giá trị riêng nhỏ thành 0; bạn vẫn sẽ cần thêm một số hằng số nhỏ vào tất cả các giá trị riêng để làm cho chúng xác định dương. Thực tế cả hai phương pháp đều làm như nhau. Chỉ cần tôi viện đến việc không sử dụng SVD mà ảnh hưởng trực tiếp đến các giá trị bản địa mẫu bằng cách thêm vào tất cả chúng. Tôi không bắt gặp bất kỳ tài liệu tham khảo nào, cả hai phương pháp đều khá trực quan. λ
usεr11852

1
@ user11852 Xin vui lòng cho ý kiến ​​của bạn một câu trả lời, tôi vẫn đang thử nghiệm, nhưng nếu hứa sẽ chấp nhận. Ngoài ra, nếu những người khác đưa ra câu trả lời gợi ý của họ, tôi sẽ bỏ phiếu vì chúng rất hữu ích / hữu ích cho sự hiểu biết của tôi về vấn đề này
Aly

1
Tôi đã nhận xét trong luồng khác của bạn rằng có các biến có tổng bằng 1 , như tập dữ liệu của bạn, khuyến khích sự không ổn định và chứa biến dự phòng. Hãy thử thả một cột. Bạn thậm chí không cần pinv: ma trận hiệp phương sai không còn là số ít.
Cam.Davidson.Pilon

Câu trả lời:


7

Các ma trận bạn đang tìm cách đảo ngược không phải là ma trận hiệp phương sai "hợp lệ" vì chúng không xác định dương; về mặt số lượng, chúng thậm chí có một số giá trị riêng âm (nhưng gần bằng 0). Điều này rất có thể là do số không của máy, ví dụ giá trị riêng cuối cùng của ma trận "matlab_covariance" của bạn là -0.000000016313723. Để sửa thành tích cực, bạn có thể làm hai việc:

  1. Chỉ cần thêm một số hằng số rất nhỏ dọc theo đường chéo; một hình thức chỉnh sửa Tikhonov thực sự ( với ).Χ+λIλ0
  2. (Dựa trên những gì whuber đề xuất) Sử dụng SVD, đặt giá trị riêng "có vấn đề" thành một giá trị nhỏ cố định (không phải bằng 0), xây dựng lại ma trận hiệp phương sai của bạn và sau đó đảo ngược điều đó. Rõ ràng nếu bạn đặt một số giá trị riêng đó thành 0, bạn sẽ kết thúc với ma trận không âm (hoặc bán tích cực), điều đó sẽ không thể đảo ngược được.

Một ma trận không âm không có nghịch đảo nhưng nó có nghịch đảo giả (tất cả các ma trận với các mục thực hoặc phức đều có nghịch đảo giả), tuy nhiên nghịch đảo Moore Pen Penrose lại nghịch đảo đắt hơn so với nghịch đảo thực và nghịch đảo tồn tại nó bằng nghịch đảo giả. Vì vậy, chỉ cần đi cho nghịch đảo :)

Cả hai phương pháp trên thực tế đều cố gắng xử lý các giá trị riêng có giá trị bằng 0 (hoặc dưới 0). Phương pháp đầu tiên là bit lượn sóng bằng tay nhưng có lẽ nhanh hơn nhiều để thực hiện. Đối với một cái gì đó ổn định hơn một chút, bạn có thể muốn tính toán SVD và sau đó đặt bằng tuyệt đối với giá trị riêng nhỏ nhất (để bạn không âm) cộng với thứ gì đó rất nhỏ (để bạn có giá trị dương). Chỉ cần cẩn thận để không thực thi tính tích cực đối với một ma trận rõ ràng là tiêu cực (hoặc đã tích cực). Cả hai phương pháp sẽ thay đổi số điều hòa của ma trận của bạn.λ

Theo thuật ngữ thống kê những gì bạn làm bằng cách thêm qua đường chéo của ma trận hiệp phương sai của bạn, bạn thêm nhiễu vào các phép đo của mình. (Bởi vì đường chéo của ma trận hiệp phương sai là phương sai của từng điểm và bằng cách thêm một cái gì đó vào các giá trị đó, bạn chỉ cần nói "phương sai tại các điểm tôi đã đọc thực sự lớn hơn một chút so với những gì tôi nghĩ ban đầu".)λ

Một thử nghiệm nhanh cho tính xác định dương của ma trận là sự tồn tại (hoặc không) của phân rã Cholesky của nó.

Cũng như một ghi chú tính toán:

  1. Không sử dụng phao, sử dụng gấp đôi để lưu trữ ma trận hiệp phương sai của bạn.
  2. Sử dụng các thư viện đại số tuyến tính số trong C ++ (như Eigen hoặc Armadillo) để có được nghịch đảo của ma trận, các sản phẩm ma trận, v.v ... Nó nhanh hơn, an toàn hơn và ngắn gọn hơn.

EDIT: Do bạn có phân tách Cholesky của ma trận sao cho (bạn phải làm điều đó để kiểm tra xem bạn có ma trận Pos.Def.) Bạn có thể giải quyết ngay hệ thống . Bạn chỉ cần giải Ly = b cho y bằng cách thay thế về phía trước, và sau đó L ^ Tx = y cho x bằng cách thay thế trở lại. (Trong eigen, chỉ sử dụng phương thức .solve (x) của đối tượng Cholesky của bạn) Cảm ơn bnaul và Zen đã chỉ ra rằng tôi tập trung rất nhiều vào việc lấy là Pos.Def. rằng tôi đã quên tại sao chúng ta quan tâm đến điều đó ngay từ đầu :)KLLTKx=bK


3
+1. Sử dụng Mathicala và áp dụng nó vào dữ liệu (thay vì ma trận hiệp phương sai được đăng, có thể được trình bày với độ chính xác quá ít) Tôi thấy không có giá trị riêng âm. Đó là như nó phải là: khi một ma trận hiệp phương sai được tính toán chính xác, nó được đảm bảo bán xác định dương, do đó, bất kỳ giá trị riêng âm nào phải được quy cho sự thiếu chính xác trong các tính toán. Bất kỳ thủ tục nghịch đảo tổng quát nào cũng phải "nhận ra" các giá trị âm nhỏ đó là số không và xử lý chúng theo đó.
whuber

Cảm ơn mọi người vì những nỗ lực, như đã nêu tôi đã bỏ phiếu và sẽ thử những điều này và nhận xét hoặc chấp nhận cho phù hợp.
Aly

Xin lỗi, tôi hơi bối rối, làm thế nào để giải quyết Cholesky sử dụng khoảng cách Mahalanobis?
Aly

Kiểm tra các liên kết trong bài viết ban đầu của bnaul. Nhưng đừng sử dụng nhưng Cholesky (đó là ý nghĩa của LDL *). LU
usεr11852

2

Các câu trả lời và bình luận được đăng đều cho thấy những điểm tốt về sự nguy hiểm của việc đảo ngược các ma trận gần như số ít. Tuy nhiên, theo như tôi có thể nói, không ai đề cập rằng tính toán khoảng cách Mahalanobis không thực sự đòi hỏi phải đảo ngược hiệp phương sai mẫu. Xem câu hỏi StackOverflow này để biết mô tả về cách thực hiện bằng cách sử dụng phân tách .LU

Nguyên tắc giống như giải một hệ tuyến tính: khi cố gắng giải cho sao cho , có nhiều phương pháp hiệu quả và ổn định số hơn nhiều so với lấy .xAx=bx=A1b

Chỉnh sửa: có thể không cần phải nói, nhưng phương pháp này tạo ra giá trị khoảng cách chính xác, trong khi thêm vào và đảo ngược chỉ mang lại một xấp xỉ.λIS


1
Bạn nói đúng, @bnaul. Tuy nhiên, nếu không có một số loại chính quy, LUphân tách cũng sẽ không hoạt động. Tôi sẽ thêm một nhận xét về điều này trong câu trả lời của tôi.
Zen

@bnaul: tại sao LU làm khi bạn đến Cholesky để kiểm tra độ chính xác dương? Giả sử bạn có ma trận hiệp phương sai hợp lệ giải cho y bằng cách thay thế về phía trước, và sau đó cho x bằng cách thay thế trở lại sẽ nhanh hơn. Mặc dù điểm tốt, tôi chắc chắn tập trung vào việc có được sự chắc chắn tích cực mà tôi đã quên tại sao tôi quan tâm đến nó ban đầu! : DK=LLTLy=bLTx=y
usεr11852

0

(Nhiều năm sau) một ví dụ nhỏ: với thiếu thứ hạng, eigenvalues ​​của sẽ là 0 đến trong độ chính xác của máy - và khoảng một nửa số "số không" này có thể :Ar<n, nrATA<0

#!/usr/bin/env python2
""" many eigenvalues of A'A are tiny but < 0 """
# e.g. A 1 x 10: [-1.4e-15 -6.3e-17 -4e-17 -2.7e-19 -8.8e-21  1e-18 1.5e-17 5.3e-17 1.4e-15  7.7]

from __future__ import division
import numpy as np
from numpy.linalg import eigvalsh  # -> lapack_lite
# from scipy.linalg import eigvalsh  # ~ same
from scipy import __version__

np.set_printoptions( threshold=20, edgeitems=10, linewidth=140,
        formatter = dict( float = lambda x: "%.2g" % x ))  # float arrays %.2g
print "versions: numpy %s  scipy %s \n" % (
        np.__version__, __version__  )

np.random.seed( 3 )

rank = 1
n = 10
A = np.random.normal( size=(rank, n) )
print "A: \n", A
AA = A.T.dot(A)
evals = eigvalsh( AA )
print "eigenvalues of A'A:", evals
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.