Come eseguire la convalida incrociata per PCA per determinare il numero di componenti principali?


13

Sto cercando di scrivere la mia funzione per l'analisi dei componenti principali, PCA (ovviamente ce ne sono già molti scritti ma sono solo interessato a implementare cose da solo). Il problema principale che ho riscontrato è la fase di convalida incrociata e il calcolo della somma dei quadrati prevista (PRESS). Non importa quale convalida incrociata io uso, è una domanda principalmente sulla teoria alla base, ma considera la convalida incrociata unilaterale (LOOCV). Dalla teoria ho scoperto che per eseguire LOOCV devi:

  1. elimina un oggetto
  2. ridimensionare il resto
  3. eseguire PCA con un certo numero di componenti
  4. ridimensionare l'oggetto cancellato in base ai parametri ottenuti in (2)
  5. prevedere l'oggetto secondo il modello PCA
  6. calcola PRESS per questo oggetto
  7. rieseguire lo stesso algoritmo su altri oggetti
  8. sommare tutti i valori PRESS
  9. profitto

Perché sono molto nuovo nel campo, per essere sicuro di avere ragione, confronto i risultati con l'output di alcuni software che ho (anche per scrivere un codice seguo le istruzioni nel software). Ottengo completamente gli stessi risultati calcolando la somma residua di quadrati e R2 , ma calcolare PRESS è un problema.

Potresti dirmi se ciò che implemento nella fase di convalida incrociata è giusto o no:

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

Nel software ( PLS_Toolbox ) funziona in questo modo:

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

Quindi, fanno qualche ulteriore normalizzazione usando questa tempRepmatvariabile: l'unica ragione per cui ho scoperto era che applicano LOOCV per un PCA robusto. Sfortunatamente, il team di supporto non ha voluto rispondere alla mia domanda poiché ho solo una versione demo del loro software.


Ulteriore verifica se comprendo lo snippet di normalizzazione aggiuntivo: qual è il ruolo della tempRepmat(kk,kk) = -1linea? La riga precedente non garantisce già che sia tempRepmat(kk,kk)uguale a -1? Inoltre, perché gli svantaggi? L'errore verrà comunque quadrato, quindi capisco correttamente che se i punti negativi vengono rimossi, nulla cambierà?
ameba dice Ripristina Monica il

Lo stavo controllando in passato e non cambierà nulla. È corretto. Ho trovato solo alcuni parallelismi con solide implementazioni di PCA perché ogni valore PRESS calcolato in tale implementazione (prima di riassumere tutto) ha il suo peso.
Kirill,

Sto cercando il codice R equivalente al codice MATLAB fornito nella risposta e ho creato una taglia.
AIM_BLB

3
@CSA, la richiesta di codice è fuori tema qui (anche, presumibilmente, tramite commenti e taglie). Dovresti essere in grado di chiederlo su Stack Overflow : puoi copiare il codice, citare la fonte con un link qui e chiedere una traduzione. Credo che tutto ciò sarebbe in tema lì.
gung - Ripristina Monica

Risposte:


21

Quello che stai facendo è sbagliato: non ha senso calcolare PRESS per PCA in quel modo! In particolare, il problema risiede nel passaggio 5.


Approccio ingenuo a PRESS per PCA

Consenti all'insieme di dati di punti nello spazio d- dimensionale: x ( i )R d ,nd . Per calcolare l'errore di ricostruzione per un singolo punto dati di test x ( i ) , si esegue PCA sul set di addestramento X ( - i ) con questo punto escluso, si prende un certo numero k di assi principali come colonne di U ( - i ) e trovare l'errore di ricostruzione comex ( i ) - x ( i ) 2 = x ( i ) -x(i)Rd,i=1nx(i)X(i)kU(i). PRESS è quindi uguale alla somma su tutti i campioni di testi, quindi l'equazione ragionevole sembra essere: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.

Per semplicità, sto ignorando i problemi di centratura e ridimensionamento qui.

L'approccio ingenuo è sbagliato

Il problema sopra è che usiamo per calcolare la previsione , e questa è una cosa pessima.x ( i )x(i)x^(i)

Nota la differenza cruciale rispetto a un caso di regressione, in cui la formula dell'errore di ricostruzione è sostanzialmente la stessa , ma la previsione viene calcolata usando le variabili del predittore e non usando . Questo non è possibile in PCA, perché in PCA non ci sono variabili dipendenti e indipendenti: tutte le variabili sono trattate insieme.y ( i ) y ( i )y(i)y^(i)2y^(i)y(i)

In pratica significa che PRESS come calcolato sopra può diminuire con l'aumentare del numero di componenti e non raggiungere mai un minimo. Il che porterebbe a pensare che tutti i componenti siano significativi. O forse in alcuni casi raggiunge un minimo, ma tende ancora a sovralimentare e sopravvalutare la dimensionalità ottimale.dkd

Un approccio corretto

