2015-11-04 13 views
5

Sto provando a calcolare i 2 principali componenti principali da un set di dati in C++ con Eigen.Analisi componenti principale con Eigen Library

Il modo in cui lo faccio al momento è di normalizzare i dati tra [0, 1] e quindi centrare la media. Dopo di che computo la matrice di covarianza ed eseguo una decomposizione autovalore su di essa. So che SVD è più veloce, ma sono confuso riguardo ai componenti calcolati.

Ecco il codice importante di come lo faccio (dove traindata è la mia matrice input dimensioni MXN):

Eigen::VectorXf normalize(Eigen::VectorXf vec) { 
    for (int i = 0; i < vec.size(); i++) { // normalize each feature. 
     vec[i] = (vec[i] - minCoeffs[i])/scalingFactors[i]; 
    } 
    return vec; 
} 

// Calculate normalization coefficients (globals of type Eigen::VectorXf). 
maxCoeffs = traindata.colwise().maxCoeff(); 
minCoeffs = traindata.colwise().minCoeff(); 
scalingFactors = maxCoeffs - minCoeffs; 

// For each datapoint. 
for (int i = 0; i < traindata.rows(); i++) { // Normalize each datapoint. 
    traindata.row(i) = normalize(traindata.row(i)); 
} 

// Mean centering data. 
Eigen::VectorXf featureMeans = traindata.colwise().mean(); 
Eigen::MatrixXf centered = traindata.rowwise() - featureMeans; 

// Compute the covariance matrix. 
Eigen::MatrixXf cov = centered.adjoint() * centered; 
cov = cov/(traindata.rows() - 1); 

Eigen::SelfAdjointEigenSolver<Eigen::MatrixXf> eig(cov); 
// Normalize eigenvalues to make them represent percentages. 
Eigen::VectorXf normalizedEigenValues = eig.eigenvalues()/eig.eigenvalues().sum(); 


// Get the two major eigenvectors and omit the others. 
Eigen::MatrixXf evecs = eig.eigenvectors(); 
Eigen::MatrixXf pcaTransform = evecs.rightCols(2); 


// Map the dataset in the new two dimensional space. 
traindata = traindata * pcaTransform; 

Il risultato di questo codice è qualcosa di simile:

enter image description here

Per confermare i miei risultati, ho provato lo stesso con WEKA. Quindi quello che ho fatto è usare il normalizzatore e il filtro centrale, in questo ordine. Quindi il componente principale filtra e salva + traccia l'output. Il risultato è questo:

enter image description here

Tecnicamente avrei fatto lo stesso, ma il risultato è così diverso. Qualcuno può vedere se ho fatto un errore?

+0

Una cosa da aggiungere: sono sicuro che WEKA utilizza SVD. Ma questo non dovrebbe spiegare la differenza nel risultato o? – Chris

risposta

1

La ragione era che Weka standardizzava il set di dati. Ciò significa che ridimensiona la varianza di ciascuna funzione in base alla varianza dell'unità. Quando l'ho fatto, le trame sembravano le stesse. Tecnicamente anche il mio approccio era corretto.

+0

potresti per favore pubblicare anche il codice di esecuzione? Grazie. –

+0

Darei un'occhiata, non so se ho ancora accesso a quel pezzo di codice e quale versione ho usato alla fine. Ma ho sicuramente parlato della standardizzazione classica, che è ben definita: https://en.wikipedia.org/wiki/Feature_scaling#Standardization – Chris

4

Quando si ridimensiona a 0,1, si modifica la variabile locale vec ma si è dimenticato di aggiornare traindata.

Inoltre, questo può essere fatto facilmente in questo modo:

RowVectorXf minCoeffs = traindata.colwise().maxCoeff(); 
RowVectorXf minCoeffs = traindata.colwise().minCoeff(); 
RowVectorXf scalingFactors = maxCoeffs - minCoeffs; 
traindata = (traindata.rowwise()-minCoeffs).array().rowwise()/scalingFactors.array(); 

cioè, utilizzando fila-vettori e matrice caratteristiche.

Aggiungo anche che la scomposizione simmetrica dell'autovalore è in realtà più veloce di SVD. Il vero vantaggio di SVD in questo caso è che evita la quadratura delle voci, ma dal momento che i dati di input sono normalizzati e centrati, e che ci si preoccupa solo degli autovalori più grandi, qui non vi è alcun problema di accuratezza.

+0

Questo è stato un errore, nel mio grande codice ho fatto una chiamata a funciton che restituisce un valore come questo: traindata.row (i) = normalize (traindata.row (i)) ;. Modificato anche qui per essere sicuro che non ci siano errori. Grazie per la semplificazione del codice, ho indovinato che è in qualche modo possibile. Riesci a vedere un altro problema? – Chris

+0

Un'altra domanda, il mio compilatore mi dice 'nessuna corrispondenza per' operatore/'' quando eseguo il tuo codice. Ho Eigen3, sembra che non ci sia una divisione rowwise o? – Chris

+0

questo genera una divisione per zero se hai per 1 funzione max = min, ho ragione? –