FastMinKernel.cpp 58 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804
  1. /**
  2. * @file FastMinKernel.cpp
  3. * @brief Efficient GPs with HIK for classification by regression (Implementation)
  4. * @author Alexander Freytag
  5. * @date 06-12-2011 (dd-mm-yyyy)
  6. */
  7. // STL includes
  8. #include <iostream>
  9. // NICE-core includes
  10. #include <core/basics/vectorio.h>
  11. #include <core/basics/Timer.h>
  12. // gp-hik-core includes
  13. #include "FastMinKernel.h"
  14. using namespace std;
  15. using namespace NICE;
  16. /* protected methods*/
  17. /////////////////////////////////////////////////////
  18. /////////////////////////////////////////////////////
  19. // PUBLIC METHODS
  20. /////////////////////////////////////////////////////
  21. /////////////////////////////////////////////////////
  22. FastMinKernel::FastMinKernel()
  23. {
  24. this->ui_d = 0;
  25. this->ui_n = 0;
  26. this->d_noise = 1.0;
  27. this->approxScheme = MEDIAN;
  28. this->b_verbose = false;
  29. this->setDebug(false);
  30. }
  31. FastMinKernel::FastMinKernel( const std::vector<std::vector<double> > & _X,
  32. const double _noise,
  33. const bool _debug,
  34. const uint & _dim
  35. )
  36. {
  37. this->setDebug(_debug);
  38. this->X_sorted.set_features( _X, _dim);
  39. this->ui_d = this->X_sorted.get_d();
  40. this->ui_n = this->X_sorted.get_n();
  41. this->d_noise = _noise;
  42. this->approxScheme = MEDIAN;
  43. this->b_verbose = false;
  44. }
  45. #ifdef NICE_USELIB_MATIO
  46. FastMinKernel::FastMinKernel ( const sparse_t & _X,
  47. const double _noise,
  48. const std::map<uint, uint> & _examples,
  49. const bool _debug,
  50. const uint & _dim
  51. ) : X_sorted( _X, _examples, _dim )
  52. {
  53. this->ui_d = this->X_sorted.get_d();
  54. this->ui_n = this->X_sorted.get_n();
  55. this->d_noise = _noise;
  56. this->approxScheme = MEDIAN;
  57. this->b_verbose = false;
  58. this->setDebug(_debug);
  59. }
  60. #endif
  61. FastMinKernel::FastMinKernel ( const std::vector< const NICE::SparseVector * > & _X,
  62. const double _noise,
  63. const bool _debug,
  64. const bool & _dimensionsOverExamples,
  65. const uint & _dim)
  66. {
  67. this->setDebug(_debug);
  68. this->X_sorted.set_features( _X, _dimensionsOverExamples, _dim);
  69. this->ui_d = this->X_sorted.get_d();
  70. this->ui_n = this->X_sorted.get_n();
  71. this->d_noise = _noise;
  72. this->approxScheme = MEDIAN;
  73. this->b_verbose = false;
  74. }
  75. FastMinKernel::~FastMinKernel()
  76. {
  77. }
  78. ///////////////////// ///////////////////// /////////////////////
  79. // GET / SET
  80. // INCLUDING ACCESS OPERATORS
  81. ///////////////////// ///////////////////// ////////////////////
  82. uint FastMinKernel::get_n() const
  83. {
  84. return this->ui_n;
  85. }
  86. uint FastMinKernel::get_d() const
  87. {
  88. return this->ui_d;
  89. }
  90. double FastMinKernel::getSparsityRatio() const
  91. {
  92. return this->X_sorted.computeSparsityRatio();
  93. }
  94. void FastMinKernel::setVerbose( const bool & _verbose)
  95. {
  96. this->b_verbose = _verbose;
  97. }
  98. bool FastMinKernel::getVerbose( ) const
  99. {
  100. return this->b_verbose;
  101. }
  102. void FastMinKernel::setDebug( const bool & _debug)
  103. {
  104. this->b_debug = _debug;
  105. this->X_sorted.setDebug( _debug );
  106. }
  107. bool FastMinKernel::getDebug( ) const
  108. {
  109. return this->b_debug;
  110. }
  111. ///////////////////// ///////////////////// /////////////////////
  112. // CLASSIFIER STUFF
  113. ///////////////////// ///////////////////// /////////////////////
  114. void FastMinKernel::applyFunctionToFeatureMatrix ( const NICE::ParameterizedFunction *_pf)
  115. {
  116. this->X_sorted.applyFunctionToFeatureMatrix( _pf );
  117. }
  118. void FastMinKernel::hik_prepare_alpha_multiplications(const NICE::Vector & _alpha,
  119. NICE::VVector & _A,
  120. NICE::VVector & _B) const
  121. {
  122. // std::cerr << "FastMinKernel::hik_prepare_alpha_multiplications" << std::endl;
  123. // std::cerr << "alpha: " << alpha << std::endl;
  124. _A.resize( this->ui_d );
  125. _B.resize( this->ui_d );
  126. // efficient calculation of k*alpha
  127. // ---------------------------------
  128. //
  129. // sum_i alpha_i k(x^i,x) = sum_i alpha_i sum_k min(x^i_k,x_k)
  130. // = sum_k sum_i alpha_i min(x^i_k, x_k)
  131. //
  132. // now let us define l_k = { i | x^i_k <= x_k }
  133. // and u_k = { i | x^i_k > x_k }, this leads to
  134. //
  135. // = sum_k ( sum_{l \in l_k} alpha_l x^i_k + sum_{u \in u_k} alpha_u x_k
  136. // = sum_k ( sum_{l \in l_k} \alpha_l x^l_k + x_k * sum_{u \in u_k}
  137. // alpha_u
  138. //
  139. // We also define
  140. // l^j_k = { i | x^i_j <= x^j_k } and
  141. // u^j_k = { i | x^i_k > x^j_k }
  142. //
  143. // We now need the partial sums
  144. //
  145. // (Definition 1)
  146. // a_{k,j} = \sum_{l \in l^j_k} \alpha_l x^l_k
  147. //
  148. // and \sum_{u \in u^j_k} \alpha_u
  149. // according to increasing values of x^l_k
  150. //
  151. // With
  152. // (Definition 2)
  153. // b_{k,j} = \sum_{l \in l^j_k} \alpha_l,
  154. //
  155. // we get
  156. // \sum_{u \in u^j_k} \alpha_u = \sum_{u=1}^n alpha_u - \sum_{l \in l^j_k} \alpha_l
  157. // = b_{k,n} - b_{k,j}
  158. // we only need as many entries as we have nonZero entries in our features for the corresponding dimensions
  159. for (uint i = 0; i < this->ui_d; i++)
  160. {
  161. uint numNonZero = this->X_sorted.getNumberOfNonZeroElementsPerDimension(i);
  162. //DEBUG
  163. //std::cerr << "number of non-zero elements in dimension " << i << " / " << d << ": " << numNonZero << std::endl;
  164. _A[i].resize( numNonZero );
  165. _B[i].resize( numNonZero );
  166. }
  167. // for more information see hik_prepare_alpha_multiplications
  168. for (uint dim = 0; dim < this->ui_d; dim++)
  169. {
  170. double alpha_sum(0.0);
  171. double alpha_times_x_sum(0.0);
  172. uint cntNonzeroFeat(0);
  173. const multimap< double, SortedVectorSparse<double>::dataelement> & nonzeroElements = this->X_sorted.getFeatureValues(dim).nonzeroElements();
  174. // loop through all elements in sorted order
  175. for ( SortedVectorSparse<double>::const_elementpointer i = nonzeroElements.begin(); i != nonzeroElements.end(); i++ )
  176. {
  177. const SortedVectorSparse<double>::dataelement & de = i->second;
  178. // index of the feature
  179. int index = de.first;
  180. // transformed element of the feature
  181. //
  182. double elem( de.second );
  183. alpha_times_x_sum += _alpha[index] * elem;
  184. _A[dim][cntNonzeroFeat] = alpha_times_x_sum;
  185. alpha_sum += _alpha[index];
  186. _B[dim][cntNonzeroFeat] = alpha_sum;
  187. cntNonzeroFeat++;
  188. }
  189. }
  190. }
  191. double *FastMinKernel::hik_prepare_alpha_multiplications_fast(const NICE::VVector & _A,
  192. const NICE::VVector & _B,
  193. const Quantization * _q,
  194. const ParameterizedFunction *_pf
  195. ) const
  196. {
  197. //NOTE keep in mind: for doing this, we already have precomputed A and B using hik_prepare_alpha_multiplications!
  198. // number of quantization bins
  199. uint hmax = _q->getNumberOfBins();
  200. // store (transformed) prototypes
  201. double * prototypes = new double [ hmax * this->ui_d ];
  202. double * p_prototypes = prototypes;
  203. for (uint dim = 0; dim < this->ui_d; dim++)
  204. {
  205. for ( uint i = 0 ; i < hmax ; i++ )
  206. {
  207. if ( _pf != NULL )
  208. {
  209. *p_prototypes = _pf->f ( dim, _q->getPrototype( i, dim ) );
  210. } else
  211. {
  212. *p_prototypes = _q->getPrototype( i, dim );
  213. }
  214. p_prototypes++;
  215. }
  216. }
  217. // creating the lookup table as pure C, which might be beneficial
  218. // for fast evaluation
  219. double *Tlookup = new double [ hmax * this->ui_d ];
  220. // std::cerr << "size of LUT: " << hmax * this->ui_d << std::endl;
  221. // sizeOfLUT = hmax * this->d;
  222. // loop through all dimensions
  223. for ( uint dim = 0; dim < this->ui_d; dim++ )
  224. {
  225. uint nrZeroIndices = this->X_sorted.getNumberOfZeroElementsPerDimension(dim);
  226. if ( nrZeroIndices == this->ui_n )
  227. continue;
  228. const multimap< double, SortedVectorSparse<double>::dataelement> & nonzeroElements = this->X_sorted.getFeatureValues(dim).nonzeroElements();
  229. SortedVectorSparse<double>::const_elementpointer i = nonzeroElements.begin();
  230. SortedVectorSparse<double>::const_elementpointer iPredecessor = nonzeroElements.begin();
  231. // index of the element, which is always bigger than the current value fval
  232. uint index = 0;
  233. // we use the quantization of the original features! the transformed feature were
  234. // already used to calculate A and B, this of course assumes monotonic functions!!!
  235. uint qBin = _q->quantize ( i->first, dim );
  236. // the next loop is linear in max(hmax, n)
  237. // REMARK: this could be changed to hmax*log(n), when
  238. // we use binary search
  239. for (uint j = 0; j < hmax; j++)
  240. {
  241. double fval = prototypes[ dim*hmax + j ];
  242. double t;
  243. if ( (index == 0) && (j < qBin) ) {
  244. // current element is smaller than everything else
  245. // resulting value = fval * sum_l=1^n alpha_l
  246. t = fval*( _B[dim][this->ui_n-1 - nrZeroIndices] );
  247. } else {
  248. // move to next example, if necessary
  249. while ( (j >= qBin) && ( index < (this->ui_n-1-nrZeroIndices)) )
  250. {
  251. index++;
  252. iPredecessor = i;
  253. i++;
  254. if ( i->first != iPredecessor->first )
  255. qBin = _q->quantize ( i->first, dim );
  256. }
  257. // compute current element in the lookup table and keep in mind that
  258. // index is the next element and not the previous one
  259. //NOTE pay attention: this is only valid if all entries are positive! -
  260. // If not, ask whether the current feature is greater than zero. If so, subtract the nrZeroIndices, if not do not
  261. if ( (j >= qBin) && ( index==(this->ui_n-1-nrZeroIndices) ) ) {
  262. // the current element (fval) is equal or bigger to the element indexed by index
  263. // in fact, the term B[dim][this->n-1-nrZeroIndices] - B[dim][index] is equal to zero and vanishes, which is logical, since all elements are smaller than j!
  264. t = _A[dim][index];// + fval*( _B[dim][this->ui_n-1-nrZeroIndices] - _B[dim][index] );
  265. } else {
  266. // standard case
  267. t = _A[dim][index-1] + fval*( _B[dim][this->ui_n-1-nrZeroIndices] - _B[dim][index-1] );
  268. }
  269. }
  270. Tlookup[ dim*hmax + j ] = t;
  271. }
  272. }
  273. delete [] prototypes;
  274. return Tlookup;
  275. }
  276. double *FastMinKernel::hikPrepareLookupTable(const NICE::Vector & _alpha,
  277. const Quantization * _q,
  278. const ParameterizedFunction *_pf
  279. ) const
  280. {
  281. // number of quantization bins
  282. uint hmax = _q->getNumberOfBins();
  283. // store (transformed) prototypes
  284. double * prototypes = new double [ hmax * this->ui_d ];
  285. double * p_prototypes = prototypes;
  286. for (uint dim = 0; dim < this->ui_d; dim++)
  287. {
  288. for ( uint i = 0 ; i < hmax ; i++ )
  289. {
  290. if ( _pf != NULL )
  291. {
  292. *p_prototypes = _pf->f ( dim, _q->getPrototype( i, dim ) );
  293. } else
  294. {
  295. *p_prototypes = _q->getPrototype( i, dim );
  296. }
  297. p_prototypes++;
  298. }
  299. }
  300. // creating the lookup table as pure C, which might be beneficial
  301. // for fast evaluation
  302. double *Tlookup = new double [ hmax * this->ui_d ];
  303. // sizeOfLUT = hmax * this->d;
  304. // loop through all dimensions
  305. for (uint dim = 0; dim < this->ui_d; dim++)
  306. {
  307. uint nrZeroIndices = this->X_sorted.getNumberOfZeroElementsPerDimension(dim);
  308. if ( nrZeroIndices == this->ui_n )
  309. continue;
  310. const multimap< double, SortedVectorSparse<double>::dataelement> & nonzeroElements = this->X_sorted.getFeatureValues(dim).nonzeroElements();
  311. double alphaSumTotalInDim(0.0);
  312. double alphaTimesXSumTotalInDim(0.0);
  313. for ( SortedVectorSparse<double>::const_elementpointer i = nonzeroElements.begin(); i != nonzeroElements.end(); i++ )
  314. {
  315. alphaSumTotalInDim += _alpha[i->second.first];
  316. alphaTimesXSumTotalInDim += _alpha[i->second.first] * i->second.second;
  317. }
  318. SortedVectorSparse<double>::const_elementpointer i = nonzeroElements.begin();
  319. SortedVectorSparse<double>::const_elementpointer iPredecessor = nonzeroElements.begin();
  320. // index of the element, which is always bigger than the current value fval
  321. uint index = 0;
  322. // we use the quantization of the original features! Nevetheless, the resulting lookupTable is computed using the transformed ones
  323. uint qBin = _q->quantize ( i->first, dim );
  324. double alpha_sum(0.0);
  325. double alpha_times_x_sum(0.0);
  326. double alpha_sum_prev(0.0);
  327. double alpha_times_x_sum_prev(0.0);
  328. for (uint j = 0; j < hmax; j++)
  329. {
  330. double fval = prototypes[ dim*hmax + j ];
  331. double t;
  332. if ( (index == 0) && (j < qBin) ) {
  333. // current element is smaller than everything else
  334. // resulting value = fval * sum_l=1^n alpha_l
  335. //t = fval*( B[dim][this->n-1 - nrZeroIndices] );
  336. t = fval*alphaSumTotalInDim;
  337. } else {
  338. // move to next example, if necessary
  339. while ( (j >= qBin) && ( index < (this->ui_n-1-nrZeroIndices)) )
  340. {
  341. alpha_times_x_sum_prev = alpha_times_x_sum;
  342. alpha_sum_prev = alpha_sum;
  343. alpha_times_x_sum += _alpha[i->second.first] * i->second.second; //i->dataElement.transformedFeatureValue
  344. alpha_sum += _alpha[i->second.first]; //i->dataElement.OrigIndex
  345. index++;
  346. iPredecessor = i;
  347. i++;
  348. if ( i->first != iPredecessor->first )
  349. qBin = _q->quantize ( i->first, dim );
  350. }
  351. // compute current element in the lookup table and keep in mind that
  352. // index is the next element and not the previous one
  353. //NOTE pay attention: this is only valid if all entries are positiv! - if not, ask wether the current feature is greater than zero. If so, subtract the nrZeroIndices, if not do not
  354. if ( (j >= qBin) && ( index==(this->ui_n-1-nrZeroIndices) ) ) {
  355. // the current element (fval) is equal or bigger to the element indexed by index
  356. // in fact, the term B[dim][this->n-1-nrZeroIndices] - B[dim][index] is equal to zero and vanishes, which is logical, since all elements are smaller than j!
  357. // double lastTermAlphaTimesXSum;
  358. // double lastTermAlphaSum;
  359. t = alphaTimesXSumTotalInDim;
  360. } else {
  361. // standard case
  362. t = alpha_times_x_sum + fval*( alphaSumTotalInDim - alpha_sum );
  363. }
  364. }
  365. Tlookup[ dim*hmax + j ] = t;
  366. }
  367. }
  368. delete [] prototypes;
  369. return Tlookup;
  370. }
  371. void FastMinKernel::hikUpdateLookupTable(double * _T,
  372. const double & _alphaNew,
  373. const double & _alphaOld,
  374. const uint & _idx,
  375. const Quantization * _q,
  376. const ParameterizedFunction *_pf
  377. ) const
  378. {
  379. if (_T == NULL)
  380. {
  381. fthrow(Exception, "FastMinKernel::hikUpdateLookupTable LUT not initialized, run FastMinKernel::hikPrepareLookupTable first!");
  382. return;
  383. }
  384. // number of quantization bins
  385. uint hmax = _q->getNumberOfBins();
  386. // store (transformed) prototypes
  387. double * prototypes = new double [ hmax * this->ui_d ];
  388. double * p_prototypes = prototypes;
  389. for (uint dim = 0; dim < this->ui_d; dim++)
  390. {
  391. for ( uint i = 0 ; i < hmax ; i++ )
  392. {
  393. if ( _pf != NULL )
  394. {
  395. *p_prototypes = _pf->f ( dim, _q->getPrototype( i, dim ) );
  396. } else
  397. {
  398. *p_prototypes = _q->getPrototype( i, dim );
  399. }
  400. p_prototypes++;
  401. }
  402. }
  403. double diffOfAlpha(_alphaNew - _alphaOld);
  404. // loop through all dimensions
  405. for ( uint dim = 0; dim < this->ui_d; dim++ )
  406. {
  407. double x_i ( (this->X_sorted( dim, _idx)) );
  408. //TODO we could also check wether x_i < tol, if we would store the tol explicitely
  409. if ( x_i == 0.0 ) //nothing to do in this dimension
  410. continue;
  411. //TODO we could speed up this by first doing a binary search for the position where the min changes, and then do two separate for-loops
  412. for (uint j = 0; j < hmax; j++)
  413. {
  414. double fval;
  415. uint q_bin = _q->quantize( x_i, dim );
  416. if ( q_bin > j )
  417. fval = prototypes[ dim*hmax + j ];
  418. else
  419. fval = x_i;
  420. _T[ dim*hmax + j ] += diffOfAlpha*fval;
  421. }
  422. }
  423. delete [] prototypes;
  424. }
  425. void FastMinKernel::hik_kernel_multiply(const NICE::VVector & _A,
  426. const NICE::VVector & _B,
  427. const NICE::Vector & _alpha,
  428. NICE::Vector & _beta
  429. ) const
  430. {
  431. _beta.resize( this->ui_n );
  432. _beta.set(0.0);
  433. // runtime is O(n*d), we do no benefit from an additional lookup table here
  434. for (uint dim = 0; dim < this->ui_d; dim++)
  435. {
  436. // -- efficient sparse solution
  437. const multimap< double, SortedVectorSparse<double>::dataelement> & nonzeroElements = this->X_sorted.getFeatureValues(dim).nonzeroElements();
  438. uint nrZeroIndices = this->X_sorted.getNumberOfZeroElementsPerDimension(dim);
  439. if ( nrZeroIndices == this->ui_n ) {
  440. // all values are zero in this dimension :) and we can simply ignore the feature
  441. continue;
  442. }
  443. uint cnt(0);
  444. for ( multimap< double, SortedVectorSparse<double>::dataelement>::const_iterator i = nonzeroElements.begin(); i != nonzeroElements.end(); i++, cnt++)
  445. {
  446. const SortedVectorSparse<double>::dataelement & de = i->second;
  447. uint feat = de.first;
  448. uint inversePosition = cnt;
  449. double fval = de.second;
  450. // in which position was the element sorted in? actually we only care about the nonzero elements, so we have to subtract the number of zero elements.
  451. //NOTE pay attention: this is only valid if all entries are positiv! - if not, ask wether the current feature is greater than zero. If so, subtract the nrZeroIndices, if not do not
  452. //we definitly know that this element exists in inversePermutation, so we have not to check wether find returns .end() or not
  453. //int inversePosition(inversePermutation.find(feat)->second - nrZeroIndices);
  454. // sum_{l \in L_k} \alpha_l x^l_k
  455. //
  456. // A is zero for zero feature values (x^l_k is zero for all l \in L_k)
  457. double firstPart( _A[dim][inversePosition] );
  458. // sum_{u \in U_k} alpha_u
  459. // B is not zero for zero feature values, but we do not
  460. // have to care about them, because it is multiplied with
  461. // the feature value
  462. // DEBUG for Björns code
  463. if ( dim >= _B.size() )
  464. fthrow(Exception, "dim exceeds B.size: " << dim << " " << _B.size() );
  465. if ( _B[dim].size() == 0 )
  466. fthrow(Exception, "B[dim] is empty");
  467. if ( (this->ui_n-1-nrZeroIndices < 0) || ((uint)(this->ui_n-1-nrZeroIndices) >= _B[dim].size() ) )
  468. fthrow(Exception, "n-1-nrZeroIndices is invalid: " << this->ui_n << " " << nrZeroIndices << " " << _B[dim].size() << " d: " << this->ui_d);
  469. if ( inversePosition < 0 || (uint)inversePosition >= _B[dim].size() )
  470. fthrow(Exception, "inverse position is invalid: " << inversePosition << " " << _B[dim].size() );
  471. double secondPart( _B[dim][this->ui_n-1-nrZeroIndices] - _B[dim][inversePosition]);
  472. _beta[feat] += firstPart + fval * secondPart; // i->elementpointer->dataElement->Value
  473. }
  474. }
  475. // The following code simply adds noise * alpha to the result
  476. // to calculate the multiplication with the regularized kernel matrix.
  477. //
  478. // Do we really want to considere noisy labels?
  479. // Yes, otherwise this would be not consistent with solveLin etc.
  480. for (uint feat = 0; feat < this->ui_n; feat++)
  481. {
  482. _beta[feat] += this->d_noise*_alpha[feat];
  483. }
  484. }
  485. void FastMinKernel::hik_kernel_multiply_fast(const double *_Tlookup,
  486. const Quantization * _q,
  487. const NICE::Vector & _alpha,
  488. NICE::Vector & _beta) const
  489. {
  490. _beta.resize( this->ui_n );
  491. _beta.set(0.0);
  492. // runtime is O(n*d), we do no benefit from an additional lookup table here
  493. for (uint dim = 0; dim < this->ui_d; dim++)
  494. {
  495. // -- efficient sparse solution
  496. const multimap< double, SortedVectorSparse<double>::dataelement> & nonzeroElements = this->X_sorted.getFeatureValues(dim).nonzeroElements();
  497. uint cnt(0);
  498. for ( multimap< double, SortedVectorSparse<double>::dataelement>::const_iterator i = nonzeroElements.begin(); i != nonzeroElements.end(); i++, cnt++)
  499. {
  500. const SortedVectorSparse<double>::dataelement & de = i->second;
  501. uint feat = de.first;
  502. uint qBin = _q->quantize( i->first, dim );
  503. _beta[feat] += _Tlookup[dim*_q->getNumberOfBins() + qBin];
  504. }
  505. }
  506. // comment about the following noise integration, see hik_kernel_multiply
  507. for (uint feat = 0; feat < this->ui_n; feat++)
  508. {
  509. _beta[feat] += this->d_noise*_alpha[feat];
  510. }
  511. }
  512. void FastMinKernel::hik_kernel_sum(const NICE::VVector & _A,
  513. const NICE::VVector & _B,
  514. const NICE::SparseVector & _xstar,
  515. double & _beta,
  516. const ParameterizedFunction *_pf) const
  517. {
  518. // sparse version of hik_kernel_sum, no really significant changes,
  519. // we are just skipping zero elements
  520. _beta = 0.0;
  521. for (SparseVector::const_iterator i = _xstar.begin(); i != _xstar.end(); i++)
  522. {
  523. uint dim = i->first;
  524. double fval = i->second;
  525. uint nrZeroIndices = this->X_sorted.getNumberOfZeroElementsPerDimension(dim);
  526. if ( nrZeroIndices == this->ui_n ) {
  527. // all features are zero and let us ignore it completely
  528. continue;
  529. }
  530. uint position;
  531. //where is the example x^z_i located in
  532. //the sorted array? -> perform binary search, runtime O(log(n))
  533. // search using the original value
  534. this->X_sorted.findFirstLargerInDimension(dim, fval, position);
  535. bool posIsZero ( position == 0 );
  536. if ( !posIsZero )
  537. {
  538. position--;
  539. }
  540. //NOTE again - pay attention! This is only valid if all entries are NOT negative! - if not, ask wether the current feature is greater than zero. If so, subtract the nrZeroIndices, if not do not
  541. //sum_{l \in L_k} \alpha_l x^l_k
  542. double firstPart(0.0);
  543. //TODO in the "overnext" line there occurs the following error
  544. // Invalid read of size 8
  545. if ( !posIsZero && ((position-nrZeroIndices) < this->ui_n) )
  546. {
  547. firstPart = (_A[dim][position-nrZeroIndices]);
  548. }
  549. // sum_{u \in U_k} alpha_u
  550. // sum_{u \in U_k} alpha_u
  551. // => double secondPart( B(dim, n-1) - B(dim, position));
  552. //TODO in the next line there occurs the following error
  553. // Invalid read of size 8
  554. double secondPart( _B[dim][this->ui_n-1-nrZeroIndices]);
  555. //TODO in the "overnext" line there occurs the following error
  556. // Invalid read of size 8
  557. if ( !posIsZero && (position >= nrZeroIndices) )
  558. {
  559. secondPart-= _B[dim][position-nrZeroIndices];
  560. }
  561. if ( _pf != NULL )
  562. {
  563. fval = _pf->f ( dim, fval );
  564. }
  565. // but apply using the transformed one
  566. _beta += firstPart + secondPart* fval;
  567. }
  568. }
  569. void FastMinKernel::hik_kernel_sum(const NICE::VVector & _A,
  570. const NICE::VVector & _B,
  571. const NICE::Vector & _xstar,
  572. double & _beta,
  573. const ParameterizedFunction *_pf
  574. ) const
  575. {
  576. _beta = 0.0;
  577. uint dim ( 0 );
  578. for (NICE::Vector::const_iterator i = _xstar.begin(); i != _xstar.end(); i++, dim++)
  579. {
  580. double fval = *i;
  581. uint nrZeroIndices = this->X_sorted.getNumberOfZeroElementsPerDimension(dim);
  582. if ( nrZeroIndices == this->ui_n ) {
  583. // all features are zero and let us ignore it completely
  584. continue;
  585. }
  586. uint position;
  587. //where is the example x^z_i located in
  588. //the sorted array? -> perform binary search, runtime O(log(n))
  589. // search using the original value
  590. this->X_sorted.findFirstLargerInDimension(dim, fval, position);
  591. bool posIsZero ( position == 0 );
  592. if ( !posIsZero )
  593. {
  594. position--;
  595. }
  596. //NOTE again - pay attention! This is only valid if all entries are NOT negative! - if not, ask wether the current feature is greater than zero. If so, subtract the nrZeroIndices, if not do not
  597. //sum_{l \in L_k} \alpha_l x^l_k
  598. double firstPart(0.0);
  599. //TODO in the "overnext" line there occurs the following error
  600. // Invalid read of size 8
  601. if ( !posIsZero && ((position-nrZeroIndices) < this->ui_n) )
  602. {
  603. firstPart = (_A[dim][position-nrZeroIndices]);
  604. }
  605. // sum_{u \in U_k} alpha_u
  606. // sum_{u \in U_k} alpha_u
  607. // => double secondPart( B(dim, n-1) - B(dim, position));
  608. //TODO in the next line there occurs the following error
  609. // Invalid read of size 8
  610. double secondPart( _B[dim][this->ui_n-1-nrZeroIndices] );
  611. //TODO in the "overnext" line there occurs the following error
  612. // Invalid read of size 8
  613. if ( !posIsZero && (position >= nrZeroIndices) )
  614. {
  615. secondPart-= _B[dim][position-nrZeroIndices];
  616. }
  617. if ( _pf != NULL )
  618. {
  619. fval = _pf->f ( dim, fval );
  620. }
  621. // but apply using the transformed one
  622. _beta += firstPart + secondPart* fval;
  623. }
  624. }
  625. void FastMinKernel::hik_kernel_sum_fast(const double *_Tlookup,
  626. const Quantization * _q,
  627. const NICE::Vector & _xstar,
  628. double & _beta
  629. ) const
  630. {
  631. _beta = 0.0;
  632. if ( _xstar.size() != this->ui_d)
  633. {
  634. fthrow(Exception, "FastMinKernel::hik_kernel_sum_fast sizes of xstar and training data does not match!");
  635. return;
  636. }
  637. // runtime is O(d) if the quantizer is O(1)
  638. for ( uint dim = 0; dim < this->ui_d; dim++)
  639. {
  640. double v = _xstar[dim];
  641. uint qBin = _q->quantize( v, dim );
  642. _beta += _Tlookup[dim*_q->getNumberOfBins() + qBin];
  643. }
  644. }
  645. void FastMinKernel::hik_kernel_sum_fast(const double *_Tlookup,
  646. const Quantization * _q,
  647. const NICE::SparseVector & _xstar,
  648. double & _beta
  649. ) const
  650. {
  651. _beta = 0.0;
  652. // sparse version of hik_kernel_sum_fast, no really significant changes,
  653. // we are just skipping zero elements
  654. // for additional comments see the non-sparse version of hik_kernel_sum_fast
  655. // runtime is O(d) if the quantizer is O(1)
  656. for (SparseVector::const_iterator i = _xstar.begin(); i != _xstar.end(); i++ )
  657. {
  658. uint dim = i->first;
  659. double v = i->second;
  660. uint qBin = _q->quantize( v, dim );
  661. _beta += _Tlookup[dim*_q->getNumberOfBins() + qBin];
  662. }
  663. }
  664. double *FastMinKernel::solveLin(const NICE::Vector & _y,
  665. NICE::Vector & _alpha,
  666. const Quantization * _q,
  667. const ParameterizedFunction *_pf,
  668. const bool & _useRandomSubsets,
  669. uint _maxIterations,
  670. const uint & _sizeOfRandomSubset,
  671. double _minDelta,
  672. bool _timeAnalysis
  673. ) const
  674. {
  675. // note: this is the optimization done in Wu10_AFD and a
  676. // random version of it. In normal cases, IKM* should be
  677. // used together with your iterative solver of choice
  678. //
  679. uint sizeOfRandomSubset(_sizeOfRandomSubset);
  680. bool verboseMinimal ( false );
  681. // number of quantization bins
  682. uint hmax = _q->getNumberOfBins();
  683. NICE::Vector diagonalElements(_y.size(),0.0);
  684. this->X_sorted.hikDiagonalElements(diagonalElements);
  685. diagonalElements += this->d_noise;
  686. NICE::Vector pseudoResidual (_y.size(),0.0);
  687. NICE::Vector delta_alpha (_y.size(),0.0);
  688. double alpha_old;
  689. double alpha_new;
  690. double x_i;
  691. // initialization of the alpha vector
  692. if (_alpha.size() != _y.size())
  693. {
  694. _alpha.resize( _y.size() );
  695. }
  696. _alpha.set(0.0);
  697. // initialize the lookup table
  698. double *Tlookup = new double [ hmax * this->ui_d ];
  699. if ( (hmax*this->ui_d) <= 0 )
  700. return Tlookup;
  701. memset(Tlookup, 0, sizeof(Tlookup[0])*hmax*this->ui_d);
  702. uint iter;
  703. Timer t;
  704. if ( _timeAnalysis )
  705. t.start();
  706. if (_useRandomSubsets)
  707. {
  708. // FIXME: this code looks bogus, since we only iterate over a random
  709. // permutation of the training examples (several random subsets), without
  710. // during anything particular between batches
  711. std::vector<uint> indices( _y.size() );
  712. for (uint i = 0; i < _y.size(); i++)
  713. indices[i] = i;
  714. if (sizeOfRandomSubset <= 0)
  715. sizeOfRandomSubset = _y.size();
  716. for ( iter = 1; iter <= _maxIterations; iter++ )
  717. {
  718. NICE::Vector perm;
  719. this->randomPermutation( perm, indices, _sizeOfRandomSubset );
  720. if ( _timeAnalysis )
  721. {
  722. t.stop();
  723. Vector r;
  724. this->hik_kernel_multiply_fast(Tlookup, _q, _alpha, r);
  725. r = r - _y;
  726. double res = r.normL2();
  727. double resMax = r.normInf();
  728. std::cerr << "SimpleGradientDescent: TIME " << t.getSum() << " " << res << " " << resMax << std::endl;
  729. t.start();
  730. }
  731. for ( uint i = 0; i < sizeOfRandomSubset; i++)
  732. {
  733. pseudoResidual(perm[i]) = -_y(perm[i]) + (this->d_noise * _alpha(perm[i]));
  734. for (uint j = 0; j < this->ui_d; j++)
  735. {
  736. x_i = this->X_sorted(j,perm[i]);
  737. pseudoResidual(perm[i]) += Tlookup[j*hmax + _q->quantize( x_i, j )];
  738. }
  739. //NOTE: this threshhold could also be a parameter of the function call
  740. if ( fabs(pseudoResidual(perm[i])) > 1e-7 )
  741. {
  742. alpha_old = _alpha(perm[i]);
  743. alpha_new = alpha_old - (pseudoResidual(perm[i])/diagonalElements(perm[i]));
  744. _alpha(perm[i]) = alpha_new;
  745. delta_alpha(perm[i]) = alpha_old-alpha_new;
  746. this->hikUpdateLookupTable(Tlookup, alpha_new, alpha_old, perm[i], _q, _pf ); // works correctly
  747. } else
  748. {
  749. delta_alpha(perm[i]) = 0.0;
  750. }
  751. }
  752. // after this only residual(i) is the valid residual... we should
  753. // really update the whole vector somehow
  754. double delta = delta_alpha.normL2();
  755. if ( this->b_verbose ) {
  756. cerr << "FastMinKernel::solveLin: iteration " << iter << " / " << _maxIterations << endl;
  757. cerr << "FastMinKernel::solveLin: delta = " << delta << endl;
  758. cerr << "FastMinKernel::solveLin: pseudo residual = " << pseudoResidual.scalarProduct(pseudoResidual) << endl;
  759. }
  760. if ( delta < _minDelta )
  761. {
  762. if ( this->b_verbose )
  763. cerr << "FastMinKernel::solveLin: small delta" << endl;
  764. break;
  765. }
  766. }
  767. }
  768. else //don't use random subsets
  769. {
  770. // this is the standard coordinate descent optimization
  771. // in each of the elements in alpha
  772. for ( iter = 1; iter <= _maxIterations; iter++ )
  773. {
  774. for ( uint i = 0; i < _y.size(); i++ )
  775. {
  776. pseudoResidual(i) = -_y(i) + (this->d_noise* _alpha(i));
  777. for (uint j = 0; j < this->ui_d; j++)
  778. {
  779. x_i = this->X_sorted(j,i);
  780. pseudoResidual(i) += Tlookup[j*hmax + _q->quantize( x_i, j )];
  781. }
  782. //NOTE: this threshhold could also be a parameter of the function call
  783. if ( fabs(pseudoResidual(i)) > 1e-7 )
  784. {
  785. alpha_old = _alpha(i);
  786. alpha_new = alpha_old - (pseudoResidual(i)/diagonalElements(i));
  787. _alpha(i) = alpha_new;
  788. delta_alpha(i) = alpha_old-alpha_new;
  789. this->hikUpdateLookupTable(Tlookup, alpha_new, alpha_old, i, _q, _pf ); // works correctly
  790. } else
  791. {
  792. delta_alpha(i) = 0.0;
  793. }
  794. }
  795. double delta = delta_alpha.normL2();
  796. if ( this->b_verbose ) {
  797. std::cerr << "FastMinKernel::solveLin: iteration " << iter << " / " << _maxIterations << std::endl;
  798. std::cerr << "FastMinKernel::solveLin: delta = " << delta << std::endl;
  799. std::cerr << "FastMinKernel::solveLin: pseudo residual = " << pseudoResidual.scalarProduct(pseudoResidual) << std::endl;
  800. }
  801. if ( delta < _minDelta )
  802. {
  803. if ( this->b_verbose )
  804. std::cerr << "FastMinKernel::solveLin: small delta" << std::endl;
  805. break;
  806. }
  807. }
  808. }
  809. if (verboseMinimal)
  810. std::cerr << "FastMinKernel::solveLin -- needed " << iter << " iterations" << std::endl;
  811. return Tlookup;
  812. }
  813. void FastMinKernel::randomPermutation(NICE::Vector & _permutation,
  814. const std::vector<uint> & _oldIndices,
  815. const uint & _newSize
  816. ) const
  817. {
  818. std::vector<uint> indices(_oldIndices);
  819. const uint oldSize = _oldIndices.size();
  820. uint resultingSize (std::min( oldSize, _newSize) );
  821. _permutation.resize(resultingSize);
  822. for ( uint i = 0; i < resultingSize; i++)
  823. {
  824. uint newIndex(rand() % indices.size());
  825. _permutation[i] = indices[newIndex ];
  826. indices.erase(indices.begin() + newIndex);
  827. }
  828. }
  829. double FastMinKernel::getFrobNormApprox()
  830. {
  831. double frobNormApprox(0.0);
  832. switch (this->approxScheme)
  833. {
  834. case MEDIAN:
  835. {
  836. //\| K \|_F^1 ~ (n/2)^2 \left( \sum_k \median_k \right)^2
  837. //motivation: estimate half of the values in dim k to zero and half of them to the median (-> lower bound expectation)
  838. for ( uint i = 0; i < this->ui_d; i++ )
  839. {
  840. double median = this->X_sorted.getFeatureValues(i).getMedian();
  841. frobNormApprox += median;
  842. }
  843. frobNormApprox = fabs(frobNormApprox) * this->ui_n/2.0;
  844. break;
  845. }
  846. case EXPECTATION:
  847. {
  848. std::cerr << "EXPECTATION" << std::endl;
  849. //\| K \|_F^1^2 ~ \sum K_{ii}^2 + (n^2 - n) \left( \frac{1}{3} \sum_k \left( 2 a_k + b_k \right) \right)
  850. // with a_k = minimal value in dim k and b_k maximal value
  851. //first term
  852. NICE::Vector diagEl;
  853. X_sorted.hikDiagonalElements(diagEl);
  854. frobNormApprox += diagEl.normL2();
  855. //second term
  856. double secondTerm(0.0);
  857. for ( uint i = 0; i < this->ui_d; i++ )
  858. {
  859. double minInDim;
  860. minInDim = this->X_sorted.getFeatureValues(i).getMin();
  861. double maxInDim;
  862. maxInDim = this->X_sorted.getFeatureValues(i).getMax();
  863. std::cerr << "min: " << minInDim << " max: " << maxInDim << std::endl;
  864. secondTerm += 2.0*minInDim + maxInDim;
  865. }
  866. secondTerm /= 3.0;
  867. secondTerm = pow(secondTerm, 2);
  868. secondTerm *= (this->ui_n * ( this->ui_n - 1 ));
  869. frobNormApprox += secondTerm;
  870. frobNormApprox = sqrt(frobNormApprox);
  871. break;
  872. }
  873. default:
  874. { //do nothing, approximate with zero :)
  875. break;
  876. }
  877. }
  878. return frobNormApprox;
  879. }
  880. void FastMinKernel::setApproximationScheme(const int & _approxScheme)
  881. {
  882. switch(_approxScheme)
  883. {
  884. case 0:
  885. {
  886. this->approxScheme = MEDIAN;
  887. break;
  888. }
  889. case 1:
  890. {
  891. this->approxScheme = EXPECTATION;
  892. break;
  893. }
  894. default:
  895. {
  896. this->approxScheme = MEDIAN;
  897. break;
  898. }
  899. }
  900. }
  901. void FastMinKernel::hikPrepareKVNApproximation(NICE::VVector & _A) const
  902. {
  903. _A.resize( this->ui_d );
  904. // efficient calculation of |k_*|^2 = k_*^T * k_*
  905. // ---------------------------------
  906. //
  907. // \sum_{i=1}^{n} \left( \sum_{d=1}^{D} \min (x_d^*, x_d^i) \right)^2
  908. // <=\sum_{i=1}^{n} \sum_{d=1}^{D} \left( \min (x_d^*, x_d^i) \right)^2
  909. // = \sum_{d=1}^{D} \sum_{i=1}^{n} \left( \min (x_d^*, x_d^i) \right)^2
  910. // = \sum_{d=1}^{D} \left( \sum_{i:x_d^i < x_*^d} (x_d^i)^2 + \sum_{j: x_d^* \leq x_d^j} (x_d^*)^2 \right)
  911. //
  912. // again let us define l_d = { i | x_d^i <= x_d^* }
  913. // and u_d = { i | x_d^i > x_d^* }, this leads to
  914. //
  915. // = \sum_{d=1}^{D} ( \sum_{l \in l_d} (x_d^l)^2 + \sum_{u \in u_d} (x_d^*)^2
  916. // = \sum_{d=1}^{D} ( \sum_{l \in l_d} (x_d^l)^2 + (x_d^*)^2 \sum_{u \in u_d} 1
  917. //
  918. // We also define
  919. // l_d^j = { i | x_d^i <= x_d^j } and
  920. // u_d^j = { i | x_d^i > x_d^j }
  921. //
  922. // We now need the partial sums
  923. //
  924. // (Definition 1)
  925. // a_{d,j} = \sum_{l \in l_d^j} (x_d^l)^2
  926. // according to increasing values of x_d^l
  927. //
  928. // We end at
  929. // |k_*|^2 <= \sum_{d=1}^{D} \left( a_{d,r_d} + (x_d^*)^2 * |u_d^{r_d}| \right)
  930. // with r_d being the index of the last example in the ordered sequence for dimension d, that is not larger than x_d^*
  931. // we only need as many entries as we have nonZero entries in our features for the corresponding dimensions
  932. for ( uint i = 0; i < this->ui_d; i++ )
  933. {
  934. uint numNonZero = this->X_sorted.getNumberOfNonZeroElementsPerDimension(i);
  935. _A[i].resize( numNonZero );
  936. }
  937. // for more information see hik_prepare_alpha_multiplications
  938. for (uint dim = 0; dim < this->ui_d; dim++)
  939. {
  940. double squared_sum(0.0);
  941. uint cntNonzeroFeat(0);
  942. const multimap< double, SortedVectorSparse<double>::dataelement> & nonzeroElements = this->X_sorted.getFeatureValues(dim).nonzeroElements();
  943. // loop through all elements in sorted order
  944. for ( SortedVectorSparse<double>::const_elementpointer i = nonzeroElements.begin(); i != nonzeroElements.end(); i++ )
  945. {
  946. const SortedVectorSparse<double>::dataelement & de = i->second;
  947. // de: first - index, second - transformed feature
  948. double elem( de.second );
  949. squared_sum += pow( elem, 2 );
  950. _A[dim][cntNonzeroFeat] = squared_sum;
  951. cntNonzeroFeat++;
  952. }
  953. }
  954. }
  955. double * FastMinKernel::hikPrepareKVNApproximationFast(NICE::VVector & _A,
  956. const Quantization * _q,
  957. const ParameterizedFunction *_pf ) const
  958. {
  959. //NOTE keep in mind: for doing this, we already have precomputed A using hikPrepareSquaredKernelVector!
  960. // number of quantization bins
  961. uint hmax = _q->getNumberOfBins();
  962. // store (transformed) prototypes
  963. double *prototypes = new double [ hmax * this->ui_d ];
  964. double * p_prototypes = prototypes;
  965. for (uint dim = 0; dim < this->ui_d; dim++)
  966. {
  967. for ( uint i = 0 ; i < hmax ; i++ )
  968. {
  969. if ( _pf != NULL )
  970. {
  971. *p_prototypes = _pf->f ( dim, _q->getPrototype( i, dim ) );
  972. } else
  973. {
  974. *p_prototypes = _q->getPrototype( i, dim );
  975. }
  976. p_prototypes++;
  977. }
  978. }
  979. // creating the lookup table as pure C, which might be beneficial
  980. // for fast evaluation
  981. double *Tlookup = new double [ hmax * this->ui_d ];
  982. // loop through all dimensions
  983. for (uint dim = 0; dim < this->ui_d; dim++)
  984. {
  985. uint nrZeroIndices = this->X_sorted.getNumberOfZeroElementsPerDimension(dim);
  986. if ( nrZeroIndices == this->ui_n )
  987. continue;
  988. const multimap< double, SortedVectorSparse<double>::dataelement> & nonzeroElements = this->X_sorted.getFeatureValues(dim).nonzeroElements();
  989. SortedVectorSparse<double>::const_elementpointer i = nonzeroElements.begin();
  990. SortedVectorSparse<double>::const_elementpointer iPredecessor = nonzeroElements.begin();
  991. // index of the element, which is always bigger than the current value fval
  992. uint index = 0;
  993. // we use the quantization of the original features! the transformed feature were
  994. // already used to calculate A and B, this of course assumes monotonic functions!!!
  995. uint qBin = _q->quantize ( i->first, dim );
  996. // the next loop is linear in max(hmax, n)
  997. // REMARK: this could be changed to hmax*log(n), when
  998. // we use binary search
  999. //FIXME we should do this!
  1000. for (uint j = 0; j < hmax; j++)
  1001. {
  1002. double fval = prototypes[ dim*hmax + j];
  1003. double t;
  1004. if ( (index == 0) && (j < qBin) ) {
  1005. // current element is smaller than everything else
  1006. // resulting value = fval * sum_l=1^n 1
  1007. t = pow( fval, 2 ) * (this->ui_n-nrZeroIndices-index);
  1008. } else {
  1009. // move to next example, if necessary
  1010. while ( (j >= qBin) && ( index < (this->ui_n-nrZeroIndices)) )
  1011. {
  1012. index++;
  1013. iPredecessor = i;
  1014. i++;
  1015. if ( i->first != iPredecessor->first )
  1016. qBin = _q->quantize ( i->first, dim );
  1017. }
  1018. // compute current element in the lookup table and keep in mind that
  1019. // index is the next element and not the previous one
  1020. //NOTE pay attention: this is only valid if all entries are positiv! - if not, ask wether the current feature is greater than zero. If so, subtract the nrZeroIndices, if not do not
  1021. if ( (j >= qBin) && ( index==(this->ui_n-1-nrZeroIndices) ) ) {
  1022. // the current element (fval) is equal or bigger to the element indexed by index
  1023. // the second term vanishes, which is logical, since all elements are smaller than j!
  1024. t = _A[dim][index];
  1025. } else {
  1026. // standard case
  1027. t = _A[dim][index-1] + pow( fval, 2 ) * (this->ui_n-nrZeroIndices-(index) );
  1028. }
  1029. }
  1030. Tlookup[ dim*hmax + j ] = t;
  1031. }
  1032. }
  1033. delete [] prototypes;
  1034. return Tlookup;
  1035. }
  1036. double* FastMinKernel::hikPrepareLookupTableForKVNApproximation(const Quantization * _q,
  1037. const ParameterizedFunction *_pf
  1038. ) const
  1039. {
  1040. // number of quantization bins
  1041. uint hmax = _q->getNumberOfBins();
  1042. // store (transformed) prototypes
  1043. double *prototypes = new double [ hmax * this->ui_d ];
  1044. double * p_prototypes = prototypes;
  1045. for (uint dim = 0; dim < this->ui_d; dim++)
  1046. {
  1047. for ( uint i = 0 ; i < hmax ; i++ )
  1048. {
  1049. if ( _pf != NULL )
  1050. {
  1051. *p_prototypes = _pf->f ( dim, _q->getPrototype( i, dim ) );
  1052. } else
  1053. {
  1054. *p_prototypes = _q->getPrototype( i, dim );
  1055. }
  1056. p_prototypes++;
  1057. }
  1058. }
  1059. // creating the lookup table as pure C, which might be beneficial
  1060. // for fast evaluation
  1061. double *Tlookup = new double [ hmax * this->ui_d ];
  1062. // loop through all dimensions
  1063. for (uint dim = 0; dim < this->ui_d; dim++)
  1064. {
  1065. uint nrZeroIndices = this->X_sorted.getNumberOfZeroElementsPerDimension(dim);
  1066. if ( nrZeroIndices == this->ui_n )
  1067. continue;
  1068. const multimap< double, SortedVectorSparse<double>::dataelement> & nonzeroElements = this->X_sorted.getFeatureValues(dim).nonzeroElements();
  1069. SortedVectorSparse<double>::const_elementpointer i = nonzeroElements.begin();
  1070. SortedVectorSparse<double>::const_elementpointer iPredecessor = nonzeroElements.begin();
  1071. // index of the element, which is always bigger than the current value fval
  1072. uint index = 0;
  1073. // we use the quantization of the original features! Nevetheless, the resulting lookupTable is computed using the transformed ones
  1074. uint qBin = _q->quantize ( i->first, dim );
  1075. double sum(0.0);
  1076. for (uint j = 0; j < hmax; j++)
  1077. {
  1078. double fval = prototypes[ dim*hmax + j];
  1079. double t;
  1080. if ( (index == 0) && (j < qBin) ) {
  1081. // current element is smaller than everything else
  1082. // resulting value = fval * sum_l=1^n 1
  1083. t = pow( fval, 2 ) * (this->ui_n-nrZeroIndices-index);
  1084. } else {
  1085. // move to next example, if necessary
  1086. while ( (j >= qBin) && ( index < (this->ui_n-nrZeroIndices)) )
  1087. {
  1088. sum += pow( i->second.second, 2 ); //i->dataElement.transformedFeatureValue
  1089. index++;
  1090. iPredecessor = i;
  1091. i++;
  1092. if ( i->first != iPredecessor->first )
  1093. qBin = _q->quantize ( i->first, dim );
  1094. }
  1095. // compute current element in the lookup table and keep in mind that
  1096. // index is the next element and not the previous one
  1097. //NOTE pay attention: this is only valid if we all entries are positiv! - if not, ask wether the current feature is greater than zero. If so, subtract the nrZeroIndices, if not do not
  1098. if ( (j >= qBin) && ( index==(this->ui_n-1-nrZeroIndices) ) ) {
  1099. // the current element (fval) is equal or bigger to the element indexed by index
  1100. // the second term vanishes, which is logical, since all elements are smaller than j!
  1101. t = sum;
  1102. } else {
  1103. // standard case
  1104. t = sum + pow( fval, 2 ) * (this->ui_n-nrZeroIndices-(index) );
  1105. }
  1106. }
  1107. Tlookup[ dim*hmax + j ] = t;
  1108. }
  1109. }
  1110. delete [] prototypes;
  1111. return Tlookup;
  1112. }
  1113. //////////////////////////////////////////
  1114. // variance computation: sparse inputs
  1115. //////////////////////////////////////////
  1116. void FastMinKernel::hikComputeKVNApproximation(const NICE::VVector & _A,
  1117. const NICE::SparseVector & _xstar,
  1118. double & _norm,
  1119. const ParameterizedFunction *_pf )
  1120. {
  1121. _norm = 0.0;
  1122. for (SparseVector::const_iterator i = _xstar.begin(); i != _xstar.end(); i++)
  1123. {
  1124. uint dim = i->first;
  1125. double fval = i->second;
  1126. uint nrZeroIndices = this->X_sorted.getNumberOfZeroElementsPerDimension(dim);
  1127. if ( nrZeroIndices == this->ui_n ) {
  1128. // all features are zero so let us ignore them completely
  1129. continue;
  1130. }
  1131. uint position;
  1132. //where is the example x^z_i located in
  1133. //the sorted array? -> perform binary search, runtime O(log(n))
  1134. // search using the original value
  1135. this->X_sorted.findFirstLargerInDimension(dim, fval, position);
  1136. bool posIsZero ( position == 0 );
  1137. if ( !posIsZero )
  1138. {
  1139. position--;
  1140. }
  1141. //NOTE again - pay attention! This is only valid if all entries are NOT negative! - if not, ask wether the current feature is greater than zero. If so, subtract the nrZeroIndices, if not do not
  1142. double firstPart(0.0);
  1143. //TODO in the "overnext" line there occurs the following error
  1144. // Invalid read of size 8
  1145. if ( !posIsZero && ((position-nrZeroIndices) < this->ui_n) )
  1146. firstPart = (_A[dim][position-nrZeroIndices]);
  1147. if ( _pf != NULL )
  1148. fval = _pf->f ( dim, fval );
  1149. fval = fval * fval;
  1150. double secondPart( 0.0);
  1151. if ( !posIsZero )
  1152. secondPart = fval * (this->ui_n-nrZeroIndices-(position+1));
  1153. else //if x_d^* is smaller than every non-zero training example
  1154. secondPart = fval * (this->ui_n-nrZeroIndices);
  1155. // but apply using the transformed one
  1156. _norm += firstPart + secondPart;
  1157. }
  1158. }
  1159. void FastMinKernel::hikComputeKVNApproximationFast(const double *_Tlookup,
  1160. const Quantization * _q,
  1161. const NICE::SparseVector & _xstar,
  1162. double & _norm
  1163. ) const
  1164. {
  1165. _norm = 0.0;
  1166. // runtime is O(d) if the quantizer is O(1)
  1167. for (SparseVector::const_iterator i = _xstar.begin(); i != _xstar.end(); i++ )
  1168. {
  1169. uint dim = i->first;
  1170. double v = i->second;
  1171. // we do not need a parameterized function here, since the quantizer works on the original feature values.
  1172. // nonetheless, the lookup table was created using the parameterized function
  1173. uint qBin = _q->quantize( v, dim );
  1174. _norm += _Tlookup[dim*_q->getNumberOfBins() + qBin];
  1175. }
  1176. }
  1177. void FastMinKernel::hikComputeKernelVector ( const NICE::SparseVector& _xstar,
  1178. NICE::Vector & _kstar
  1179. ) const
  1180. {
  1181. //init
  1182. _kstar.resize( this->ui_n );
  1183. _kstar.set(0.0);
  1184. if ( this->b_debug )
  1185. {
  1186. std::cerr << " FastMinKernel::hikComputeKernelVector -- input: " << std::endl;
  1187. _xstar.store( std::cerr);
  1188. }
  1189. //let's start :)
  1190. for (SparseVector::const_iterator i = _xstar.begin(); i != _xstar.end(); i++)
  1191. {
  1192. uint dim = i->first;
  1193. double fval = i->second;
  1194. if ( this->b_debug )
  1195. std::cerr << "dim: " << dim << " fval: " << fval << std::endl;
  1196. uint nrZeroIndices = this->X_sorted.getNumberOfZeroElementsPerDimension(dim);
  1197. if ( nrZeroIndices == this->ui_n ) {
  1198. // all features are zero so let us ignore them completely
  1199. continue;
  1200. }
  1201. uint position;
  1202. //where is the example x^z_i located in
  1203. //the sorted array? -> perform binary search, runtime O(log(n))
  1204. // search using the original value
  1205. this->X_sorted.findFirstLargerInDimension(dim, fval, position);
  1206. //position--;
  1207. if ( this->b_debug )
  1208. std::cerr << " position: " << position << std::endl;
  1209. //get the non-zero elements for this dimension
  1210. const multimap< double, SortedVectorSparse<double>::dataelement> & nonzeroElements = this->X_sorted.getFeatureValues(dim).nonzeroElements();
  1211. //run over the non-zero elements and add the corresponding entries to our kernel vector
  1212. uint count(nrZeroIndices);
  1213. for ( SortedVectorSparse<double>::const_elementpointer i = nonzeroElements.begin(); i != nonzeroElements.end(); i++, count++ )
  1214. {
  1215. uint origIndex(i->second.first); //orig index (i->second.second would be the transformed feature value)
  1216. if ( this->b_debug )
  1217. std::cerr << "i->1.2: " << i->second.first << " origIndex: " << origIndex << " count: " << count << " position: " << position << std::endl;
  1218. if (count < position)
  1219. _kstar[origIndex] += i->first; //orig feature value
  1220. else
  1221. _kstar[origIndex] += fval;
  1222. }
  1223. }
  1224. }
  1225. //////////////////////////////////////////
  1226. // variance computation: non-sparse inputs
  1227. //////////////////////////////////////////
  1228. void FastMinKernel::hikComputeKVNApproximation(const NICE::VVector & _A,
  1229. const NICE::Vector & _xstar,
  1230. double & _norm,
  1231. const ParameterizedFunction *_pf )
  1232. {
  1233. _norm = 0.0;
  1234. uint dim ( 0 );
  1235. for (Vector::const_iterator i = _xstar.begin(); i != _xstar.end(); i++, dim++)
  1236. {
  1237. double fval = *i;
  1238. uint nrZeroIndices = this->X_sorted.getNumberOfZeroElementsPerDimension(dim);
  1239. if ( nrZeroIndices == this->ui_n ) {
  1240. // all features are zero so let us ignore them completely
  1241. continue;
  1242. }
  1243. uint position;
  1244. //where is the example x^z_i located in
  1245. //the sorted array? -> perform binary search, runtime O(log(n))
  1246. // search using the original value
  1247. this->X_sorted.findFirstLargerInDimension(dim, fval, position);
  1248. bool posIsZero ( position == 0 );
  1249. if ( !posIsZero )
  1250. {
  1251. position--;
  1252. }
  1253. //NOTE again - pay attention! This is only valid if all entries are NOT negative! - if not, ask wether the current feature is greater than zero. If so, subtract the nrZeroIndices, if not do not
  1254. double firstPart(0.0);
  1255. //TODO in the "overnext" line there occurs the following error
  1256. // Invalid read of size 8
  1257. if ( !posIsZero && ((position-nrZeroIndices) < this->ui_n) )
  1258. firstPart = (_A[dim][position-nrZeroIndices]);
  1259. double secondPart( 0.0);
  1260. if ( _pf != NULL )
  1261. fval = _pf->f ( dim, fval );
  1262. fval = fval * fval;
  1263. if ( !posIsZero )
  1264. secondPart = fval * (this->ui_n-nrZeroIndices-(position+1));
  1265. else //if x_d^* is smaller than every non-zero training example
  1266. secondPart = fval * (this->ui_n-nrZeroIndices);
  1267. // but apply using the transformed one
  1268. _norm += firstPart + secondPart;
  1269. }
  1270. }
  1271. void FastMinKernel::hikComputeKVNApproximationFast(const double *_Tlookup,
  1272. const Quantization * _q,
  1273. const NICE::Vector & _xstar,
  1274. double & _norm
  1275. ) const
  1276. {
  1277. _norm = 0.0;
  1278. // runtime is O(d) if the quantizer is O(1)
  1279. uint dim ( 0 );
  1280. for ( NICE::Vector::const_iterator i = _xstar.begin(); i != _xstar.end(); i++, dim++ )
  1281. {
  1282. double v = *i;
  1283. // we do not need a parameterized function here, since the quantizer works on the original feature values.
  1284. // nonetheless, the lookup table was created using the parameterized function
  1285. uint qBin = _q->quantize( v, dim );
  1286. _norm += _Tlookup[dim*_q->getNumberOfBins() + qBin];
  1287. }
  1288. }
  1289. void FastMinKernel::hikComputeKernelVector( const NICE::Vector & _xstar,
  1290. NICE::Vector & _kstar) const
  1291. {
  1292. //init
  1293. _kstar.resize(this->ui_n);
  1294. _kstar.set(0.0);
  1295. //let's start :)
  1296. uint dim ( 0 );
  1297. for (NICE::Vector::const_iterator i = _xstar.begin(); i != _xstar.end(); i++, dim++)
  1298. {
  1299. double fval = *i;
  1300. uint nrZeroIndices = this->X_sorted.getNumberOfZeroElementsPerDimension(dim);
  1301. if ( nrZeroIndices == this->ui_n ) {
  1302. // all features are zero so let us ignore them completely
  1303. continue;
  1304. }
  1305. uint position;
  1306. //where is the example x^z_i located in
  1307. //the sorted array? -> perform binary search, runtime O(log(n))
  1308. // search using the original value
  1309. this->X_sorted.findFirstLargerInDimension(dim, fval, position);
  1310. //position--;
  1311. //get the non-zero elements for this dimension
  1312. const multimap< double, SortedVectorSparse<double>::dataelement> & nonzeroElements = this->X_sorted.getFeatureValues(dim).nonzeroElements();
  1313. //run over the non-zero elements and add the corresponding entries to our kernel vector
  1314. uint count(nrZeroIndices);
  1315. for ( SortedVectorSparse<double>::const_elementpointer i = nonzeroElements.begin(); i != nonzeroElements.end(); i++, count++ )
  1316. {
  1317. uint origIndex(i->second.first); //orig index (i->second.second would be the transformed feature value)
  1318. if (count < position)
  1319. _kstar[origIndex] += i->first; //orig feature value
  1320. else
  1321. _kstar[origIndex] += fval;
  1322. }
  1323. }
  1324. }
  1325. ///////////////////// INTERFACE PERSISTENT /////////////////////
  1326. // interface specific methods for store and restore
  1327. ///////////////////// INTERFACE PERSISTENT /////////////////////
  1328. void FastMinKernel::restore ( std::istream & _is,
  1329. int _format )
  1330. {
  1331. bool b_restoreVerbose ( false );
  1332. if ( _is.good() )
  1333. {
  1334. if ( b_restoreVerbose )
  1335. std::cerr << " restore FastMinKernel" << std::endl;
  1336. std::string tmp;
  1337. _is >> tmp; //class name
  1338. if ( ! this->isStartTag( tmp, "FastMinKernel" ) )
  1339. {
  1340. std::cerr << " WARNING - attempt to restore FastMinKernel, but start flag " << tmp << " does not match! Aborting... " << std::endl;
  1341. throw;
  1342. }
  1343. _is.precision (numeric_limits<double>::digits10 + 1);
  1344. bool b_endOfBlock ( false ) ;
  1345. while ( !b_endOfBlock )
  1346. {
  1347. _is >> tmp; // start of block
  1348. if ( this->isEndTag( tmp, "FastMinKernel" ) )
  1349. {
  1350. b_endOfBlock = true;
  1351. continue;
  1352. }
  1353. tmp = this->removeStartTag ( tmp );
  1354. if ( b_restoreVerbose )
  1355. std::cerr << " currently restore section " << tmp << " in FastMinKernel" << std::endl;
  1356. if ( tmp.compare("ui_n") == 0 )
  1357. {
  1358. _is >> this->ui_n;
  1359. _is >> tmp; // end of block
  1360. tmp = this->removeEndTag ( tmp );
  1361. }
  1362. else if ( tmp.compare("ui_d") == 0 )
  1363. {
  1364. _is >> this->ui_d;
  1365. _is >> tmp; // end of block
  1366. tmp = this->removeEndTag ( tmp );
  1367. }
  1368. else if ( tmp.compare("d_noise") == 0 )
  1369. {
  1370. _is >> this->d_noise;
  1371. _is >> tmp; // end of block
  1372. tmp = this->removeEndTag ( tmp );
  1373. }
  1374. else if ( tmp.compare("approxScheme") == 0 )
  1375. {
  1376. int approxSchemeInt;
  1377. _is >> approxSchemeInt;
  1378. setApproximationScheme(approxSchemeInt);
  1379. _is >> tmp; // end of block
  1380. tmp = this->removeEndTag ( tmp );
  1381. }
  1382. else if ( tmp.compare("X_sorted") == 0 )
  1383. {
  1384. this->X_sorted.restore(_is,_format);
  1385. _is >> tmp; // end of block
  1386. tmp = this->removeEndTag ( tmp );
  1387. }
  1388. else
  1389. {
  1390. std::cerr << "WARNING -- unexpected FastMinKernel object -- " << tmp << " -- for restoration... aborting" << std::endl;
  1391. throw;
  1392. }
  1393. }
  1394. }
  1395. else
  1396. {
  1397. std::cerr << "FastMinKernel::restore -- InStream not initialized - restoring not possible!" << std::endl;
  1398. }
  1399. }
  1400. void FastMinKernel::store ( std::ostream & _os,
  1401. int _format
  1402. ) const
  1403. {
  1404. if (_os.good())
  1405. {
  1406. // show starting point
  1407. _os << this->createStartTag( "FastMinKernel" ) << std::endl;
  1408. _os.precision (numeric_limits<double>::digits10 + 1);
  1409. _os << this->createStartTag( "ui_n" ) << std::endl;
  1410. _os << this->ui_n << std::endl;
  1411. _os << this->createEndTag( "ui_n" ) << std::endl;
  1412. _os << this->createStartTag( "ui_d" ) << std::endl;
  1413. _os << this->ui_d << std::endl;
  1414. _os << this->createEndTag( "ui_d" ) << std::endl;
  1415. _os << this->createStartTag( "d_noise" ) << std::endl;
  1416. _os << this->d_noise << std::endl;
  1417. _os << this->createEndTag( "d_noise" ) << std::endl;
  1418. _os << this->createStartTag( "approxScheme" ) << std::endl;
  1419. _os << this->approxScheme << std::endl;
  1420. _os << this->createEndTag( "approxScheme" ) << std::endl;
  1421. _os << this->createStartTag( "X_sorted" ) << std::endl;
  1422. //store the underlying data
  1423. this->X_sorted.store(_os, _format);
  1424. _os << this->createEndTag( "X_sorted" ) << std::endl;
  1425. // done
  1426. _os << this->createEndTag( "FastMinKernel" ) << std::endl;
  1427. }
  1428. else
  1429. {
  1430. std::cerr << "OutStream not initialized - storing not possible!" << std::endl;
  1431. }
  1432. }
  1433. void FastMinKernel::clear ()
  1434. {
  1435. std::cerr << "FastMinKernel clear-function called" << std::endl;
  1436. }
  1437. ///////////////////// INTERFACE ONLINE LEARNABLE /////////////////////
  1438. // interface specific methods for incremental extensions
  1439. ///////////////////// INTERFACE ONLINE LEARNABLE /////////////////////
  1440. void FastMinKernel::addExample( const NICE::SparseVector * _example,
  1441. const double & _label,
  1442. const bool & _performOptimizationAfterIncrement
  1443. )
  1444. {
  1445. // no parameterized function was given - use default
  1446. this->addExample ( _example );
  1447. }
  1448. void FastMinKernel::addMultipleExamples( const std::vector< const NICE::SparseVector * > & _newExamples,
  1449. const NICE::Vector & _newLabels,
  1450. const bool & _performOptimizationAfterIncrement
  1451. )
  1452. {
  1453. // no parameterized function was given - use default
  1454. this->addMultipleExamples ( _newExamples );
  1455. }
  1456. void FastMinKernel::addExample( const NICE::SparseVector * _example,
  1457. const NICE::ParameterizedFunction *_pf
  1458. )
  1459. {
  1460. this->X_sorted.add_feature( *_example, _pf );
  1461. this->ui_n++;
  1462. }
  1463. void FastMinKernel::addMultipleExamples( const std::vector< const NICE::SparseVector * > & _newExamples,
  1464. const NICE::ParameterizedFunction *_pf
  1465. )
  1466. {
  1467. for ( std::vector< const NICE::SparseVector * >::const_iterator exIt = _newExamples.begin();
  1468. exIt != _newExamples.end();
  1469. exIt++ )
  1470. {
  1471. this->X_sorted.add_feature( **exIt, _pf );
  1472. this->ui_n++;
  1473. }
  1474. }