Làm thế nào để thực hiện xác nhận chéo cho PCA để xác định số lượng thành phần chính?


13

Tôi đang cố gắng viết chức năng của riêng mình để phân tích thành phần chính, PCA (tất nhiên đã có rất nhiều bài viết nhưng tôi chỉ quan tâm đến việc tự mình thực hiện công cụ). Vấn đề chính tôi gặp phải là bước xác thực chéo và tính toán tổng bình phương dự đoán (PRESS). Tôi không sử dụng xác nhận chéo nào, đó là câu hỏi chủ yếu về lý thuyết đằng sau, nhưng hãy xem xét xác thực chéo một lần (LOOCV). Từ lý thuyết tôi phát hiện ra rằng để thực hiện LOOCV, bạn cần phải:

  1. xóa một đối tượng
  2. quy mô phần còn lại
  3. thực hiện PCA với một số thành phần
  4. chia tỷ lệ đối tượng bị xóa theo các tham số thu được trong (2)
  5. dự đoán đối tượng theo mô hình PCA
  6. tính toán PRESS cho đối tượng này
  7. thực hiện lại thuật toán tương tự cho các đối tượng khác
  8. tổng hợp tất cả các giá trị PRESS
  9. lợi nhuận

Bởi vì tôi rất mới trong lĩnh vực này, để chắc chắn rằng tôi đúng, tôi so sánh kết quả với đầu ra từ một số phần mềm tôi có (cũng để viết một số mã tôi làm theo hướng dẫn trong phần mềm). Tôi nhận được kết quả hoàn toàn giống nhau khi tính tổng bình phương còn lại và , nhưng tính toán PRESS là một vấn đề.R2

Bạn có thể vui lòng cho tôi biết nếu những gì tôi thực hiện trong bước xác thực chéo là đúng hay không:

case 'loocv'

% # n - number of objects
% # p - number of variables
% # vComponents - the number of components used in CV
dataSets = divideData(n,n); 
         % # it is just a variable responsible for creating datasets for CV 
         % #  (for LOOCV datasets will be equal to [1, 2, 3, ... , n]);'
tempPRESS = zeros(n,vComponents);

for j = 1:n
  Xmodel1 = X; % # X - n x p original matrix
  Xmodel1(dataSets{j},:) = []; % # delete the object to be predicted
  [Xmodel1,Xmodel1shift,Xmodel1div] = skScale(Xmodel1, 'Center', vCenter, 
                                              'Scaling', vScaling); 
          % # scale the data and extract the shift and scaling factor
  Xmodel2 = X(dataSets{j},:); % # the object to be predicted
  Xmodel2 = bsxfun(@minus,Xmodel2,Xmodel1shift); % # shift and scale the object
  Xmodel2 = bsxfun(@rdivide,Xmodel2,Xmodel1div);
  [Xscores2,Xloadings2] = myNipals(Xmodel1,0.00000001,vComponents); 
          % # the way to calculate the scores and loadings
                % # Xscores2 - n x vComponents matrix
                % # Xloadings2 - vComponents x p matrix
  for i = 1:vComponents
    tempPRESS(j,i) = sum(sum((Xmodel2* ...
       (eye(p) - transpose(Xloadings2(1:i,:))*Xloadings2(1:i,:))).^2));
  end
end
PRESS = sum(tempPRESS,1);

Trong phần mềm ( PLS_Toolbox ), nó hoạt động như thế này:

for i = 1:vComponents
    tempPCA = eye(p) - transpose(Xloadings2(1:i,:))*Xloadings2(1:i,:);
    for kk = 1:p
        tempRepmat(:,kk) = -(1/tempPCA(kk,kk))*tempPCA(:,kk);
          % # this I do not understand
        tempRepmat(kk,kk) = -1; 
          % # here is some normalization that I do not get
    end 
    tempPRESS(j,i) = sum(sum((Xmodel2*tempRepmat).^2)); 
end

