Кращий алгоритм PCA для величезної кількості функцій (> 10 К)?


54

Раніше я запитував це в StackOverflow, але, здається, це може бути більш доречним, враховуючи, що він не отримав відповідей на SO. Це свого роду на перетині між статистикою та програмуванням.

Мені потрібно написати якийсь код, щоб зробити PCA (аналіз основних компонентів). Я переглянув відомі алгоритми і реалізував цей , який, наскільки я можу сказати, еквівалентний алгоритму NIPALS. Він добре працює для пошуку перших 2-3 основних компонентів, але потім, схоже, стає дуже повільним для сходження (на порядку сотень до тисяч ітерацій). Ось деталі того, що мені потрібно:

  1. Алгоритм повинен бути ефективним при роботі з величезною кількістю функцій (порядку від 10 000 до 20 000) та розмірами вибірки на порядок кількох сотень.

  2. Він повинен бути розумно реалізованим без гідної лінійної бібліотеки алгебри / матриці, оскільки цільовою мовою є D, якого ще немає, і навіть якби це було, я б вважав за краще не додавати його як залежність до проекту, про який йде мова. .

Як зауваження, на тому ж наборі даних R, здається, знаходить всі основні компоненти дуже швидко, але в ньому використовується синхронічне розкладання значення, яке я не хочу кодувати.


2
Існує навантаження загальнодоступних алгоритмів SVD. Дивіться en.wikipedia.org/wiki/… . Ви не можете використовувати або адаптувати одну з них? Також R є відкритим кодом і має ліцензію GPL, то чому б не запозичити його алгоритм, якщо він виконує цю роботу?
Роб Хайндман

@Rob: Я хотів би уникати написання бібліотеки лінійної алгебри, а також хочу уникати копілефт GPL. Крім того, я раніше розглядав біти та фрагменти вихідного коду R, і він, як правило, не дуже читабельний.
dimimcha

4
Я щось пропускаю? У вас є> 10K функцій, але <1K зразки? Це означає, що останні 9K компоненти є довільними. Ви хочете, щоб усі 1K перших компонентів?
shabbychef

2
У будь-якому випадку вам не вдасться уникнути необхідності впровадження SVD, хоча завдяки численним лінійним дослідженням лінійної алгебри, тепер існує маса методів на вибір, залежно від того, наскільки велика / мала, розріджена / щільна ваша матриця, або якщо ви хочете лише одиничні значення або повний набір сингулярних значень і лівий / правий сингулярні вектори. Алгоритми не дуже важко зрозуміти IMHO.
JM не є статистиком

Чи можете ви сказати нам, чому ви хочете робити PCA?
Робін Жирард

Відповіді:


27

Я реалізував рандомізований SVD, як зазначено у "Halko, N., Martinsson, PG, Shkolnisky, Y., & Tygert, M. (2010). Алгоритм для аналізу основних компонентів великих наборів даних. Arxiv preprint arXiv: 1007.5510, 0526. Отримано 1 квітня 2011 року з http://arxiv.org/abs/1007.5510 . ". Якщо ви хочете отримати усічений SVD, він дійсно працює набагато швидше, ніж зміни SVD в MATLAB. Ви можете отримати його тут:

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, це більш ніж в 10 разів швидше, ніж використання Matlab SVD. До речі, вам може знадобитися така проста функція для тестової функції:

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 на величезних наборах даних.

Див. Мартинссона, Рохліна та Тайгерта "Рандомізований алгоритм розкладання матриць". У Tygert є код для дуже швидкої реалізації PCA.

Нижче представлена ​​проста реалізація рандомізованого 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 для розкладання штрафу на матриці. Цей пакет досить дивовижний. Я, мабуть, мушу зазначити, що в ньому написано "Віттен", якщо у людей виникнуть проблеми з пошуком цитати. Нарешті, в ОП сказали, що не хочуть нічого, написаного на R, але по суті будь-який великий пакет SVD там буде мати швидкість C, C ++ або Fortran.
Девід Дж. Харріс

4

Здається, що, можливо, ви хочете використовувати алгоритм Ланцоса . Якщо цього не сталося, ви можете звернутися до Golub & Van Loan. Я колись зашифрував у своєму тексті алгоритм SVD (в SML всіх мов), і він працював досить добре.


3

Я б запропонував спробувати ядро PCA, яке має складність у часі та просторі залежно від кількості прикладів (N), а не від кількості функцій (P), що, на мою думку, було б більш підходящим у ваших налаштуваннях (P >> N)). Ядро PCA в основному працює з матрицею ядра NxN (матриця подібності між точками даних), а не з коваріаційною матрицею PxP, з якою важко впоратися для великого П. Ще одна гарна річ у PCA ядра полягає в тому, що він може вивчати нелінійні проекції а також якщо ви використовуєте його з відповідним ядром. Дивіться цей документ на PCA ядра .


2

Здається, я згадую, що можна виконати PCA шляхом обчислення власного розкладання X ^ TX, а не XX ^ T, а потім перетворення для отримання ПК. Однак я не можу пригадати деталі з власної сторони, але це в чудовій книзі Джолліффа, і я перегляну її, коли я буду на роботі. Я би транслітерував лінійні алгебри підпрограми з, наприклад, Числових методів на C, а не використовувати будь-який інший алгоритм.


5
Добре горе ... побудова матриці коваріації ніколи не є найкращим способом для SVD. Я показав приклад того, чому явно формування матриці коваріації не є хорошою ідеєю в math.SE: math.stackexchange.com/questions/3869/3871#3871 .
JM не є статистиком

1

Існує також метод завантаження Fisher et al , розроблений для декількох сотень зразків високого розміру.

Основна ідея методу сформульована як "переустановка - це трансформація низьких розмірів". Отже, якщо у вас є невелика (кілька сотень) кількість високомірних зразків, ви не можете отримати більше основних компонентів, ніж кількість ваших зразків. Таким чином, має сенс розглядати вибірки як парсимонічну основу, проектувати дані на лінійну підпростору, що охоплюється цими векторами, та обчислювати PCA у цьому меншому підпросторі. Вони також містять докладніші відомості про те, як поводитися з випадком, коли не всі зразки можуть зберігатися в пам'яті.


0

Дивіться статтю Сема Ровейса, алгоритми Е. М. для PCA та SPCA .


Алгоритм Вікіпедії цитує це і рівносильно цьому для випадку пошуку одного головного компонента за раз.
dimimcha

Гаразд, я бачу посилання зараз. Це досить простий підхід, і, як згадує Вікіпедія, є основна ідея. Однак, поміркувавши, вам доведеться мати справу з деякими компромісами (конвергенція в даному випадку). Цікаво, чи ви тут ставите правильне запитання. Чи справді немає хороших прив’язок до бібліотек лінанг для D?
вр
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.