2015-11-04 109 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,对吗? –