FPCFullSearch.cpp 9.6 KB

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