2015-11-04 6 views
5

Ich versuche, die 2 Hauptkomponenten aus einem Datensatz in C++ mit Eigen zu berechnen.Hauptkomponentenanalyse mit Eigenbibliothek

Die Art, wie ich es im Moment mache, besteht darin, die Daten zwischen [0, 1] zu normalisieren und dann den Mittelwert zu zentrieren. Danach berechne ich die Kovarianzmatrix und führe eine Eigenwertzerlegung darauf aus. Ich weiß, SVD ist schneller, aber ich bin verwirrt über die berechneten Komponenten. Hier

ist der Haupt Code, wie ich es tun (wo traindata ist meine MxN Größe Eingangsmatrix):

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; 

Das Ergebnis dieses Code ist so etwas wie diese:

enter image description here

Um meine Ergebnisse zu bestätigen, habe ich das gleiche mit WEKA versucht. Also habe ich den Normalize- und den Center-Filter in dieser Reihenfolge benutzt. Dann filtert und speichert die Hauptkomponente die Ausgabe. Das Ergebnis ist dieses:

enter image description here

Technisch sollte ich das gleiche getan haben, aber das Ergebnis ist so anders. Kann jemand sehen, ob ich einen Fehler gemacht habe?

+0

Eine Sache hinzuzufügen: Ich bin ziemlich sicher, dass WEKA SVD verwendet. Aber das sollte nicht den Unterschied im Ergebnis erklären oder? – Chris

Antwort

1

Der Grund war, dass Weka standardisiert wurde der Datensatz. Das heißt, es skaliert die Varianz jedes Merkmals zur Einheitsvarianz. Als ich das tat, sahen die Plots gleich aus. Technisch war mein Ansatz auch korrekt.

+0

Könntest du bitte auch den laufenden Code posten? Vielen Dank. –

+0

Ich werde es mir ansehen, ich weiß nicht, ob ich immer noch Zugang zu diesem Code habe und welche Version ich letztendlich benutzt habe. Aber ich habe sicherlich über die klassische Standardisierung gesprochen, die klar definiert ist: https://en.wikipedia.org/wiki/Feature_scaling#Standardization – Chris

4

Wenn Sie auf 0,1 skalieren, ändern Sie die lokale Variable vec, aber vergessen, traindata zu aktualisieren.

Darüber hinaus kann diese leicht auf diese Weise durchgeführt werden, mehr:

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

das heißt, Zeilenvektoren und Array Funktionen.

Lassen Sie mich auch hinzufügen, dass die symmetrische Eigenwertzerlegung tatsächlich schneller ist als SVD. Der wahre Vorteil von SVD in diesem Fall ist, dass es die Quadrierung der Einträge vermeidet, aber da Ihre Eingabedaten normalisiert und zentriert sind und Sie sich nur um die größten Eigenwerte kümmern, gibt es hier keine Genauigkeitsprobleme.

+0

Das war ein Fehler, in meinem großen Code habe ich einen Funktionsaufruf gemacht, der wie folgt auf den Wert zurückgeht: traindata.row (i) = normalize (traindata.row (i)); Auch hier geändert, um sicherzustellen, dass es keinen Fehler gibt. Danke für die Code-Vereinfachung, ich vermutete, dass es irgendwie möglich ist. Kannst du ein anderes Problem sehen? – Chris

+0

Eine andere Frage, mein Compiler sagt mir 'keine Übereinstimmung für' Operator/'', wenn ich Ihren Code ausführen. Ich habe Eigen3, scheint es keine reihenweise Aufteilung oder? – Chris

+0

das wird Division durch Null, wenn Sie für 1 Feature Max = Min haben, bin ich richtig? –