鱼C论坛

 找回密码
 立即注册
查看: 1555|回复: 2

C++版本主成分分析(PCA)的特征向量与Matlab自带PCA函数特征向量正负号不同,求助。

[复制链接]
发表于 2020-5-3 16:56:15 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x

请教各位高手:

        用C++编写的主成分分析(PCA)求解得到的特征向量与Matlab自带PCA函数特征向量正负号有些不同。
        特征向量数值是相同的,特征值也一样,就特征向量的正负符号有些是相反的。
        我在网上找过两个自编版本,都出现类似问题。

        困扰好久了,请高人指点。

  1. #include <iostream>
  2. #include "Eigen/Dense"
  3. #include "pca.h"
  4. #include <fstream>

  5. using Eigen::MatrixXd;
  6. using namespace std;

  7. int main()
  8. {

  9.         int rows = 150; //number of samples
  10.         int columns = 4; //number of components
  11.         string pathData = "test_data.txt";

  12.         MatrixXd m(rows, columns);

  13.         ifstream istrm;
  14.         istrm.open(pathData);

  15.         double a{ 0 };
  16.         for (int i = 0; i < 150; i++)
  17.         {
  18.                 istrm >> a;
  19.                 m(i, 0) = a;
  20.                 istrm >> a;
  21.                 m(i, 1) = a;
  22.                 istrm >> a;
  23.                 m(i, 2) = a;
  24.                 istrm >> a;
  25.                 m(i, 3) = a;
  26.         }
  27.         istrm.close();



  28.         //for (int i = 0; i < 150; i++)
  29.         //{
  30.         //        printf("m的值:%lf,%lf,%lf,%lf\n", m(i, 0), m(i, 1), m(i, 2), m(i, 3));
  31.         //}

  32.         MatrixXd uReduced = PCA::Compute(m);
  33.         MatrixXd mReduced = m*uReduced;

  34.         cout << uReduced << endl;
  35.         system("pause");
  36. }
复制代码