Vì vậy, họ thực hiện một số chuẩn hóa bổ sung bằng cách sử dụng tempRepmatbiến này : lý do duy nhất tôi tìm thấy là họ áp dụng LOOCV cho PCA mạnh mẽ. Thật không may, nhóm hỗ trợ không muốn trả lời câu hỏi của tôi vì tôi chỉ có phiên bản demo của phần mềm của họ.


Kiểm tra thêm nếu tôi hiểu đoạn trích chuẩn hóa bổ sung: vai trò của tempRepmat(kk,kk) = -1dòng là gì? Không phải dòng trước đã đảm bảo tempRepmat(kk,kk)bằng -1? Ngoài ra, tại sao lại có lỗi? Dù sao thì lỗi cũng sẽ được bình phương, vì vậy tôi có hiểu chính xác rằng nếu các lỗi được loại bỏ, sẽ không có gì thay đổi?
amip nói rằng Phục hồi lại

Tôi đã kiểm tra nó trong quá khứ và sẽ không có gì thay đổi. Đúng rồi. Tôi chỉ tìm thấy một số điểm tương đồng với việc triển khai PCA mạnh mẽ bởi vì mọi giá trị PRESS được tính toán trong quá trình thực hiện đó (trước khi tóm tắt mọi thứ) đều có trọng số riêng.
Kirill

Tôi đang tìm mã R tương đương với mã MATLAB được cung cấp trong câu trả lời và đã đưa ra một tiền thưởng.
AIM_BLB

3
@CSA, yêu cầu mã không có chủ đề ở đây (thậm chí, có lẽ, thông qua ý kiến ​​& tiền thưởng). Bạn sẽ có thể yêu cầu điều đó trên Stack Overflow : bạn có thể sao chép mã, trích dẫn nguồn w / một liên kết ở đây và yêu cầu dịch. Tôi tin rằng tất cả những gì sẽ có trong chủ đề đó.
gung - Phục hồi Monica

Câu trả lời:


21

Những gì bạn đang làm là sai: không có nghĩa gì khi tính toán BÁO CHÍ cho PCA như thế! Cụ thể, vấn đề nằm ở bước 5 của bạn.


Cách tiếp cận ngây thơ để PRESS cho PCA

