前段时间写了一个HOG+PCA降维,在进行随机森林训练的时候,说得知道降维后信息保留了多少,以及得把降维后所有特征的重要性进行排序,于是删删改改终于解决了!
PCA的简单原理参见另一篇博客
代码及解释
Mat pca_1(MatrixXd samFeatureMatrix, int featureNum, int k){
// samFeatureMatrix 为原始矩阵
// featureNum 为图像原始特征维度大小
// k 为可选的降维大小
//复制一份原始矩阵,便于后续得到投影矩阵后进行相乘得到最终结果
MatrixXd X = samFeatureMatrix;
// 去均值化
RowVectorXd meanVecRow = X.colwise().mean();
X.rowwise() -= meanVecRow;
// 计算协方差矩阵
MatrixXd cov = X.transpose()*X / X.rows();
// 划重点,eigen库中在定义时可以直接调用,达到自动排序的目的,但注意是从小到大排序
SelfAdjointEigenSolver<MatrixXd> solver(cov); //有序排列
//调用库直接计算特征值和特征向量
MatrixXd eigenValues = solver.eigenvalues();
MatrixXd eigenVectors = solver.eigenvectors();
//需要查看的话,直接cout即可
cout << eigenValues << endl;
//计算可降维的维度以及保留信息的程度
// 大致思路是 降维后的特征值平方和 与 原始矩阵的特征值平方和 相除,可以估计出特征损失,也可根据特征向量的差来实现
int dim;
double sum = 0;
// 简单的 for 求和,知道满足条件,输出结果
for (int i = eigenValues.rows()-1; i >= 0; --i)
{
sum += eigenValues(i, 0);
dim = i;
if (sum / eigenValues.sum() >= 0.90) //信息保留90
break;
}
int final_dim = eigenValues.rows() - dim;
//实际维度
cout << "---------------result---------" << endl;
cout << final_dim << endl;
// 最终结果,将原始矩阵与我们的特征向量的最后final_dim列进行相乘,得到最终投影矩阵
MatrixXd projection = samFeatureMatrix * eigenVectors.rightCols(final_dim);
// eigen::Matrix -> cv::mat,类型转换
Mat projectionMat;
eigen2cv(projection, projectionMat);
cout << projectionMat.cols << endl << projectionMat.rows << endl;
cout << projectionMat << endl;
return projectionMat;
}
int main(){
//hog_pca
MatrixXd descriptorValues;
descriptorValues = get_hog_feature();
pca_1(descriptorValues, 324, 38);
return 0;
}
总结
总的来说是很简单的,只要掌握了原理,明白自己每一步要干什么就可以,同时注意合理调用库函数,减少工作量;
优化的两个亮点:
- 实现了特征重要性排序,但注意是从小到大排序,所以在最后选择主要成分的时候需要选择右边的列,而不是左边的列;
- 实现了维度的选择和信息保留程度的量化处理,便于后续分析该特征是否可用。