具有大量功能(> 10K)的最佳PCA算法?


54

我之前在StackOverflow上问过这个问题,但由于它在SO上没有任何答案,因此似乎在这里更合适。这是统计和编程之间的交集。

我需要编写一些代码来进行PCA(主成分分析)。我浏览了众所周知的算法并实现了这一算法,据我所知,它等效于NIPALS算法。它可以很好地找到前2-3个主成分,但是收敛似乎很慢(大约数百到数千次迭代)。以下是我需要的详细信息:

  1. 当处理大量特征(10,000到20,000数量级)和数百个样本量的样本时,该算法必须高效。

  2. 它必须在没有像样的线性代数/矩阵库的情况下可以合理地实现,因为目标语言是D,但尚不存在,即使有,我也不想将其作为对相关项目的依赖项添加。

附带说明一下,在同一个数据集上,R看起来非常快地找到了所有主成分,但是它使用了奇异值分解,这不是我想自己编写的东西。


2
有大量的公共SVD算法。参见en.wikipedia.org/wiki/…。您不能使用或改用其中之一吗?而且,R是开放源代码的,并且具有GPL许可证,那么,如果R能够完成任务,为什么不借用它的算法呢?
Rob Hyndman 2010年

@Rob:我想避免实际编写线性代数库,而且我也想避免GPL的copyleft。另外,我之前已经看过R源代码的零碎片段,并且通常不太可读。
dsimcha's

4
我想念什么吗?您具有1万多个特征,但样本却小于1万?这意味着最后的9K分量是任意的。您是否需要所有1K的第一个组件?
shabbychef

2
无论如何,您都不必逃避实现SVD的麻烦,尽管由于进行了大量的数值线性代数研究,现在有很多方法可供选择,具体取决于矩阵的大小,稀疏/密集或是否您只需要奇异值,或完整的奇异值集和左/右奇异向量。这些算法并不难理解恕我直言。
JM不是统计学家

您能告诉我们为什么要进行PCA吗?
罗宾吉拉德2011年

Answers:


27

我已经实现了随机化的SVD,如“ N.Halko,Martinsson,PG,Shkolnisky,Y.,&Tygert,M.(2010)中给出的。大数据集主成分分析的算法。Arxiv预印本arXiv: 1007.5510,0526 。从http://arxiv.org/abs/1007.5510中检索。”。如果要截断SVD,它的工作速度实际上要比MATLAB中的SVD版本快得多。你可以在这里得到它:

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

要测试它,只需在同一文件夹中创建一个图像(就像一个大矩阵一样,您可以自己创建矩阵)

% 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

当我在桌面上运行635 * 483尺寸的图像时,我得到

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

如您所见,对于较低的k,它的速度比使用Matlab SVD快10倍以上。顺便说一句,对于测试功能,您可能需要以下简单功能:

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

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

我没有添加PCA方法,因为使用SVD可以轻松实现。您可以检查此链接以查看其关系。


12

您可以尝试使用几个选项。

1- 惩罚矩阵分解。您对u和v施加一些惩罚约束以得到一些稀疏性。基因组数据已使用的快速算法

参见Whitten Tibshirani。他们也有一个R-pkg。“惩罚矩阵分解,可用于稀疏主成分和规范相关分析。”

2- 随机SVD。由于SVD是主要算法,因此可能需要找到一个非常快速的近似值,尤其是对于探索性分析。使用随机SVD,您可以对庞大的数据集进行PCA。

参见Martinsson,Rokhlin和Tygert,“矩阵分解的随机算法”。泰格(Tygert)具有用于快速实施PCA的代码。

以下是R中随机化SVD的简单实现。

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用于惩罚矩阵分解。这个包裹真是太神奇了。我可能应该提到它的拼写是“ Witten”,以防人们在找不到引用时遇到麻烦。最后,OP表示他们不希望用R编写任何东西,但实际上任何大型SVD软件包都将具有C,C ++或Fortran后端以提高速度。
David J. Harris


3

我建议尝试使用时间/空间复杂度取决于示例数量(N)而不是特征数量(P)的内核PCA,我认为它更适合您的设置(P >> N))。内核PCA基本上使用NxN内核矩阵(数据点之间的相似性矩阵),而不是使用PxP协方差矩阵,后者对于大P来说很难处理。内核PCA的另一个优点是,它可以学习非线性投影如果将它与合适的内核一起使用,也是如此。请参阅有关内核PCA的本文


2

我似乎还记得,可以通过计算X ^ TX而不是XX ^ T的特征分解来执行PCA,然后进行变换以获取PC。但是我记不住这些细节,但这是在Jolliffe的(优秀)书中,当我下次工作时会去查找它。我会从C语言中的“数值方法”音译线性代数例程,而不是使用任何其他算法。


5
悲痛...构造协方差矩阵从来都不是SVD的最佳方法。我展示了一个示例,说明为什么在math.SE上显式形成协方差矩阵不是一个好主意:math.stackexchange.com/questions/3869/3871#3871
JM不是统计学家

1

Fisher等人也使用了bootstrap方法,方法设计用于数百个高维样本。

该方法的主要思想被表述为“重采样是低维变换”。因此,如果您有少量(数百个)高维样本,那么得到的主成分将不会超过样本数。因此,有必要将样本视为简约的基础,将数据投影到这些向量跨越的线性子空间上,并在此较小的子空间内计算PCA。当并非所有样本都可能存储在存储器中时,它们还提供了更多详细信息。


0

参见Sam Roweis的论文,《用于PCA和SPCA的EM算法》


维基百科算法引用了这一点,并且相当于一次找到一个主要成分的情况。
dsimcha's

好的,我现在看到了链接。这是一种非常简单的方法,就像Wikipedia所提到的那样,该基本思想也有所进步。但是,考虑一下,您将不得不进行某种权衡(在这种情况下为收敛)。我想知道您是否在问正确的问题。真的没有对D的linalg库的良好绑定吗?
ars
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.