xj(i)x(i)http://alexhwilliams.info/itsneuronalblog/2018/02/26/crossval/ per una bella discussione e l'implementazione di Python (PCA con valori mancanti è implementato tramite i minimi quadrati alternati).

x(i)x(i)

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

x(i)kU(i)xj(i)xj(i)Rd1x^j(i)jxj(i)U(i)z^Rkxj(i)z^=[Uj(i)]+xj(i)RkUj(i)U(i)j[]+z^U(i)[Uj(i)]+xj(i)j[]j

Un'approssimazione all'approccio corretto

Non capisco bene la normalizzazione aggiuntiva utilizzata in PLS_Toolbox, ma qui c'è un approccio che va nella stessa direzione.

xj(i)z^approx=[Uj(i)]xj(i), cioè semplicemente prendere la trasposizione invece di pseudo-inversa. In altre parole, la dimensione che viene lasciata fuori per il test non viene affatto conteggiata e anche i pesi corrispondenti vengono semplicemente eliminati. Penso che questo dovrebbe essere meno preciso, ma potrebbe essere spesso accettabile. La cosa buona è che la formula risultante può ora essere vettorializzata come segue (ometto il calcolo):

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

U(i)Udiag{}UU

Aggiornamento (febbraio 2018): sopra ho definito una procedura "corretta" e un'altra "approssimativa", ma non sono più così sicuro che ciò sia significativo. Entrambe le procedure hanno senso e penso che nessuna delle due sia più corretta. Mi piace molto che la procedura "approssimativa" abbia una formula più semplice. Inoltre, ricordo che avevo un set di dati in cui la procedura "approssimativa" ha prodotto risultati che sembravano più significativi. Sfortunatamente, non ricordo più i dettagli.


Esempi

Ecco come si confrontano questi metodi per due set di dati noti: set di dati Iris e set di dati wine. Si noti che il metodo ingenuo produce una curva decrescente monotonicamente, mentre altri due metodi producono una curva con un minimo. Si noti inoltre che nel caso dell'iride, il metodo approssimativo suggerisce 1 PC come numero ottimale, ma il metodo pseudoinverso suggerisce 2 PC. (E guardando qualsiasi grafico a dispersione PCA per il set di dati Iris, sembra che entrambi i primi PC trasmettano un segnale.) E nel caso del vino il metodo pseudoinverso punta chiaramente a 3 PC, mentre il metodo approssimativo non può decidere tra 3 e 5.

inserisci qui la descrizione dell'immagine


Codice Matlab per eseguire la convalida incrociata e tracciare i risultati

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

La ringrazio per la risposta! Conosco quel foglio. E ho applicato la convalida incrociata per riga descritta qui (sembra corrispondere esattamente al codice che ho fornito). Confronto con il software PLS_Toolbox ma hanno una riga di codice in LOOCV che non capisco davvero (ho scritto nella domanda originale).
Kirill,

Sì, la chiamano "convalida incrociata a livello di riga" e l'implementazione sembra soddisfacente, ma tieni presente che questo è un brutto modo per eseguire la convalida incrociata (come affermato e dimostrato empiricamente in Bro et al.) E sostanzialmente dovresti non usarlo mai! Per quanto riguarda la riga di codice che non capisci, hai intenzione di aggiornare la tua domanda? Non sono sicuro di cosa ti riferisci.
ameba dice Ripristina Monica il

Il fatto è che questa implementazione sembra avere la capacità di raggiungere il minimo nel CV.
Kirill,

x^x

ki

1

Per aggiungere un punto ancora più generale alla bella risposta di @ amoeba:

Una differenza pratica e cruciale tra modelli supervisionati e non supervisionati è che per i modelli non supervisionati è necessario pensare molto di più a ciò che considerereste equivalente e cosa no.

y^y^y

Al fine di costruire misure di prestazione significative, è necessario pensare a quali tipi di libertà del modello sono insignificanti per la propria applicazione e quali no. Ciò porterebbe a una PRESS sui punteggi, possibilmente (di solito?) Dopo una sorta di rotazione / capovolgimento simile a Procrustes.

PREMERE su x La mia ipotesi è (non ho tempo per scoprire cosa fanno le loro 2 righe di codice - ma forse potresti passare da una riga all'altra e dare un'occhiata?):

Al fine di ottenere una misura utile per determinare una buona complessità del modello da una misura che dia una bontà di adattamento che in genere aumenterà fino al raggiungimento del modello di rango intero, è necessario penalizzare per modelli troppo complessi. Il che a sua volta significa che questa penalizzazione è a) cruciale eb) l'adeguamento della penalità regolerà la complessità scelta.


Nota a margine: vorrei solo aggiungere che starei molto attento con questo tipo di ottimizzazione automatizzata della complessità del modello. Nella mia esperienza molti di questi algoritmi producono solo pseudooggettività e spesso hanno il costo di funzionare bene solo per determinati tipi di dati.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.