Hãy tập dữ liệu gồm điểm trong d không gian ba chiều: x ( i )R d ,ndx(i)Rd,i=1nx(i)X(i)kU(i)iPRESS ? = n i = 1 x ( i ) - U ( -x(i)x^(i)2=x(i)U(i)[U(i)]x(i)2i

PRESS=?i=1nx(i)U(i)[U(i)]x(i)2.

Để đơn giản, tôi bỏ qua các vấn đề định tâm và nhân rộng ở đây.

Cách tiếp cận ngây thơ là sai

Vấn đề ở trên là chúng tôi sử dụng để tính toán dự đoán và đó là một điều rất tồi tệ.x(i)x^(i)

Lưu ý sự khác biệt quan trọng đối với trường hợp hồi quy, trong đó công thức cho lỗi tái cấu trúc về cơ bản là giống nhau , nhưng dự đoán được tính bằng các biến dự đoán và không sử dụng . Điều này là không thể trong PCA, bởi vì trong PCA không có biến phụ thuộc và độc lập: tất cả các biến được xử lý cùng nhau.y(i)y^(i)2y^(i)y(i)

Trong thực tế, điều đó có nghĩa là PRESS như được tính toán ở trên có thể giảm khi số lượng thành phần tăng lên và không bao giờ đạt đến mức tối thiểu. Điều đó sẽ dẫn người ta nghĩ rằng tất cả các thành phần là đáng kể. Hoặc có thể trong một số trường hợp, nó đạt đến mức tối thiểu, nhưng vẫn có xu hướng quá phù hợp và đánh giá quá cao chiều tối ưu.kd

Một cách tiếp cận đúng

Có một số cách tiếp cận có thể, xem Bro et al. (2008) Xác thực chéo các mô hình thành phần: cái nhìn quan trọng về các phương pháp hiện tại để có cái nhìn tổng quan và so sánh. Một cách tiếp cận là loại bỏ một chiều của một điểm dữ liệu tại một thời điểm (ví dụ thay vì ), để dữ liệu đào tạo trở thành ma trận với một giá trị bị thiếu và sau đó để dự đoán ("tạp chất") giá trị còn thiếu này với PCA. (Tất nhiên người ta có thể giữ ngẫu nhiên một số phần tử ma trận lớn hơn, ví dụ 10%). Vấn đề là tính toán PCA với các giá trị bị thiếu có thể được tính toán khá chậm (nó dựa trên thuật toán EM), nhưng cần phải lặp đi lặp lại nhiều lần ở đây. Cập nhật: xem http://alexhwilliams.info/itsneuronalblog/2018/02/26/crossval/xj(i)x(i) để thảo luận tốt và triển khai Python (PCA với các giá trị bị thiếu được triển khai thông qua các bình phương tối thiểu xen kẽ).

Một cách tiếp cận mà tôi thấy thực tế hơn nhiều là bỏ qua một điểm dữ liệu tại một thời điểm, tính PCA trên dữ liệu đào tạo (chính xác như trên), nhưng sau đó lặp lại các kích thước của , bỏ chúng cùng một lúc và tính toán lỗi tái tạo bằng phần còn lại. Điều này có thể khá khó hiểu khi bắt đầu và các công thức có xu hướng trở nên khá lộn xộn, nhưng việc thực hiện khá đơn giản. Trước tiên tôi xin đưa ra công thức (hơi đáng sợ), và sau đó giải thích ngắn gọn về nó:x(i)x(i)

PRESSPCA=i=1nj=1d|xj(i)[U(i)[Uj(i)]+xj(i)]j|2.

Hãy xem xét các vòng lặp bên trong ở đây. Chúng tôi đã bỏ qua một điểm và tính thành phần chính trên dữ liệu đào tạo, . Bây giờ chúng tôi giữ từng giá trị làm thử nghiệm và sử dụng các kích thước còn lại để thực hiện dự đoán . Dự đoán là tọa độ thứ của "phép chiếu" (theo nghĩa bình phương nhỏ nhất) của trên không gian con được kéo dài bởi . Để tính toán, hãy tìm một điểm trong không gian PC gần nhất vớix(i)kU(i)xj(i)xj(i)Rd1x^j(i)jxj(i)U(i)z^Rkxj(i) bằng cách tính toán trong đó là với hàng thứ đá ra và là viết tắt của giả. Bây giờ ánh xạ trở lại không gian ban đầu: và lấy tọa độ thứ . z^=[Uj(i)]+xj(i)RkUj(i)U(i)j[]+z^U(i)[Uj(i)]+xj(i)j[]j

Một cách gần đúng với cách tiếp cận đúng

Tôi hoàn toàn không hiểu chuẩn hóa bổ sung được sử dụng trong PLS_Toolbox, nhưng đây là một cách tiếp cận theo cùng một hướng.

Có một cách khác để ánh xạ vào không gian của các thành phần chính: , tức là chỉ cần chuyển đổi thay vì giả nghịch đảo. Nói cách khác, kích thước còn lại để thử nghiệm hoàn toàn không được tính và các trọng số tương ứng cũng chỉ đơn giản là bị loại ra. Tôi nghĩ rằng điều này nên ít chính xác hơn, nhưng thường có thể được chấp nhận. Điều tốt là công thức kết quả bây giờ có thể được vector hóa như sau (Tôi bỏ qua tính toán):xj(i)z^approx=[Uj(i)]xj(i)

PRESSPCA,approx=i=1nj=1d|xj(i)[U(i)[Uj(i)]xj(i)]j|2=i=1n(IUU+diag{UU})x(i)2,

trong đó tôi đã viết là cho sự gọn nhẹ và có nghĩa là đặt tất cả các phần tử không chéo thành 0. Lưu ý rằng công thức này trông giống hệt như công thức đầu tiên (PRESS ngây thơ) với một hiệu chỉnh nhỏ! Cũng lưu ý rằng việc hiệu chỉnh này chỉ phụ thuộc vào đường chéo của , giống như trong mã PLS_Toolbox. Tuy nhiên, công thức vẫn khác với những gì dường như được triển khai trong PLS_Toolbox và sự khác biệt này tôi không thể giải thích. U d i một g {} U UU(i)Udiag{}UU

Cập nhật (Tháng 2 năm 2018): Ở trên tôi đã gọi một thủ tục là "chính xác" và một "gần đúng" khác nhưng tôi không chắc chắn nữa rằng điều này có ý nghĩa. Cả hai thủ tục đều có ý nghĩa và tôi nghĩ không đúng hơn. Tôi thực sự thích rằng thủ tục "gần đúng" có một công thức đơn giản hơn. Ngoài ra, tôi nhớ rằng tôi đã có một số tập dữ liệu trong đó thủ tục "gần đúng" mang lại kết quả trông có ý nghĩa hơn. Thật không may, tôi không nhớ các chi tiết nữa.


Ví dụ

Dưới đây là cách các phương thức này so sánh cho hai bộ dữ liệu nổi tiếng: bộ dữ liệu Iris và bộ dữ liệu rượu vang. Lưu ý rằng phương pháp ngây thơ tạo ra một đường cong giảm đơn điệu, trong khi hai phương pháp khác mang lại một đường cong với mức tối thiểu. Lưu ý thêm rằng trong trường hợp Iris, phương pháp gần đúng gợi ý 1 PC là số tối ưu nhưng phương pháp giả cho thấy 2 PC. .

nhập mô tả hình ảnh ở đây


Mã Matlab để thực hiện xác nhận chéo và vẽ kết quả

function pca_loocv(X)

%// loop over data points 
for n=1:size(X,1)
    Xtrain = X([1:n-1 n+1:end],:);
    mu = mean(Xtrain);
    Xtrain = bsxfun(@minus, Xtrain, mu);
    [~,~,V] = svd(Xtrain, 'econ');
    Xtest = X(n,:);
    Xtest = bsxfun(@minus, Xtest, mu);

    %// loop over the number of PCs
    for j=1:min(size(V,2),25)
        P = V(:,1:j)*V(:,1:j)';        %//'
        err1 = Xtest * (eye(size(P)) - P);
        err2 = Xtest * (eye(size(P)) - P + diag(diag(P)));
        for k=1:size(Xtest,2)
            proj = Xtest(:,[1:k-1 k+1:end])*pinv(V([1:k-1 k+1:end],1:j))'*V(:,1:j)'; 
            err3(k) = Xtest(k) - proj(k);
        end

        error1(n,j) = sum(err1(:).^2);
        error2(n,j) = sum(err2(:).^2);
        error3(n,j) = sum(err3(:).^2);
    end    
end

error1 = sum(error1);
error2 = sum(error2);
error3 = sum(error3);
%// plotting code
figure
hold on
plot(error1, 'k.--')
plot(error2, 'r.-')
plot(error3, 'b.-')
legend({'Naive method', 'Approximate method', 'Pseudoinverse method'}, ...
    'Location', 'NorthWest')
legend boxoff
set(gca, 'XTick', 1:length(error1))
set(gca, 'YTick', [])
xlabel('Number of PCs')
ylabel('Cross-validation error')

Cảm ơn về câu trả lời của bạn! Tôi biết tờ giấy đó. Và tôi đã áp dụng xác thực chéo theo hàng được mô tả ở đó (có vẻ như nó chính xác tương ứng với mã tôi đã cung cấp). Tôi so sánh với phần mềm PLS_Toolbox nhưng họ có một dòng mã trong LOOCV mà tôi thực sự không hiểu (tôi đã viết trong câu hỏi ban đầu).
Kirill

Có, họ gọi đó là "xác thực chéo theo hàng" và việc triển khai của bạn có vẻ ổn, nhưng xin lưu ý rằng đây là một cách không tốt để thực hiện xác thực chéo (như đã nêu và cũng được chứng minh bằng thực nghiệm trong Bro et al.) Và về cơ bản bạn nên không bao giờ sử dụng nó Về dòng mã bạn không hiểu - bạn có định cập nhật câu hỏi của mình không? Không chắc chắn những gì bạn đang đề cập đến.
amip nói rằng Phục hồi lại

Điều này là việc thực hiện này dường như có khả năng đạt tối thiểu trong CV.
Kirill

ÁP LỰC của rất gần với phương sai được giải thích LOO% - Tôi sẽ không nói điều này là tốt hay xấu, nhưng nó chắc chắn là điều mà người ta cần phải biết. Và giống như% đã giải thích phương sai sẽ tiếp cận 1 khi mô hình PCA đạt thứ hạng của tập dữ liệu, X PRESS này sẽ tiếp cận 0.x^x
cbeleites không hài lòng với SX

@Kirill: Cảm ơn, đoạn mã có ý nghĩa ngay bây giờ (có thể bạn có thể xóa các nhận xét trên hiện đã lỗi thời). Tôi không biết phải làm gì, nhưng nếu bạn nói nó làm cho lỗi dự đoán được tính toán đạt đến mức tối thiểu, thì có lẽ nó đang đưa ra một số hình phạt cho lớn hơn (số lượng thành phần; tức là biến trong mã của bạn). Thành thật mà nói, tôi sẽ hoài nghi về bất kỳ phương pháp nào như vậy (trừ khi có sự biện minh về mặt lý thuyết cho nó), đặc biệt là có những cách tiếp cận tốt hơn như cách tôi mô tả trong câu trả lời của mình. ki
amip nói rằng Phục hồi lại

1

Để thêm một điểm chung hơn nữa vào câu trả lời hay của @ amoeba:

Một sự khác biệt thực tế và quan trọng giữa các mô hình được giám sát và không giám sát là đối với các mô hình không giám sát, bạn cần suy nghĩ kỹ hơn những gì bạn cho là tương đương và những gì không.

Các mô hình được giám sát luôn có đầu ra cuối cùng của chúng theo cách mà bạn không cần quan tâm nhiều về điều này: theo định nghĩa và xây dựng, tuyên bố có cùng ý nghĩa với , vì vậy bạn có thể so sánh trực tiếp. y yy^y^y

Để xây dựng các biện pháp hiệu suất có ý nghĩa, bạn cần nghĩ loại tự do nào của mô hình là vô nghĩa đối với ứng dụng của bạn và loại nào không. Điều đó sẽ dẫn đến một ÁP LỰC về điểm số, có thể (thường là?) Sau một số loại xoay / lật giống như Procrustes.

BÁO CHÍ trên x Dự đoán của tôi là (Tôi không có thời gian để tìm hiểu 2 dòng mã của họ làm gì - nhưng có lẽ bạn có thể bước qua các dòng và xem?):

Để có được một thước đo hữu ích để xác định độ phức tạp của mô hình tốt từ một thước đo mang lại mức độ phù hợp thường sẽ tăng cho đến khi đạt được mô hình xếp hạng đầy đủ, bạn cần phải xử phạt các mô hình quá phức tạp. Điều đó có nghĩa là hình phạt này là a) rất quan trọng và b) điều chỉnh hình phạt sẽ điều chỉnh độ phức tạp đã chọn.


Lưu ý bên lề: Tôi chỉ muốn nói thêm rằng tôi rất cẩn thận với kiểu tối ưu hóa độ phức tạp mô hình tự động này. Theo kinh nghiệm của tôi, nhiều thuật toán này chỉ mang lại tính khách quan giả và thường đi kèm với chi phí chỉ hoạt động tốt đối với một số loại dữ liệu nhất định.

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.