PCA.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. /**
  2. * @file PCA.cpp
  3. * @brief Computation of a PCA by Eigenvalue Decomposition, Karhunen Loewe Transform
  4. * @author Michael Koch
  5. * @date 5/27/2008
  6. */
  7. /** \example testPCA.cpp
  8. * This is an example of how to use the Principal Component Analysis (PCA) class.
  9. */
  10. // #define DEBUG
  11. #undef DEBUG
  12. #include <vislearning/nice_nonvis.h>
  13. #include <iostream>
  14. #include <math.h>
  15. #include <vector>
  16. #include <map>
  17. #include "vislearning/math/algebra/AlgebraTools.h"
  18. #include "vislearning/math/algebra/GenericMatrix.h"
  19. #include "vislearning/math/algebra/EigValues.h"
  20. #ifdef NICE_USELIB_TRLAN
  21. #include "vislearning/math/algebra_trlan/EigValuesTRLAN.h"
  22. #endif
  23. // #include "vislearning/fourier/FourierLibrary.h"
  24. #include "PCA.h"
  25. #include "vislearning/baselib/Gnuplot.h"
  26. #ifdef NICE_USELIB_ICE
  27. #include <image_nonvis.h>
  28. #include "core/iceconversion/convertice.h"
  29. #include "core/iceconversion/image_convertice.h"
  30. using namespace ice;
  31. #endif
  32. using namespace OBJREC;
  33. using namespace std;
  34. using namespace NICE;
  35. PCA::PCA(uint dim, uint maxiteration, double mindelta)
  36. {
  37. init(dim, maxiteration, mindelta);
  38. }
  39. PCA::PCA(void)
  40. {
  41. init();
  42. }
  43. PCA::~PCA()
  44. {
  45. }
  46. void PCA::init(uint dim, uint maxiteration, double mindelta)
  47. {
  48. this->targetDimension = dim;
  49. this->maxiteration = maxiteration;
  50. this->mindelta = mindelta;
  51. }
  52. void PCA::restore(istream & is, int format)
  53. {
  54. is >> basis;
  55. is >> normalization;
  56. is >> mean;
  57. is >> targetDimension;
  58. }
  59. void PCA::store(ostream & os, int format) const
  60. {
  61. os << basis << normalization << mean << targetDimension;
  62. }
  63. void PCA::clear()
  64. {
  65. }
  66. void PCA::calculateBasis(const NICE::Matrix &features,
  67. const uint targetDimension, const uint mode)
  68. {
  69. calculateBasis(features, targetDimension, mode, false);
  70. }
  71. void PCA::calculateBasis(const NICE::Matrix &features,
  72. const uint targetDimension, const uint mode, const bool adaptive,
  73. const double targetRatio)
  74. {
  75. this->targetDimension = targetDimension;
  76. uint srcFeatureSize = features.cols();
  77. uint mindimension = std::min(this->targetDimension, srcFeatureSize);
  78. #ifdef DEBUG
  79. cout << "calculateBasis ... stay a while and listen ..." << endl;
  80. #endif
  81. NICE::Matrix eigenvectors;
  82. NICE::Vector eigenvalue;
  83. #ifdef DEBUG
  84. cout << "EigenValue Decomposition Working..." << endl;
  85. #endif
  86. //switch mode: svd, lanczos, broken lanczos, arnoldi
  87. //switch eigenvalue decomposition
  88. switch (mode)
  89. {
  90. case 0:
  91. {
  92. #ifndef NICE_USELIB_ICE
  93. fthrow(Exception, "PCA: ICE needed for mode 0");
  94. #else
  95. #ifdef DEBUG
  96. fprintf(stderr, "PCA: warning covariance computation in progress, memory and time consuming ...!\n");
  97. #endif
  98. ice::Matrix sigma;
  99. ice::Matrix eigenvectors_ice;
  100. ice::Vector eigenvalue_ice;
  101. ice::Matrix features_ice(NICE::makeICEMatrix(features));
  102. Statistics st(srcFeatureSize);
  103. #ifdef DEBUG
  104. fprintf(stderr, "Transform Features ... %d %d \n", features_ice.rows(), features_ice.cols());
  105. #endif
  106. Put(st, features_ice);
  107. ice::Vector mean_ice = Mean(st);
  108. mean = makeEVector(mean_ice);
  109. #ifdef DEBUG
  110. cout << "MEAN: " << mean_ice << endl;
  111. #endif
  112. sigma = Covariance(st);
  113. #ifdef DEBUG
  114. fprintf(stderr, "Covariance Ready \n");
  115. fprintf(stderr, "Eigenvalue Computation ... \n");
  116. #endif
  117. Eigenvalue(sigma, eigenvalue_ice, eigenvectors_ice);
  118. NICE::Matrix eigenvectors_tmp;
  119. eigenvectors_tmp = makeDoubleMatrix(eigenvectors_ice); //bad error
  120. eigenvectors.resize(eigenvectors_tmp.rows(), eigenvectors_tmp.cols());
  121. eigenvectors = eigenvectors_tmp;
  122. eigenvalue = makeEVector(eigenvalue_ice);
  123. #endif
  124. break;
  125. }
  126. case 1:
  127. {
  128. #ifdef DEBUG
  129. fprintf(stderr, "Calc mean \n");
  130. #endif
  131. AlgebraTools::calculateMean(features, mean);
  132. #ifdef DEBUG
  133. fprintf(stderr, "fastest available method \n");
  134. // cerr<<"mean:"<<mean<<endl;
  135. #endif
  136. #ifdef NICE_USELIB_TRLAN
  137. EigValues *eig = new EigValuesTRLAN();//fast lanczos TRLAN
  138. #else
  139. EigValues *eig = new EVArnoldi();//Arnoldi for (srcFeatureSize<n)
  140. #endif
  141. NICE::Matrix features_transpose = features.transpose();
  142. GMCovariance C(&features_transpose);
  143. #ifdef DEBUG
  144. fprintf(stderr, "Perform PCA\n");
  145. fprintf(stderr, "Matrix size %d %d %d %d \n", features.rows(), features.cols(), C.rows(), C.cols());
  146. #endif
  147. if (adaptive)
  148. {
  149. eig->getEigenvalues(C, eigenvalue, eigenvectors, srcFeatureSize);
  150. }
  151. else
  152. {
  153. eig->getEigenvalues(C, eigenvalue, eigenvectors, mindimension);
  154. }
  155. #ifdef DEBUG
  156. fprintf(stderr, "PCA: Computation ready \n");
  157. // cerr<<eigenvectors<<endl;
  158. #endif
  159. #ifdef DEBUG
  160. fprintf(stderr, "PCA: Transpose ready \n");
  161. // cerr<<eigenvectors<<endl;
  162. #endif
  163. break;
  164. }
  165. #ifdef USE_BROKEN_LANCZOS
  166. case 2:
  167. {
  168. //Experimental TODO
  169. #ifdef DEBUG
  170. fprintf(stderr, "Calc mean \n");
  171. #endif
  172. AlgebraTools::calculateMean(features, mean);
  173. #ifdef DEBUG
  174. fprintf(stderr, "Lanczos \n");
  175. #endif
  176. EigValues *eig = new EVLanczos(100, 1e-4);
  177. GMCovariance C(&features.transpose());
  178. if(adaptive)
  179. {
  180. eig->getEigenvalues(C, eigenvalue, eigenvectors, srcFeatureSize);
  181. }
  182. else
  183. {
  184. eig->getEigenvalues(C, eigenvalue, eigenvectors, mindimension); //fast lanczos
  185. }
  186. #ifdef DEBUG
  187. fprintf(stderr, "PCA: Computation ready \n");
  188. #endif
  189. break;
  190. }
  191. #endif
  192. case 3:
  193. {
  194. #ifdef DEBUG
  195. fprintf(stderr, "Calc mean \n");
  196. #endif
  197. AlgebraTools::calculateMean(features, mean);
  198. #ifdef DEBUG
  199. fprintf(stderr, "Arnoldi \n");
  200. #endif
  201. EigValues *eig = new EVArnoldi(100, 1e-4);
  202. NICE::Matrix tmppointer = features.transpose();
  203. GMCovariance C(&tmppointer);
  204. if (adaptive)
  205. {
  206. eig->getEigenvalues(C, eigenvalue, eigenvectors, srcFeatureSize);
  207. }
  208. else
  209. {
  210. eig->getEigenvalues(C, eigenvalue, eigenvectors, mindimension); //fast arnoldi
  211. }
  212. #ifdef DEBUG
  213. fprintf(stderr, "PCA: Computation ready \n");
  214. #endif
  215. #ifdef DEBUG
  216. fprintf(stderr, "PCA: Transpose ready \n");
  217. #endif
  218. break;
  219. }
  220. default:
  221. fprintf(stderr, "PCA: Wrong Computation mode: %d!\n", mode);
  222. break;
  223. }
  224. #ifdef DEBUG
  225. fprintf(stderr, "Eigenvalue Decomposition ready \n");
  226. cerr << eigenvectors << endl;
  227. cerr << eigenvalue << endl;
  228. //sort values
  229. fprintf(stderr, "Eigenvector-Rows:%i Eigenvector-Cols:%i\n", (int)eigenvectors.rows(), (int)eigenvectors.cols());
  230. #endif
  231. multimap<double, NICE::Vector> map;
  232. double sumeigen = 0.0;
  233. NICE::Vector ratio(eigenvectors.cols());
  234. for (uint i = 0; i < eigenvectors.cols(); i++)//every eigenvector
  235. {
  236. NICE::Vector eigenvector(srcFeatureSize);
  237. for (uint k = 0; k < srcFeatureSize; k++)
  238. {
  239. eigenvector[k] = eigenvectors(k, i);
  240. }
  241. map.insert(pair<double, NICE::Vector> (eigenvalue[i], eigenvector));
  242. sumeigen += eigenvalue[i];
  243. }
  244. //compute basis size
  245. if (adaptive)
  246. { //compute target dimension
  247. uint dimensioncount = 0;
  248. double addedratio = 0.0;
  249. multimap<double, NICE::Vector>::reverse_iterator it = map.rbegin();
  250. while (addedratio <= targetRatio && it != map.rend())
  251. {
  252. //calc ratio
  253. ratio[dimensioncount] = (*it).first / sumeigen;
  254. addedratio += ratio[dimensioncount];
  255. dimensioncount++;
  256. it++;
  257. }
  258. this->targetDimension = dimensioncount;
  259. }
  260. mindimension = std::min(this->targetDimension, srcFeatureSize);
  261. this->targetDimension = mindimension;
  262. basis = NICE::Matrix(srcFeatureSize, mindimension);
  263. //get sorted values
  264. uint count = 0;
  265. multimap<double, NICE::Vector>::reverse_iterator it = map.rbegin();
  266. while (count < this->targetDimension && it != map.rend())
  267. {
  268. NICE::Vector eigenvector = (*it).second;
  269. //put eigenvector into column
  270. for (uint k = 0; k < srcFeatureSize; k++)
  271. {
  272. basis(k, count) = eigenvector[k];
  273. }
  274. //calc ratio
  275. ratio[count] = (*it).first / sumeigen;
  276. count++;
  277. it++;
  278. }
  279. //normalization matrix / modify variance to 1 for all eigenvectors
  280. normalization = NICE::Matrix(mindimension, mindimension, 0);
  281. for (uint k = 0; k < mindimension; k++)
  282. {
  283. normalization(k, k) = 1.0 / sqrt(eigenvalue[k]);
  284. }
  285. #ifdef DEBUG
  286. cerr << basis << endl;
  287. // if (mode == 0)
  288. // {
  289. // cout << eigenvectors << endl;
  290. // cout << basis << endl;
  291. // cout << mean << endl;
  292. cout << "Eigenvalue-absolute:" << eigenvalue << endl;
  293. cout << "Eigenvalue-ratio:" << ratio << endl;
  294. // }
  295. #endif
  296. }
  297. NICE::Vector PCA::getFeatureVector(const NICE::Vector &data,
  298. const bool normalize)
  299. {
  300. //free data of mean
  301. if (normalize)
  302. {
  303. NICE::Vector meanfree(data);
  304. meanfree -= mean;
  305. //Y=W^t * B^T
  306. if(normbasis.rows() == 0)
  307. normbasis.multiply(normalization, basis, false, true);
  308. NICE::Vector tmp;
  309. tmp.multiply(normbasis, meanfree);
  310. return tmp;
  311. }
  312. else
  313. {
  314. NICE::Vector meanfree(data);
  315. meanfree -= mean;
  316. //Y=W^t * B^T
  317. NICE::Vector tmp;
  318. tmp.multiply(basis, meanfree, true);
  319. return tmp;
  320. }
  321. }
  322. NICE::Vector PCA::getMean()
  323. {
  324. return mean;
  325. }
  326. NICE::Matrix PCA::getBasis()
  327. {
  328. return basis;
  329. }
  330. int PCA::getTargetDim()
  331. {
  332. return targetDimension;
  333. }