2015-11-04 83 views
5

我想從C++的數據集中計算2個主要成分與特徵。用特徵庫進行主成分分析

我現在這樣做的方法是對[0, 1]之間的數據進行規範化處理,然後將中間值居中。之後,我計算協方差矩陣並對其進行特徵值分解。我知道SVD速度更快,但我對計算組件感到困惑。

這裏是我是如何做到這一點(其中traindata是我的M×N的大小的輸入矩陣)的主要代碼:

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; 

這段代碼的結果是這樣的:

enter image description here

爲了確認我的結果,我嘗試了WEKA。所以我所做的就是按照這個順序使用標準化和中心過濾器。然後主要組件過濾並保存並繪製輸出。結果是這樣的:

enter image description here

技術上我應該做的是相同的,但結果是如此不同。任何人都可以看到我是否犯了一個錯誤?

+0

有一點需要補充:我很確定WEKA正在使用SVD。但是這不應該解釋結果的差異還是? – Chris

回答

1

原因是Weka 標準化了數據集。這意味着它將每個特徵的方差縮放到單位方差。當我這樣做時,情節看起來是一樣的。從技術上講,我的方法也是正確的。

+0

你能否也請發佈運行代碼?謝謝。 –

+0

我來看看,我不知道我是否仍然可以訪問那段代碼以及最終使用的版本。但我確實談到了經典的標準化,這個標準化很好定義:https://en.wikipedia.org/wiki/Feature_scaling#Standardization – Chris

4

縮放到0,1時,修改本地變量vec,但忘記更新traindata

此外,這可以更容易地完成這種方式:

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

即,使用行向量和陣列特徵。

我還要補充一點,對稱特徵值分解實際上比SVD快。在這種情況下,奇異值分解的真正優點是它避免了對輸入的平方,但是由於輸入數據是規範化和居中的,並且只關心最大特徵值,所以這裏沒有精度問題。

+0

這是一個錯誤,在我的大代碼中,我做了一個函數調用,像這樣返回值:traindata.row(i)= normalize(traindata.row(i));.爲了確保沒有錯誤,在這裏改變了它。感謝代碼簡化,我猜想它是可能的。你能看到另一個問題嗎? – Chris

+0

另一個問題是,當我運行你的代碼時,我的編譯器告訴我''operator''不匹配。我有Eigen3,似乎沒有行劃分或? – Chris

+0

如果你有1個特徵max = min,這將拋出除數爲0,對嗎? –