上面是主函数


  1. #ifndef PCA_H
  2. #define PCA_H

  3. #include <iostream>
  4. #include <assert.h>
  5. #include <vector>
  6. #include "Eigen/Dense"
  7. #include "Eigen/Eigenvalues"

  8. using namespace Eigen;
  9. using namespace std;

  10. class PCA
  11. {
  12. public:

  13.         /*
  14.          * Computes the princcipal component of a given matrix. Computation steps:
  15.          * Compute the mean image.
  16.          * Compute standard dispersion
  17.          * Normalizing input data
  18.          * Compute the covariance matrix
  19.          * Calculate the eigenvalues and eigenvectors for the covariance matrix

  20.          * @input MatrixXd D the data samples matrix.
  21.          *
  22.          * @returns MatrixXd The eigenVectors of principal component
  23.          */

  24.         static MatrixXd Compute(MatrixXd D)
  25.         {
  26.                 // 1. Compute the mean image
  27.                 MatrixXd mean(1, D.cols());
  28.                 mean.setZero();

  29.                 for (int i = 0; i < D.cols(); i++)
  30.                 {
  31.                         mean(0, i) = D.col(i).mean();
  32.                 }


  33.                 // 2. Compute standard dispersion
  34.                 MatrixXd s(1, D.cols());
  35.                 for (int i = 0; i < D.cols(); i++)
  36.                 {
  37.                         double ss{ 0 };

  38.                         for (int j = 0; j < D.rows(); j++)
  39.                         {
  40.                                 double tmp = (D(j, i) - mean(0, i));
  41.                                 tmp *= tmp;
  42.                                 ss += tmp;
  43.                         }
  44.                         ss /= (D.rows() - 1);
  45.                         s(0, i) = sqrt(ss);
  46.                 }
  47.                 //cout << s << endl;


  48.                 // 3. Normalizing input data
  49.                 MatrixXd normD(D);
  50.                 normD.setZero();
  51.                 for (int i = 0; i < D.cols(); i++)
  52.                 {
  53.                         for (int j = 0; j < D.rows(); j++)
  54.                         {
  55.                                 normD(j, i) = (D(j, i) - mean(0, i)) / s(0, i);
  56.                         }
  57.                 }
  58.                 //cout << normD << endl;


  59.                 // 3. Compute the covariance matrix
  60.                 mean.setZero();

  61.                 for (int i = 0; i < normD.cols(); i++)
  62.                 {
  63.                         mean(0, i) = normD.col(i).mean();
  64.                 }

  65.                 MatrixXd covariance(D.cols(), D.cols());
  66.                 covariance.setZero();
  67.                 for (int k = 0; k < D.cols(); k++)
  68.                 {
  69.                         for (int j = 0; j < D.cols(); j++)
  70.                         {
  71.                                 double sum{ 0 };
  72.                                 for (int i = 0; i < D.rows(); i++)
  73.                                 {
  74.                                         sum += (normD(i, k) - mean(0, k))*(normD(i, j) - mean(0, j));
  75.                                 }
  76.                                 covariance(k, j) = sum / (D.rows() - 1);
  77.                         }
  78.                 }


  79.                 covariance(0, 0) = 0.685693512304251;
  80.                 covariance(0, 1) = -0.0424340044742729;
  81.                 covariance(0, 2) = 1.27431543624161;
  82.                 covariance(0, 3) = 0.516270693512304;

  83.                 covariance(1, 0) = -0.0424340044742729;
  84.                 covariance(1, 1) = 0.189979418344519;
  85.                 covariance(1, 2) = -0.329656375838926;
  86.                 covariance(1, 3) = -0.121639373601790;

  87.                 covariance(2, 0) = 1.27431543624161;
  88.                 covariance(2, 1) = -0.329656375838926;
  89.                 covariance(2, 2) = 3.11627785234899;
  90.                 covariance(2, 3) = 1.29560939597315;

  91.                 covariance(3, 0) = 0.516270693512304;
  92.                 covariance(3, 1) = -0.121639373601790;
  93.                 covariance(3, 2) = 1.29560939597315;
  94.                 covariance(3, 3) = 0.581006263982103;

  95.                 //cout << "斜方差" << "\n"<<endl;
  96.                 //cout << covariance << endl;


  97.                 // 4. Calculate the eigenvalues and eigenvectors for the covariance matrix
  98.                 EigenSolver<MatrixXd> solver(covariance);
  99.                 MatrixXd eigenVectors = solver.eigenvectors().real();
  100.                 VectorXd eigenValues = solver.eigenvalues().real();

  101.                 cout << eigenVectors << endl;
  102.                 cout << "\n";

  103.                 cout << eigenValues << endl;
  104.                 cout << "\n";


  105.                 // 5. Find out an number of most important principal components with broken stick method   
  106.                 int newDimension = brockenStickMethod(eigenValues);

  107.                 // 6. Return the matrix of eigenVectors appropriate most important components
  108.                 MatrixXd featureVectors(D.cols(), newDimension);

  109.                 VectorXd sortEV(eigenValues);
  110.                 sort(sortEV.derived().data(), sortEV.derived().data() + sortEV.derived().size());

  111.                 for (int i = 0; i < newDimension; i++)
  112.                 {
  113.                         double max = sortEV(sortEV.rows() - 1 - i);

  114.                         for (int j = 0; j < eigenValues.size(); j++)
  115.                         {
  116.                                 if (max == eigenValues(j))
  117.                                 {
  118.                                         VectorXd tmp = eigenVectors.col(j);
  119.                                         featureVectors.col(i) = tmp;
  120.                                 }
  121.                         }
  122.                 }
  123.                
  124.                 return featureVectors;
  125.         }

  126. private:
  127.         static int brockenStickMethod(VectorXd eigenValues)
  128.         {
  129.                 // Compute number of components, which most influed
  130.                 double trc{ eigenValues.sum() };
  131.                 VectorXd l(eigenValues.size());
  132.                 int countPrincComp{ 0 };
  133.                 l.setZero();

  134.                 for (int i = 0; i < eigenValues.size(); i++)
  135.                 {
  136.                         double sum{ 0 };
  137.                         for (int j = i; j < eigenValues.size(); j++)
  138.                         {
  139.                                 double coef = 1;
  140.                                 sum += (coef / j);
  141.                         }
  142.                         l(i) = sum / eigenValues.size();
  143.                 }


  144.                 VectorXd sortEV(eigenValues);
  145.                 sort(sortEV.derived().data(), sortEV.derived().data() + sortEV.derived().size());

  146.                 for (int i = 0; i < sortEV.size(); i++)
  147.                 {
  148.                         if ((sortEV(i) / trc) >l(i)) { countPrincComp++; }
  149.                 }

  150.                 return countPrincComp;
  151.         }
  152. };

  153. #endif // PCA_H
复制代码


这个是pac.h文件。

在测试阶段,我把主成分步骤中corr的值替换成cov,使得它和Matlab版本一致。





想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2020-5-6 00:37:07 | 显示全部楼层
我回答一下我自己的帖子,实在是花了太多的时间:

并不是编程的问题,而是:

主成分的特征向量百有两个约束条件:(1)特征向量的模为1;(2)特征向量度两两正交。在这两个条件的制约下,一问个特征值对应两个方向相反的特征向量a和-a。因此答需要再设定一个约束条件,即:取值最大的样本的主成回分的得分必须大于取值最小的样本的主成分的得分,满足这个条件的特答征向量就只有一个了。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-5-6 01:57:48 | 显示全部楼层
SugarCane88 发表于 2020-5-6 00:37
我回答一下我自己的帖子,实在是花了太多的时间:

并不是编程的问题,而是:

为了给后续寻找这个问题答案的人少走弯路,我再次回答:

主要就是这句话“forcing the largest component of each vector to be positive”
即如果想要 sign of egienvector唯一的话,确保各个成分中最大的值为正,如某个主成分向量中绝对值最大的值为负,则整行取相反即可。

例如:
主成分系数为“0.603033336168387
-0.735701512298084
-0.281699372506027
-0.125457338572313”

由于绝对值最大的“-0.735701512298084”为负,所以该主成分对应的特征向量全部取反:
“-0.603033336168387
0.735701512298084
0.281699372506027
0.125457338572313”

这样可保证与matlab,python等软件算出的特征值一致。

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-4-29 02:17

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表