TestLaplace.cpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. #ifdef NICE_USELIB_CPPUNIT
  2. #include "TestLaplace.h"
  3. #include <string>
  4. #include <iostream>
  5. #include <stdexcept>
  6. #include <core/basics/cppunitex.h>
  7. #include <core/basics/numerictools.h>
  8. #include <vislearning/classifier/kernelclassifier/LikelihoodFunction.h>
  9. #include <vislearning/classifier/kernelclassifier/GPLaplaceOptimizationProblem.h>
  10. #include <vislearning/classifier/kernelclassifier/LaplaceApproximation.h>
  11. #include <vislearning/classifier/kernelclassifier/LHCumulativeGauss.h>
  12. #include <vislearning/math/kernels/KernelLinearCombination.h>
  13. using namespace NICE;
  14. using namespace OBJREC;
  15. using namespace std;
  16. CPPUNIT_TEST_SUITE_REGISTRATION( TestLaplace );
  17. void TestLaplace::setUp() {
  18. }
  19. void TestLaplace::tearDown() {
  20. }
  21. void TestLaplace::testLikelihoodDiff()
  22. {
  23. uint numSteps = 10000;
  24. double lengthScale = 1.3;
  25. double bias = 0.4;
  26. LHCumulativeGauss l (lengthScale, bias);
  27. for ( uint degree = 0 ; degree < 3 ; degree++ )
  28. {
  29. // test gradient
  30. double oldv = 0.0;
  31. double error = 0.0;
  32. for ( uint i = 0 ; i < numSteps; i++ )
  33. {
  34. double f = i / (double)numSteps;
  35. double v;
  36. if ( degree == 2 )
  37. v = l.hessian(1.0,f);
  38. else if ( degree == 1 )
  39. v = l.gradient(1.0,f);
  40. else
  41. v = l.logLike(1.0,f);
  42. if ( i > 0 )
  43. {
  44. double d = 0.0;
  45. d = (v - oldv)*numSteps;
  46. double diff = 0.0;
  47. if ( degree == 2 )
  48. diff = l.thirdgrad(1.0,f);
  49. else if ( degree == 1 )
  50. diff = l.hessian(1.0,f);
  51. else
  52. diff = l.gradient(1.0,f);
  53. error += fabs(d-diff);
  54. }
  55. oldv = v;
  56. }
  57. error /= numSteps;
  58. CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, error, 1e-4);
  59. }
  60. }
  61. void TestLaplace::testCumGaussPredict()
  62. {
  63. // test integral stuff
  64. uint numSteps = 100000;
  65. double lengthScale = 1.3;
  66. double bias = 0.4;
  67. LHCumulativeGauss l (lengthScale, bias);
  68. double mean = 0.0;
  69. double variance = 1.0;
  70. double sum = 0.0;
  71. for ( uint i = 0 ; i < numSteps ; i++ )
  72. {
  73. double f = randGaussDouble( sqrt(variance) ) + mean;
  74. sum += l.likelihood(1.0,f);
  75. }
  76. sum /= numSteps;
  77. CPPUNIT_ASSERT_DOUBLES_EQUAL(sum, l.predictAnalytically( mean, variance ), 1e-2);
  78. }
  79. void TestLaplace::testHyperParameterOptGradients()
  80. {
  81. double step = 0.00001;
  82. double interval = 1.0;
  83. double a_max = 0.0;
  84. bool firstIteration = true;
  85. double oldObj = 0.0;
  86. Vector iparameters (1);
  87. iparameters[0] = log(1.0);
  88. Vector y (6, 1.0);
  89. for ( uint i = 0 ; i < 3 ; i++ )
  90. y[i] = -1.0;
  91. Matrix X (6, 2);
  92. X(0,0) = 1.0; X(0,1) = 0.0;
  93. X(1,0) = 0.3; X(1,1) = 0.4;
  94. X(2,0) = 0.8; X(2,1) = 0.1;
  95. X(3,0) = -1.0; X(3,1) = 0.2;
  96. X(4,0) = -0.8; X(4,1) = 0.1;
  97. X(5,0) = -0.3; X(5,1) = 0.5;
  98. Matrix kernelMatrix (6,6);
  99. for ( uint i = 0 ; i < X.rows(); i++ )
  100. for ( uint j = 0 ; j < X.rows(); j++ )
  101. kernelMatrix(i,j) = exp(- (X.getRow(i) - X.getRow(j)).normL2());
  102. KernelData kernelData;
  103. kernelData.setCachedMatrix ( 0, &kernelMatrix );
  104. KernelLinearCombination kernelFunction ( 1 );
  105. kernelFunction.updateKernelData( &kernelData );
  106. LHCumulativeGauss likelihood;
  107. LaplaceApproximation laplaceApproximation;
  108. GPLaplaceOptimizationProblem gp_problem ( &kernelData, y, &kernelFunction, &likelihood, &laplaceApproximation, false );
  109. for ( double s = -interval ; s <= interval ; s+=step )
  110. {
  111. Vector parameters ( iparameters );
  112. parameters[0] += s;
  113. gp_problem.setParameters( parameters );
  114. double obj = gp_problem.computeObjective ( );
  115. Vector gradient;
  116. gp_problem.computeGradient( gradient );
  117. double deriv = gradient[0];
  118. if ( ! firstIteration )
  119. {
  120. double derivApprox = ( obj - oldObj ) / step;
  121. double a = fabs(deriv - derivApprox);
  122. if ( a > a_max )
  123. a_max = a;
  124. // DEBUGGING:
  125. // cerr << "EVAL: " << parameters[0] << " objective " << obj << " D(closedform) " << deriv << " D(approximated) " << derivApprox << endl;
  126. }
  127. firstIteration = false;
  128. oldObj = obj;
  129. }
  130. CPPUNIT_ASSERT_DOUBLES_EQUAL(0.0, a_max, 1e-5);
  131. }
  132. void TestLaplace::testModeFinding()
  133. {
  134. Config conf;
  135. //conf.sB("LaplaceApproximation", "laplace_verbose", true );
  136. LHCumulativeGauss likelihood;
  137. Vector y (6, 1.0);
  138. for ( uint i = 0 ; i < 3 ; i++ )
  139. y[i] = -1.0;
  140. /** Test 1: Check the special case of a diagonal kernel matrix */
  141. {
  142. Matrix K (6,6);
  143. K.setIdentity();
  144. KernelData kernelData ( &conf, K );
  145. /* Due to the diagonal kernel matrix
  146. * mode finding is really simple
  147. *
  148. * objective function:
  149. * log L(y_i, f_i) - 0.5 * f_i^2
  150. * = log ( ( ::erf(y*f)+1 ) / 2.0 ) - 0.5 * f_i^2
  151. * gnuplot:
  152. * plot [-1:1] log ((erf(x)+1) / 2.0) - 0.5*x**2
  153. *
  154. *
  155. * and the mode f should obey the
  156. * equation:
  157. * L'(y_i, f_i) - f_i = 0
  158. *
  159. * L'(y_i, f_i) = y_i*exp(-f_i^2)/(L(y,f)*sqrt(Pi)) ;
  160. * L(y_i, f_i) = ( ::erf(y*f)+1 ) / 2.0
  161. *
  162. * y_i = 1 ->
  163. * exp(-f_i^2)/sqrt(PI) = f_i * (erf(f)+1) / 2.0
  164. * gnuplot
  165. * plot [-1:1] exp(-x**2)/sqrt(pi), x * (erf(x)+1) / 2.0
  166. *
  167. * -> intersection at f = 0.541132
  168. *
  169. */
  170. LaplaceApproximation laplaceApproximation ( &conf );
  171. laplaceApproximation.approximate ( &kernelData, y, &likelihood );
  172. const Vector & mode = laplaceApproximation.getMode();
  173. for ( uint k = 0 ; k < mode.size(); k++ )
  174. CPPUNIT_ASSERT_DOUBLES_EQUAL( 0.541132, y[k] * mode[k], 1e-5 );
  175. }
  176. /** Test 2: Check the self-consistent equation of the Mode */
  177. {
  178. Matrix X (6, 2);
  179. X(0,0) = 1.0; X(0,1) = 0.0;
  180. X(1,0) = 0.3; X(1,1) = 0.4;
  181. X(2,0) = 0.8; X(2,1) = 0.1;
  182. X(3,0) = -1.0; X(3,1) = 0.2;
  183. X(4,0) = -0.8; X(4,1) = 0.1;
  184. X(5,0) = -0.3; X(5,1) = 0.5;
  185. Matrix K (6,6);
  186. for ( uint i = 0 ; i < X.rows(); i++ )
  187. for ( uint j = 0 ; j < X.rows(); j++ )
  188. K(i,j) = exp(- (X.getRow(i) - X.getRow(j)).normL2());
  189. KernelData kernelData ( &conf, K );
  190. LaplaceApproximation laplaceApproximation ( &conf );
  191. laplaceApproximation.approximate ( &kernelData, y, &likelihood );
  192. const Vector & mode = laplaceApproximation.getMode();
  193. Vector gradLikelihood ( mode.size() );
  194. for ( uint i = 0; i < mode.size(); i++ )
  195. gradLikelihood[i] = likelihood.gradient ( y[i], mode[i] );
  196. // the mode should obey the following equation: mode = K gradLikelihood
  197. // (cf. eq. 3.17 Rasmussen and Williams)
  198. CPPUNIT_ASSERT_DOUBLES_EQUAL ( 0.0, ( mode - K * gradLikelihood ).normL2(), 1e-15 );
  199. }
  200. }
  201. #endif