Thuật toán PCA tốt nhất cho số lượng lớn các tính năng (> 10K)?


54

Trước đây tôi đã hỏi điều này trên StackOverflow, nhưng có vẻ như nó có thể phù hợp hơn ở đây, với điều kiện là nó không nhận được bất kỳ câu trả lời nào về SO. Đó là loại giao điểm giữa thống kê và lập trình.

Tôi cần viết một số mã để làm PCA (Phân tích thành phần chính). Tôi đã duyệt qua các thuật toán nổi tiếng và thực hiện thuật toán này , theo như tôi có thể nói là tương đương với thuật toán NIPALS. Nó hoạt động tốt để tìm 2-3 thành phần chính đầu tiên, nhưng sau đó dường như trở nên rất chậm để hội tụ (theo thứ tự hàng trăm đến hàng nghìn lần lặp). Dưới đây là chi tiết về những gì tôi cần:

  1. Thuật toán phải hiệu quả khi xử lý số lượng lớn các tính năng (thứ tự 10.000 đến 20.000) và kích thước mẫu theo thứ tự vài trăm.

  2. Nó phải được thực hiện một cách hợp lý mà không cần thư viện đại số / ma trận tuyến tính đàng hoàng, vì ngôn ngữ đích là D, chưa có ngôn ngữ nào, và ngay cả khi có, tôi không muốn thêm nó như một phụ thuộc vào dự án đang đề cập .

Như một lưu ý phụ, trên cùng một tập dữ liệu R dường như tìm thấy tất cả các thành phần chính rất nhanh, nhưng nó sử dụng phân tách giá trị số ít, đây không phải là thứ tôi muốn tự viết mã.


2
Có rất nhiều thuật toán SVD công cộng. Xem en.wikipedia.org/wiki/ . Bạn không thể sử dụng hoặc điều chỉnh một trong số họ? Ngoài ra, R là nguồn mở và theo giấy phép GPL, vậy tại sao không mượn thuật toán của nó nếu nó thực hiện công việc?
Rob Hyndman

@Rob: Tôi thực sự muốn tránh việc viết một thư viện đại số tuyến tính và tôi cũng muốn tránh copyleft của GPL. Ngoài ra, tôi đã xem xét các bit và mẩu mã nguồn R trước đây và nó thường không dễ đọc.
dsimcha

4
Tui bỏ lỡ điều gì vậy? Bạn có> 10K tính năng nhưng <1K mẫu? Điều này có nghĩa là các thành phần 9K cuối cùng là tùy ý. Bạn có muốn tất cả 1K của các thành phần đầu tiên?
shabbychef

2
Trong mọi trường hợp, bạn không thể thoát khỏi việc phải thực hiện SVD, mặc dù nhờ có nhiều nghiên cứu đại số tuyến tính, hiện nay có rất nhiều phương pháp để lựa chọn, tùy thuộc vào ma trận của bạn lớn / nhỏ, thưa thớt / dày đặc như thế nào, hoặc nếu bạn chỉ muốn các giá trị số ít hoặc tập hợp đầy đủ các giá trị số ít và các vectơ số đơn trái / phải. Các thuật toán không quá khó để hiểu IMHO.
JM không phải là một nhà thống kê

Bạn có thể cho chúng tôi biết lý do tại sao bạn muốn làm PCA?
cướp girard

Câu trả lời:


27

Tôi đã triển khai SVD ngẫu nhiên như được đưa ra trong "Halko, N., Martinsson, PG, Shkolnisky, Y., & Tygert, M. (2010). Một thuật toán để phân tích thành phần chính của các tập dữ liệu lớn. Arxiv in sẵn arXiv: 1007.5510, 0526. Truy xuất ngày 1 tháng 4 năm 2011, từ http://arxiv.org/abs/1007.5510 . ". Nếu bạn muốn rút ngắn SVD, nó thực sự hoạt động nhanh hơn nhiều so với các biến thể svd trong MATLAB. Bạn có thể lấy nó ở đây:

