FPCFullSearch.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. /**
  2. * @file FPCFullSearch.cpp
  3. * @brief optimal feature search like performed by boosting
  4. * @author Erik Rodner
  5. * @date 04/21/2008
  6. */
  7. #include <iostream>
  8. #include <time.h>
  9. #include "FPCFullSearch.h"
  10. #include "vislearning/features/fpfeatures/createFeatures.h"
  11. using namespace OBJREC;
  12. using namespace std;
  13. // refactor-nice.pl: check this substitution
  14. // old: using namespace ice;
  15. using namespace NICE;
  16. #undef DEBUG_BOOSTING
  17. FPCFullSearch::FPCFullSearch( const Config *_conf ) : conf(_conf)
  18. {
  19. f = NULL;
  20. srand(time(NULL));
  21. use_regression = _conf->gB("FPCFullSearch", "use_regression", false);
  22. }
  23. FPCFullSearch::~FPCFullSearch()
  24. {
  25. if ( f != NULL )
  26. delete f;
  27. }
  28. ClassificationResult FPCFullSearch::classify ( Example & pe )
  29. {
  30. assert ( f != NULL );
  31. double value = f->val ( &pe );
  32. int indicator = 0;
  33. if ( parity*value > parity*threshold )
  34. indicator = 1;
  35. else
  36. indicator = -1;
  37. FullVector scores(2);
  38. scores[0] = 0.0;
  39. if ( use_regression )
  40. scores[1] = alpha*indicator + beta;
  41. else
  42. scores[1] = indicator;
  43. int classno = (scores[1] >= 0);
  44. return ClassificationResult ( classno, scores );
  45. }
  46. void FPCFullSearch::train ( FeaturePool & fp, Examples & examples )
  47. {
  48. f = NULL;
  49. fprintf (stderr, "FPCFullSearch: Feature optimization ...\n");
  50. // ------------ search for a optimal hypothesis
  51. // hypothesis defined by feature index and threshold (i,t)
  52. // [v_i > t]
  53. const Feature *optimal_feature = NULL;
  54. double optimal_alpha = 1.0;
  55. double optimal_beta = 0.0;
  56. double minimum_error = numeric_limits<double>::max();
  57. double optimal_threshold = 0.0;
  58. int optimal_parity = 1;
  59. for ( FeaturePool::const_iterator feature_i = fp.begin();
  60. feature_i != fp.end() ; feature_i++ )
  61. {
  62. const Feature *fcurrent = feature_i->second;
  63. #ifdef DEBUG_BOOSTING
  64. fcurrent->store(cerr); cerr << endl; // DEBUG
  65. #endif
  66. FeatureValues values;
  67. int example_index = 0;
  68. double overallWeight1 = 0.0;
  69. double overallWeight0 = 0.0;
  70. for ( vector< pair<int, Example> >::const_iterator example_i = examples.begin();
  71. example_i != examples.end() ; example_i++, example_index++ )
  72. {
  73. int classno = example_i->first;
  74. assert ( (classno == 0) || (classno == 1) );
  75. const Example & ce = example_i->second;
  76. double value;
  77. value = fcurrent->val ( &ce );
  78. values.insert (
  79. quadruplet<double,int,int,double> (
  80. value, classno, example_index, ce.weight
  81. )
  82. );
  83. overallWeight0 += ( 1 - classno ) * ce.weight;
  84. overallWeight1 += classno * ce.weight;
  85. }
  86. assert ( overallWeight0 + overallWeight1 > 10e-8 );
  87. int previous_class = 0;
  88. double previous_value = 0.0;
  89. //unused:
  90. //double first_value = values.begin()->first;
  91. FeatureValues::const_iterator lastElement = values.end();
  92. lastElement--;
  93. //unused:
  94. //double last_value = lastElement->first;
  95. double minimum_feature_error = minimum_error;
  96. double w0 = 0.0;
  97. double w1 = 0.0;
  98. for ( FeatureValues::const_iterator i = values.begin();
  99. i != values.end(); i++ )
  100. {
  101. double w = i->fourth;
  102. int current_class = i->second;
  103. double current_value = i->first;
  104. #ifdef DEBUG_BOOSTING
  105. fprintf (stderr, "w %f w0 %f w1 %f cc %f\n", w, w0, w1, current_value );
  106. #endif
  107. assert (! NICE::isNaN(current_value));
  108. if ( (i != values.begin()) // do not check at the begin
  109. #ifdef CHECK_AT_CLASS_BOUNDARIES_ONLY
  110. && (current_class != previous_class) // check only at class splits
  111. #endif
  112. && ( (current_value != previous_value) // check only at possible value splits
  113. #ifdef CHECK_AT_EQUAL_SPLIT
  114. || ( (current_value != first_value)
  115. && (current_value != last_value) // or if it is an equal split
  116. // but can split the values somehow
  117. )
  118. #endif
  119. )
  120. )
  121. {
  122. // possible split found -----
  123. #ifdef DEBUG_BOOSTING
  124. fprintf (stderr, "Current Best Setting: minimum_feature_error %f; optimal_threshold %f\n", minimum_feature_error, optimal_threshold );
  125. fprintf (stderr, "Current Split: 0(%f | %f) 1(%f | %f) w %f %d\n",
  126. w0, overallWeight0 - w0, w1, overallWeight1 - w1, w, current_class );
  127. #endif
  128. double current_threshold = (current_value + previous_value) / 2.0;
  129. // case: parity 1 -> all above threshold belongs to class 1
  130. // -> error: w1 + overallWeight0 - w0
  131. double error1 = 1.0;
  132. // some variables which are important for regression
  133. double a = 0.0;
  134. double c = 0.0;
  135. double d = 0.0;
  136. double W = 0.0;
  137. double regerror = 0.0;
  138. double det = 0.0;
  139. if ( use_regression )
  140. {
  141. /* We solve the following linear equation system:
  142. (MATLAB syntax)
  143. [ [ w, a ]; [ a, w ] ] * [ alpha; beta ] = [ c ; d ]
  144. with the following definitions (x_i is the response, indicator)
  145. */
  146. // W = \sum_i w_i = \sum_i w_i x_i^2
  147. W = overallWeight0 + overallWeight1;
  148. // a = \sum_i w_i x_i
  149. a = (overallWeight0 - w0) + (overallWeight1 - w1) - (w0 + w1);
  150. // \sum_i x_i y_i w_i
  151. c = w0 - w1 + (overallWeight1 - w1) - (overallWeight0 - w0);
  152. // \sum_i y_i
  153. d = overallWeight1 - overallWeight0;
  154. // the determinant of the coefficient matrix
  155. det = W*W - a*a;
  156. /* The following is somewhat tricky.
  157. We do not want to recalculate the regression
  158. error by looping over all examples. Therefore
  159. one can derive the following formula for the regression
  160. error.
  161. To derive this formula, one has to
  162. solve the linear equations above and substitute
  163. the optimal alpha and beta in the formulation of
  164. the error: \sum_i ( \alpha x_i + \beta - y_i )^2.
  165. FIXME: write a tex-note about this stuff.
  166. */
  167. regerror = - (W*c*c - 2*a*c*d + W*d*d) / det + W;
  168. error1 = regerror;
  169. } else {
  170. error1 = w1 + overallWeight0 - w0;
  171. }
  172. // prefer splits which really seperate classes !
  173. if ( current_class == previous_class ) error1 += 1e-12;
  174. #ifdef DEBUG_BOOSTING
  175. fprintf (stderr, "a=%f; c=%f, d=%f, W=%f, regerror=%f, alpha=%f, beta=%f\n", a, c, d, W, regerror,
  176. c/a - (d-c)/(W-a), (d-c) / (W-a));
  177. fprintf (stderr, "> %f (>=%f) belongs to class 1: err=%f\n", current_threshold, current_value, error1);
  178. #endif
  179. if ( error1 < minimum_feature_error )
  180. {
  181. optimal_threshold = current_threshold;
  182. optimal_parity = 1;
  183. if ( use_regression )
  184. {
  185. // solution of the linear equation system
  186. optimal_beta = (W*d - a*c)/det;
  187. optimal_alpha = (W*c - a*d)/det;
  188. } else {
  189. optimal_beta = 0.0;
  190. optimal_alpha = 1.0;
  191. }
  192. minimum_feature_error = error1;
  193. #ifdef DEBUG_BOOSTING
  194. fprintf (stderr, "optimal feature: 0(%f | %f) 1(%f | %f) %f %d\n",
  195. w0, overallWeight0 - w0, w1, overallWeight1 - w1, w, current_class );
  196. #endif
  197. }
  198. if ( ! use_regression )
  199. {
  200. // case: parity -1 -> all below threshold belongs to class 1
  201. // -> error: w0 + overallWeight1 - w1
  202. double error0 = w0 + overallWeight1 - w1;
  203. if ( current_class == previous_class ) error0 += 1e-12;
  204. #ifdef DEBUG_BOOSTING
  205. fprintf (stderr, "< %f (<=%f) belongs to class 1: err=%f\n", current_threshold, previous_value, error0);
  206. #endif
  207. if ( error0 < minimum_feature_error )
  208. {
  209. optimal_threshold = current_threshold;
  210. optimal_parity = -1;
  211. optimal_beta = 0.0;
  212. optimal_alpha = 1.0;
  213. minimum_feature_error = error0;
  214. #ifdef DEBUG_BOOSTING
  215. fprintf (stderr, "optimal feature: 0(%f | %f) 1(%f | %f) w %f %d\n",
  216. w0, overallWeight0 - w0, w1, overallWeight1 - w1, w, current_class );
  217. #endif
  218. }
  219. }
  220. }
  221. w1 += current_class * w;
  222. w0 += (1-current_class) * w;
  223. previous_class = current_class;
  224. previous_value = current_value;
  225. }
  226. // update optimal feature
  227. if ( minimum_feature_error < minimum_error )
  228. {
  229. optimal_feature = fcurrent;
  230. minimum_error = minimum_feature_error;
  231. }
  232. }
  233. assert ( optimal_feature != NULL );
  234. fprintf (stderr, "FPCFullSearch: Feature optimization ...done\n");
  235. optimal_feature->store(cerr);
  236. cerr << endl;
  237. f = optimal_feature->clone();
  238. threshold = optimal_threshold;
  239. parity = optimal_parity;
  240. alpha = optimal_alpha;
  241. beta = optimal_beta;
  242. last_error = minimum_error;
  243. }
  244. FPCFullSearch *FPCFullSearch::clone () const
  245. {
  246. FPCFullSearch *myclone = new FPCFullSearch (conf);
  247. if ( f == NULL )
  248. myclone->f = NULL;
  249. else
  250. myclone->f = f->clone();
  251. myclone->threshold = threshold;
  252. myclone->parity = parity;
  253. myclone->beta = beta;
  254. myclone->alpha = alpha;
  255. return myclone;
  256. }
  257. void FPCFullSearch::restore (istream & is, int format)
  258. {
  259. // refactor-nice.pl: check this substitution
  260. // old: string feature_tag;
  261. std::string feature_tag;
  262. is >> feature_tag;
  263. fprintf (stderr, "feature tag: %s\n", feature_tag.c_str() );
  264. f = createFeatureFromTag ( conf, feature_tag );
  265. if ( f == NULL ) {
  266. fprintf (stderr, "Unknown feature description: %s\n",
  267. feature_tag.c_str() );
  268. exit(-1);
  269. }
  270. f->restore ( is, format );
  271. is >> threshold;
  272. is >> parity;
  273. is >> alpha;
  274. is >> beta;
  275. cerr << "T " << threshold << " P " << parity << endl;
  276. // refactor-nice.pl: check this substitution
  277. // old: string tmp;
  278. std::string tmp;
  279. is >> tmp;
  280. }
  281. void FPCFullSearch::store (ostream & os, int format) const
  282. {
  283. f->store ( os, format );
  284. os << endl;
  285. os << threshold << " " << parity << " ";
  286. os << alpha << " " << beta << endl;
  287. }
  288. void FPCFullSearch::clear()
  289. {
  290. if ( f != NULL )
  291. delete f;
  292. }
  293. const Feature *FPCFullSearch::getStump ( double & _threshold, double & _parity ) const
  294. {
  295. _threshold = threshold;
  296. _parity = parity;
  297. return f;
  298. }