function [U,S,V] = fsvd(A, k, i, usePowerMethod)
% FSVD Fast Singular Value Decomposition 
% 
%   [U,S,V] = FSVD(A,k,i,usePowerMethod) computes the truncated singular
%   value decomposition of the input matrix A upto rank k using i levels of
%   Krylov method as given in [1], p. 3.
% 
%   If usePowerMethod is given as true, then only exponent i is used (i.e.
%   as power method). See [2] p.9, Randomized PCA algorithm for details.
% 
%   [1] Halko, N., Martinsson, P. G., Shkolnisky, Y., & Tygert, M. (2010).
%   An algorithm for the principal component analysis of large data sets.
%   Arxiv preprint arXiv:1007.5510, 0526. Retrieved April 1, 2011, from
%   http://arxiv.org/abs/1007.5510. 
%   
%   [2] Halko, N., Martinsson, P. G., & Tropp, J. A. (2009). Finding
%   structure with randomness: Probabilistic algorithms for constructing
%   approximate matrix decompositions. Arxiv preprint arXiv:0909.4061.
%   Retrieved April 1, 2011, from http://arxiv.org/abs/0909.4061.
% 
%   See also SVD.
% 
%   Copyright 2011 Ismail Ari, http://ismailari.com.

    if nargin < 3
        i = 1;
    end

    % Take (conjugate) transpose if necessary. It makes H smaller thus
    % leading the computations to be faster
    if size(A,1) < size(A,2)
        A = A';
        isTransposed = true;
    else
        isTransposed = false;
    end

    n = size(A,2);
    l = k + 2;

    % Form a real n×l matrix G whose entries are iid Gaussian r.v.s of zero
    % mean and unit variance
    G = randn(n,l);


    if nargin >= 4 && usePowerMethod
        % Use only the given exponent
        H = A*G;
        for j = 2:i+1
            H = A * (A'*H);
        end
    else
        % Compute the m×l matrices H^{(0)}, ..., H^{(i)}
        % Note that this is done implicitly in each iteration below.
        H = cell(1,i+1);
        H{1} = A*G;
        for j = 2:i+1
            H{j} = A * (A'*H{j-1});
        end

        % Form the m×((i+1)l) matrix H
        H = cell2mat(H);
    end

    % Using the pivoted QR-decomposiion, form a real m×((i+1)l) matrix Q
    % whose columns are orthonormal, s.t. there exists a real
    % ((i+1)l)×((i+1)l) matrix R for which H = QR.  
    % XXX: Buradaki column pivoting ile yapılmayan hali.
    [Q,~] = qr(H,0);

    % Compute the n×((i+1)l) product matrix T = A^T Q
    T = A'*Q;

    % Form an SVD of T
    [Vt, St, W] = svd(T,'econ');

    % Compute the m×((i+1)l) product matrix
    Ut = Q*W;

    % Retrieve the leftmost m×k block U of Ut, the leftmost n×k block V of
    % Vt, and the leftmost uppermost k×k block S of St. The product U S V^T
    % then approxiamtes A. 

    if isTransposed
        V = Ut(:,1:k);
        U = Vt(:,1:k);     
    else
        U = Ut(:,1:k);
        V = Vt(:,1:k);
    end
    S = St(1:k,1:k);
end

Để kiểm tra nó, chỉ cần tạo một hình ảnh trong cùng một thư mục (giống như một ma trận lớn, bạn có thể tự tạo ma trận)

% Example code for fast SVD.

clc, clear

%% TRY ME
k = 10; % # dims
i = 2;  % # power
COMPUTE_SVD0 = true; % Comment out if you do not want to spend time with builtin SVD.

% A is the m×n matrix we want to decompose
A = im2double(rgb2gray(imread('test_image.jpg')))';

%% DO NOT MODIFY
if COMPUTE_SVD0
    tic
    % Compute SVD of A directly
    [U0, S0, V0] = svd(A,'econ');
    A0 = U0(:,1:k) * S0(1:k,1:k) * V0(:,1:k)';
    toc
    display(['SVD Error: ' num2str(compute_error(A,A0))])
    clear U0 S0 V0
end

% FSVD without power method
tic
[U1, S1, V1] = fsvd(A, k, i);
toc
A1 = U1 * S1 * V1';
display(['FSVD HYBRID Error: ' num2str(compute_error(A,A1))])
clear U1 S1 V1

% FSVD with power method
tic
[U2, S2, V2] = fsvd(A, k, i, true);
toc
A2 = U2 * S2 * V2';
display(['FSVD POWER Error: ' num2str(compute_error(A,A2))])
clear U2 S2 V2

subplot(2,2,1), imshow(A'), title('A (orig)')
if COMPUTE_SVD0, subplot(2,2,2), imshow(A0'), title('A0 (svd)'), end
subplot(2,2,3), imshow(A1'), title('A1 (fsvd hybrid)')
subplot(2,2,4), imshow(A2'), title('A2 (fsvd power)')

SVD nhanh

Khi tôi chạy nó trên máy tính để bàn của mình cho một hình ảnh có kích thước 635 * 483, tôi nhận được

Elapsed time is 0.110510 seconds.
SVD Error: 0.19132
Elapsed time is 0.017286 seconds.
FSVD HYBRID Error: 0.19142
Elapsed time is 0.006496 seconds.
FSVD POWER Error: 0.19206

Như bạn có thể thấy, đối với các giá trị thấp k, nó nhanh hơn 10 lần so với sử dụng Matlab SVD. Nhân tiện, bạn có thể cần chức năng đơn giản sau cho chức năng kiểm tra:

function e = compute_error(A, B)
% COMPUTE_ERROR Compute relative error between two arrays

    e = norm(A(:)-B(:)) / norm(A(:));
end

Tôi đã không thêm phương thức PCA vì nó đơn giản để thực hiện bằng cách sử dụng SVD. Bạn có thể kiểm tra liên kết này để xem mối quan hệ của họ.


12

bạn có thể thử sử dụng một vài lựa chọn.

1- Phân rã ma trận hình phạt . Bạn áp dụng một số hạn chế về hình phạt đối với u và v để có được sự thưa thớt. Thuật toán nhanh đã được sử dụng trên dữ liệu genomics

Xem Whites Tibshirani. Họ cũng có R-pkg. "Một phân rã ma trận bị phạt, với các ứng dụng cho các thành phần chính thưa thớt và phân tích tương quan chính tắc."

2- SVD ngẫu nhiên . Vì SVD là một thuật toán chính, nên việc tìm một xấp xỉ rất nhanh có thể là mong muốn, đặc biệt là cho phân tích thăm dò. Sử dụng SVD ngẫu nhiên, bạn có thể thực hiện PCA trên các bộ dữ liệu khổng lồ.

Xem Martinsson, Rokhlin và Tygert "Một thuật toán ngẫu nhiên để phân rã ma trận". Tygert có mã để triển khai PCA rất nhanh.

Dưới đây là một triển khai đơn giản của SVD ngẫu nhiên trong R.

ransvd = function(A, k=10, p=5) {
  n = nrow(A)
  y = A %*% matrix(rnorm(n * (k+p)), nrow=n)
  q = qr.Q(qr(y))
  b = t(q) %*% A
  svd = svd(b)
  list(u=q %*% svd$u, d=svd$d, v=svd$v)
}

+1 cho phân rã ma trận bị phạt. Gói đó là khá tuyệt vời. Tôi có lẽ nên đề cập rằng nó đánh vần là "Witten", trong trường hợp mọi người gặp khó khăn trong việc tìm kiếm trích dẫn. Cuối cùng, OP cho biết họ không muốn bất cứ điều gì được viết bằng R, nhưng về cơ bản, bất kỳ gói SVD lớn nào ngoài đó sẽ có phụ trợ C, C ++ hoặc Fortran để tăng tốc.
David J. Harris

4

Có vẻ như bạn muốn sử dụng Thuật toán Lanczos . Không, bạn có thể muốn tham khảo Golub & Van Loan. Tôi đã từng mã hóa một thuật toán SVD (bằng SML, của tất cả các ngôn ngữ) từ văn bản của họ và nó hoạt động khá tốt.


3

Tôi khuyên bạn nên thử PCA hạt nhân có độ phức tạp thời gian / không gian phụ thuộc vào số lượng ví dụ (N) thay vì số tính năng (P), mà tôi nghĩ sẽ phù hợp hơn trong cài đặt của bạn (P >> N)). Kernel PCA về cơ bản hoạt động với ma trận hạt nhân NxN (ma trận tương tự giữa các điểm dữ liệu), chứ không phải là ma trận hiệp phương sai PxP có thể khó xử lý đối với P. lớn cũng như nếu bạn sử dụng nó với một hạt nhân phù hợp. Xem bài viết này trên PCA kernel .


2

Tôi dường như nhớ lại rằng có thể thực hiện PCA bằng cách tính toán phân tách bản địa của X ^ TX chứ không phải XX ^ T và sau đó chuyển đổi để có được PC. Tuy nhiên tôi không thể nhớ các chi tiết ngoài tay, nhưng nó có trong cuốn sách (xuất sắc) của Jolliffe và tôi sẽ tìm kiếm nó khi tôi làm việc tiếp theo. Tôi đã phiên âm các thói quen đại số tuyến tính từ ví dụ Phương thức số trong C, thay vì sử dụng bất kỳ thuật toán nào khác.


5
Thật đau buồn ... xây dựng ma trận hiệp phương sai không bao giờ là cách tốt nhất cho SVD. Tôi đã hiển thị một ví dụ về lý do tại sao hình thành rõ ràng ma trận hiệp phương sai không phải là một ý tưởng hay trong toán học.SE: math.stackexchange.com/questions/3869/3871#3871 .
JM không phải là một thống kê

1

Ngoài ra còn có phương pháp bootstrap của Fisher et al , được thiết kế cho hàng trăm mẫu có kích thước cao.

Ý tưởng chính của phương pháp được hình thành là "lấy mẫu lại là một phép biến đổi kích thước thấp". Vì vậy, nếu bạn có một số lượng nhỏ (vài trăm) mẫu chiều cao, thì bạn không thể có được nhiều thành phần chính hơn số lượng mẫu của bạn. Do đó, có ý nghĩa khi xem xét các mẫu như là một cơ sở tuyệt vời, chiếu dữ liệu trên không gian con tuyến tính được kéo dài bởi các vectơ này và tính PCA trong không gian con nhỏ hơn này. Họ cũng cung cấp thêm chi tiết cách xử lý vụ việc khi không phải tất cả các mẫu có thể được lưu trong bộ nhớ.


0

Xem bài viết của Sam Roweis, Thuật toán EM cho PCA và SPCA .


Thuật toán Wikipedia trích dẫn điều này và tương đương với điều này trong trường hợp tìm thấy một thành phần chính tại một thời điểm.
dsimcha

OK, tôi thấy liên kết bây giờ. Đây là một cách tiếp cận khá đơn giản, và giống như Wikipedia đề cập, có những tiến bộ dựa trên ý tưởng cơ bản này. Mặc dù vậy, về phản xạ, bạn sẽ phải đối phó với một số loại đánh đổi (hội tụ trong trường hợp này). Tôi tự hỏi nếu bạn hỏi đúng câu hỏi ở đây. Có thực sự không có ràng buộc tốt cho các thư viện linalg cho D?
ars
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.