Bladeren bron

OnlineLearnable added, GPHIK ready for IL with 1 example update, everything nice and clean

Alexander Freytag 11 jaren geleden
bovenliggende
commit
e26c33948b

+ 588 - 24
FMKGPHyperparameterOptimization.cpp

@@ -39,6 +39,392 @@
 using namespace NICE;
 using namespace std;
 
+/////////////////////////////////////////////////////
+/////////////////////////////////////////////////////
+//                 PROTECTED METHODS
+/////////////////////////////////////////////////////
+/////////////////////////////////////////////////////
+
+void FMKGPHyperparameterOptimization::updateAfterSingleIncrement ( 
+      const NICE::SparseVector & x,
+      const std::set < int > newClasses,
+      const bool & performOptimizationAfterIncrement )
+{
+  NICE::Timer t;
+  t.start();
+  if ( this->fmk == NULL )
+    fthrow ( Exception, "FastMinKernel object was not initialized!" );
+
+  std::map<int, NICE::Vector> binaryLabels;
+  std::set<int> classesToUse;
+  //TODO this could be made faster when storing the previous binary label vectors...
+  this->prepareBinaryLabels ( binaryLabels, this->labels , classesToUse );
+  
+  if ( this->verbose )
+    std::cerr << "labels.size() after increment: " << this->labels.size() << std::endl;
+
+  NICE::Timer t1;
+
+  NICE::GPLikelihoodApprox * gplike;
+  uint parameterVectorSize;
+
+  std::cerr << "setup GPLikelihoodApprox object " << std::endl;
+  t1.start();
+  this->setupGPLikelihoodApprox ( gplike, binaryLabels, parameterVectorSize );
+  t1.stop();
+  if ( this->verboseTime )
+    std::cerr << "Time used for setting up the gplike-objects: " << t1.getLast() << std::endl;
+
+
+  std::cerr << "setup previous alpha guess " << std::endl;
+  t1.start();
+  if ( this->b_usePreviousAlphas )
+  {
+    //We initialize it with the same values as we use in GPLikelihoodApprox in batch training
+    //default in GPLikelihoodApprox for the first time:
+    // alpha = (binaryLabels[classCnt] * (1.0 / eigenmax[0]) );
+    double factor ( 1.0 / this->eigenMax[0] );
+    
+    std::map<int, NICE::Vector>::const_iterator binaryLabelsIt = binaryLabels.begin();
+    
+    for ( std::map<int, NICE::Vector>::iterator lastAlphaIt = lastAlphas.begin() ;lastAlphaIt != lastAlphas.end(); lastAlphaIt++ )
+    {
+      int oldSize ( lastAlphaIt->second.size() );
+      lastAlphaIt->second.resize ( oldSize + 1 );
+
+  
+
+      if ( binaryLabelsIt->second[oldSize] > 0 ) //we only have +1 and -1, so this might be benefitial in terms of speed
+        lastAlphaIt->second[oldSize] = factor;
+      else
+        lastAlphaIt->second[oldSize] = -factor; //we follow the initialization as done in previous steps
+        //lastAlphaIt->second[oldSize] = 0.0; // following the suggestion of Yeh and Darrell
+
+      binaryLabelsIt++;
+      
+    }
+
+    //compute unaffected alpha-vectors for the new classes
+    for (std::set<int>::const_iterator newClIt =  newClasses.begin(); newClIt != newClasses.end(); newClIt++)
+    {      
+      NICE::Vector alphaVec = (binaryLabels[*newClIt] * factor ); //see GPLikelihoodApprox for an explanation
+      lastAlphas.insert( std::pair<int, NICE::Vector>(*newClIt, alphaVec) );
+    }      
+
+    gplike->setInitialAlphaGuess ( &lastAlphas );
+  }
+  else
+  {
+    //if we do not use previous alphas, we do not have to set up anything here
+  }
+  
+  t1.stop();
+  if ( this->verboseTime )
+    std::cerr << "Time used for setting up the alpha-objects: " << t1.getLast() << std::endl;
+
+    std::cerr << "update Eigendecomposition " << std::endl;
+  t1.start();
+  // we compute all needed eigenvectors for standard classification and variance prediction at ones.
+  // nrOfEigenvaluesToConsiderForVarApprox should NOT be larger than 1 if a method different than approximate_fine is used!
+  this->updateEigenDecomposition(  std::max ( this->nrOfEigenvaluesToConsider, this->nrOfEigenvaluesToConsiderForVarApprox) );
+  t1.stop();
+  if ( this->verboseTime )
+    std::cerr << "Time used for setting up the eigenvectors-objects: " << t1.getLast() << std::endl;
+
+  if ( this->verbose )
+    std::cerr << "resulting eigenvalues for first class: " << eigenMax[0] << std::endl;
+
+  // we can reuse the already given performOptimization-method:
+  // OPT_GREEDY
+  // for this strategy we can't reuse any of the previously computed scores
+  // so come on, let's do the whole thing again...
+  // OPT_DOWNHILLSIMPLEX
+  // Here we can benefit from previous results, when we use them as initialization for our optimizer
+  // ikmsums.begin()->second->getParameters ( currentParameters ); uses the previously computed optimal parameters
+  // as initialization
+  // OPT_NONE
+  // nothing to do, obviously
+  //NOTE we could skip this, if we do not want to change our parameters given new examples
+  if ( this->verbose )
+    std::cerr << "perform optimization after increment " << std::endl;
+  
+  int optimizationMethodTmpCopy;
+  if ( !performOptimizationAfterIncrement )
+  {
+    // if no optimization shall be carried out, we simply set the optimization method to NONE but run the optimization
+    // call nonetheless, thereby computing alpha vectors, etc. which would be not initialized 
+    optimizationMethodTmpCopy = this->optimizationMethod;
+    this->optimizationMethod = OPT_NONE;
+  }
+    
+    t1.start();
+    //TODO add option for handing over previous solution!
+    this->performOptimization ( *gplike, parameterVectorSize);
+//         this->performOptimization ( *gplike, parameterVectorSize, false /* initialize not with default values but using the last solution */ );
+    t1.stop();
+    if ( this->verboseTime )
+      std::cerr << "Time used for performing the optimization: " << t1.getLast() << std::endl;
+
+    if ( this->verbose )
+      std::cerr << "Preparing after retraining for classification ..." << std::endl;
+
+    t1.start();
+    this->transformFeaturesWithOptimalParameters ( *gplike, parameterVectorSize );
+    t1.stop();
+    if ( this->verboseTime)
+      std::cerr << "Time used for transforming features with optimal parameters: " << t1.getLast() << std::endl;
+
+  if ( !performOptimizationAfterIncrement )
+  {
+    this->optimizationMethod = optimizationMethodTmpCopy;
+  }
+
+  
+  //NOTE unfortunately, the whole vector alpha differs, and not only its last entry.
+  // If we knew any method, which could update this efficiently, we could also compute A and B more efficiently by updating them.
+  // Since we are not aware of any such method, we have to compute them completely new
+  // :/
+  t1.start();
+  this->computeMatricesAndLUTs ( *gplike );
+  t1.stop();
+  if ( this->verboseTime )
+    std::cerr << "Time used for setting up the A'nB -objects: " << t1.getLast() << std::endl;
+
+  t.stop();  
+
+  NICE::ResourceStatistics rs;
+  std::cerr << "Time used for re-learning: " << t.getLast() << std::endl;
+  long maxMemory;
+  rs.getMaximumMemory ( maxMemory );
+  std::cerr << "Maximum memory used: " << maxMemory << " KB" << std::endl;
+
+  //don't waste memory
+  delete gplike;
+}
+
+void FMKGPHyperparameterOptimization::updateAfterMultipleIncrements ( 
+      const std::vector<const NICE::SparseVector*> & x, 
+      const std::set < int > newClasses,
+      const bool & performOptimizationAfterIncrement )
+{
+  Timer t;
+  t.start();
+  if ( fmk == NULL )
+    fthrow ( Exception, "FastMinKernel object was not initialized!" );
+
+  std::map<int, NICE::Vector> binaryLabels;
+  std::set<int> classesToUse;
+  this->prepareBinaryLabels ( binaryLabels, labels , classesToUse );
+  //actually, this is not needed, since we have the global set knownClasses
+  classesToUse.clear();
+  
+  std::map<int, NICE::Vector> newBinaryLabels;
+  if ( newClasses.size() > 0)
+  {
+    for (std::set<int>::const_iterator newClIt = newClasses.begin(); newClIt != newClasses.end(); newClIt++)
+    {
+      std::map<int, NICE::Vector>::iterator binLabelIt = binaryLabels.find(*newClIt);
+      newBinaryLabels.insert(*binLabelIt);
+    }
+  }
+  
+  if ( this->verbose )
+    std::cerr << "labels.size() after increment: " << this->labels.size() << std::endl;
+ 
+  
+  // ************************************************************
+  //   include the information for classes we know so far    
+  // ************************************************************
+  if ( this->verbose)
+    std::cerr <<  "include the information for classes we know so far " << std::endl;
+  
+  NICE::Timer t1;
+  t1.start();
+  //update the kernel combinations
+  //TODO verify that this is not needed anymore in any IKMLinearCombination class
+    
+  //we have to reset the fmk explicitely
+  ( ( GMHIKernel* ) this->ikmsum->getModel ( this->ikmsum->getNumberOfModels() - 1 ) )->setFastMinKernel ( this->fmk );
+
+  t1.stop();
+  if ( this->verboseTime )
+    std::cerr << "Time used for setting up the ikm-objects for known classes: " << t1.getLast() << std::endl;
+  
+  // *********************************************
+  //          work on the new classes
+  // *********************************************
+    
+  if ( this->verbose )    
+    std::cerr << "work on the new classes " << std::endl;
+  
+  double tmpNoise;  
+  (this->ikmsum->getModel( 0 ))->getFirstDiagonalElement(tmpNoise);
+    
+
+  
+  // ******************************************************************************************
+  //       now do everything which is independent of the number of new classes
+  // ******************************************************************************************  
+
+  if ( this->verbose )
+    std::cerr << "now do everything which is independent of the number of new classes" << std::endl;
+
+  NICE::GPLikelihoodApprox * gplike;
+  uint parameterVectorSize;
+
+  t1.start();
+  this->setupGPLikelihoodApprox ( gplike, binaryLabels, parameterVectorSize );
+  t1.stop();
+  if ( this->verboseTime )
+    std::cerr << "Time used for setting up the gplike-objects: " << t1.getLast() << std::endl;
+
+  t1.start();
+  // we compute all needed eigenvectors for standard classification and variance prediction at ones.
+  // nrOfEigenvaluesToConsiderForVarApprox should NOT be larger than 1 if a method different than approximate_fine is used!
+  this->updateEigenDecomposition(  std::max ( this->nrOfEigenvaluesToConsider, this->nrOfEigenvaluesToConsiderForVarApprox) );
+  t1.stop();
+  if ( this->verboseTime )
+    std::cerr << "Time used for setting up the eigenvectors-objects: " << t1.getLast() << std::endl;
+
+  t1.start();
+  if ( this->b_usePreviousAlphas )
+  {
+    double factor ( 1.0 / this->eigenMax[0] );
+	  
+    std::map<int, NICE::Vector>::const_iterator binaryLabelsIt = binaryLabels.begin();
+    
+    for ( std::map<int, NICE::Vector>::iterator lastAlphaIt = lastAlphas.begin() ;lastAlphaIt != lastAlphas.end(); lastAlphaIt++ )
+    {
+ 
+      int oldSize ( lastAlphaIt->second.size() );
+      lastAlphaIt->second.resize ( oldSize + x.size() );
+
+      //We initialize it with the same values as we use in GPLikelihoodApprox in batch training
+      //default in GPLikelihoodApprox for the first time:
+      // alpha = (binaryLabels[classCnt] * (1.0 / eigenmax[0]) );
+         
+
+      for ( uint i = 0; i < x.size(); i++ )
+      {
+        if ( binaryLabelsIt->second[oldSize+i] > 0 ) //we only have +1 and -1, so this might be benefitial in terms of speed
+          lastAlphaIt->second[oldSize+i] = factor;
+        else
+          lastAlphaIt->second[oldSize+i] = -factor; //we follow the initialization as done in previous steps
+          //lastAlphaIt->second[oldSize+i] = 0.0; // following the suggestion of Yeh and Darrell
+      }
+
+      binaryLabelsIt++;
+    }
+
+    //compute unaffected alpha-vectors for the new classes
+    for (std::set<int>::const_iterator newClIt =  newClasses.begin(); newClIt != newClasses.end(); newClIt++)
+    {      
+      NICE::Vector alphaVec = (binaryLabels[*newClIt] * factor ); //see GPLikelihoodApprox for an explanation
+      lastAlphas.insert( std::pair<int, NICE::Vector>(*newClIt, alphaVec) );
+    }      
+
+    //TODO check that memory will not be erased when calling delete!
+    gplike->setInitialAlphaGuess ( &lastAlphas );
+  }
+  else
+  {
+  }
+  
+  //if we do not use previous alphas, we do not have to set up anything here  
+  t1.stop();
+  if ( this->verboseTime )
+    std::cerr << "Time used for setting up the alpha-objects: " << t1.getLast() << std::endl;  
+
+  if ( this->verbose )
+    std::cerr << "resulting eigenvalues of first class: " << eigenMax[0] << std::endl;
+
+  // we can reuse the already given performOptimization-method:
+  // OPT_GREEDY
+  // for this strategy we can't reuse any of the previously computed scores
+  // so come on, let's do the whole thing again...
+  // OPT_DOWNHILLSIMPLEX
+  // Here we can benefit from previous results, when we use them as initialization for our optimizer
+  // ikmsums.begin()->second->getParameters ( currentParameters ); uses the previously computed optimal parameters
+  // as initialization
+  // OPT_NONE
+  // nothing to do, obviously
+  //NOTE we can skip this, if we do not want to change our parameters given new examples
+  if ( performOptimizationAfterIncrement )
+  {
+    t1.start();
+    //TODO add option for handing over previous solution!
+    this->performOptimization ( *gplike, parameterVectorSize);
+//         this->performOptimization ( *gplike, parameterVectorSize, false /* initialize not with default values but using the last solution */ );
+    t1.stop();
+    if ( this->verboseTime )
+      std::cerr << "Time used for performing the optimization: " << t1.getLast() << std::endl;
+    
+    t1.start();
+    this->transformFeaturesWithOptimalParameters ( *gplike, parameterVectorSize );
+    t1.stop();
+    if ( this->verboseTime)
+      std::cerr << "Time used for transforming features with optimal parameters: " << t1.getLast() << std::endl;
+  }
+  else
+  {
+    //deactivate the optimization method;
+    int originalOptimizationMethod = optimizationMethod;
+    this->optimizationMethod = OPT_NONE;
+    //and deactive the noise-optimization as well
+    if ( this->optimizeNoise )
+      this->optimizeNoise = false;
+    
+    t1.start();
+    //this is needed to compute the alpha vectors for the standard parameter settings
+    //TODO add option for handing over previous solution!
+    this->performOptimization ( *gplike, parameterVectorSize);
+//         this->performOptimization ( *gplike, parameterVectorSize, false /* initialize not with default values but using the last solution */ );
+    t1.stop();
+    std::cerr << "skip optimization after increment" << std::endl;
+    if ( this->verboseTime )
+      std::cerr << "Time used for performing the optimization: " << t1.getLast() << std::endl;
+
+    std::cerr << "skip feature transformation" << std::endl;
+    if ( this->verboseTime )
+      std::cerr << "Time used for transforming features with optimal parameters: " << t1.getLast() << std::endl;
+    
+    //re-activate the optimization method
+    this->optimizationMethod = originalOptimizationMethod;    
+  }
+
+  if ( this->verbose )
+    std::cerr << "Preparing after retraining for classification ..." << std::endl;
+
+
+  //NOTE unfortunately, the whole vector alpha differs, and not only its last entry.
+  // If we knew any method, which could update this efficiently, we could also compute A and B more efficiently by updating them.
+  // Since we are not aware of any such method, we have to compute them completely new
+  // :/
+  t1.start();
+  this->computeMatricesAndLUTs ( *gplike );
+  t1.stop();
+  if ( this->verboseTime )
+    std::cerr << "Time used for setting up the A'nB -objects: " << t1.getLast() << std::endl;
+
+  t.stop();
+
+  NICE::ResourceStatistics rs;
+  std::cerr << "Time used for re-learning: " << t.getLast() << std::endl;
+  long maxMemory;
+  rs.getMaximumMemory ( maxMemory );
+  std::cerr << "Maximum memory used: " << maxMemory << " KB" << std::endl;
+
+  //don't waste memory
+  delete gplike;
+}
+
+
+/////////////////////////////////////////////////////
+/////////////////////////////////////////////////////
+//                 PUBLIC METHODS
+/////////////////////////////////////////////////////
+/////////////////////////////////////////////////////
+
 FMKGPHyperparameterOptimization::FMKGPHyperparameterOptimization()
 {
   // initialize pointer variables
@@ -58,6 +444,8 @@ FMKGPHyperparameterOptimization::FMKGPHyperparameterOptimization()
   //stupid unneeded default values
   binaryLabelPositive = -1;
   binaryLabelNegative = -2;
+  
+  this->b_usePreviousAlphas = false;
 }
 
 FMKGPHyperparameterOptimization::FMKGPHyperparameterOptimization ( const Config *_conf, ParameterizedFunction *_pf, FastMinKernel *_fmk, const string & _confSection )
@@ -144,7 +532,7 @@ void FMKGPHyperparameterOptimization::initialize ( const Config *_conf, Paramete
 
   this->verifyApproximation = _conf->gB ( _confSection, "verify_approximation", false );
   this->nrOfEigenvaluesToConsider = _conf->gI ( _confSection, "nrOfEigenvaluesToConsider", 1 );
-  this->nrOfEigenvaluesToConsiderForVarApprox = _conf->gI ( _confSection, "nrOfEigenvaluesToConsiderForVarApprox", 2 );
+  this->nrOfEigenvaluesToConsiderForVarApprox = _conf->gI ( _confSection, "nrOfEigenvaluesToConsiderForVarApprox", 1 );
 
 
 
@@ -228,6 +616,8 @@ void FMKGPHyperparameterOptimization::initialize ( const Config *_conf, Paramete
   if ( verbose )
     std::cerr << "Optimize noise: " << ( optimizeNoise ? "on" : "off" ) << std::endl;
   
+  this->b_usePreviousAlphas = _conf->gB ( _confSection, "b_usePreviousAlphas", true );
+  
   if ( verbose )
   {
     std::cerr << "------------" << std::endl;
@@ -236,7 +626,10 @@ void FMKGPHyperparameterOptimization::initialize ( const Config *_conf, Paramete
   }
 }
 
-// get and set methods
+
+///////////////////// ///////////////////// /////////////////////
+//                         GET / SET
+///////////////////// ///////////////////// ///////////////////// 
 
 void FMKGPHyperparameterOptimization::setParameterUpperBound ( const double & _parameterUpperBound )
 {
@@ -252,9 +645,13 @@ std::set<int> FMKGPHyperparameterOptimization::getKnownClassNumbers ( ) const
   return this->knownClasses;
 }
 
- //high level methods
 
-void FMKGPHyperparameterOptimization::setupGPLikelihoodApprox ( GPLikelihoodApprox * & gplike, const std::map<int, NICE::Vector> & binaryLabels, uint & parameterVectorSize )
+
+///////////////////// ///////////////////// /////////////////////
+//                      CLASSIFIER STUFF
+///////////////////// ///////////////////// /////////////////////
+
+inline void FMKGPHyperparameterOptimization::setupGPLikelihoodApprox ( GPLikelihoodApprox * & gplike, const std::map<int, NICE::Vector> & binaryLabels, uint & parameterVectorSize )
 {
   gplike = new GPLikelihoodApprox ( binaryLabels, ikmsum, linsolver, eig, verifyApproximation, nrOfEigenvaluesToConsider );
   gplike->setDebug( debug );
@@ -275,12 +672,7 @@ void FMKGPHyperparameterOptimization::updateEigenDecomposition( const int & i_no
     throw("Problem in calculating Eigendecomposition of kernel matrix. Abort program...");
   }
   
-  // EigenValue computation does not necessarily extract them in decreasing order.
-  // Therefore: sort eigenvalues decreasingly!
-  NICE::VectorT< int > ewPermutation;
-  eigenMax.sortDescend ( ewPermutation );
-
-  
+  //NOTE EigenValue computation extracts EV and EW per default in decreasing order.
   
 }
 
@@ -317,7 +709,7 @@ void FMKGPHyperparameterOptimization::performOptimization ( GPLikelihoodApprox &
   {
     //standard as before, normal optimization
     if ( verbose )    
-        std::cerr << "DOWNHILLSIMPLEX WITHOUT BALANCED LEARNING!!! " << std::endl;
+        std::cerr << "DOWNHILLSIMPLEX!!! " << std::endl;
 
     // downhill simplex strategy
     OPTIMIZATION::DownhillSimplexOptimizer optimizer;
@@ -335,7 +727,7 @@ void FMKGPHyperparameterOptimization::performOptimization ( GPLikelihoodApprox &
 
     //the scales object does not really matter in the actual implementation of Downhill Simplex
     //OPTIMIZATION::matrix_type scales ( parameterVectorSize, 1);
-    //cales.Set(1.0);
+    //scales.Set(1.0);
     
     OPTIMIZATION::SimpleOptProblem optProblem ( &gplike, initialParams, initialParams /* scales*/ );
 
@@ -376,23 +768,23 @@ void FMKGPHyperparameterOptimization::performOptimization ( GPLikelihoodApprox &
   if ( verbose )
   {
     std::cerr << "Optimal hyperparameter was: " << gplike.getBestParameters() << std::endl;
-  
-    std::map<int, NICE::Vector> bestAlphas = gplike.getBestAlphas();
-    std::cerr << "length of alpha vectors: " << bestAlphas.size() << std::endl;
-    std::cerr << "alpha vector for first class: " << bestAlphas.begin()->second << std::endl;
   }
 }
 
 void FMKGPHyperparameterOptimization::transformFeaturesWithOptimalParameters ( const GPLikelihoodApprox & gplike, const uint & parameterVectorSize )
 {
-  // transform all features with the "optimal" parameter
+  // transform all features with the currently "optimal" parameter
   ikmsum->setParameters ( gplike.getBestParameters() );    
 }
 
 void FMKGPHyperparameterOptimization::computeMatricesAndLUTs ( const GPLikelihoodApprox & gplike )
 {
+  
+  std::cerr << "FMKGPHyperparameterOptimization::computeMatricesAndLUTs" << std::endl;
   precomputedA.clear();
   precomputedB.clear();
+  
+  std::cerr << "length of best alpha vectors: " << gplike.getBestAlphas().size() << std::endl; 
 
   for ( std::map<int, NICE::Vector>::const_iterator i = gplike.getBestAlphas().begin(); i != gplike.getBestAlphas().end(); i++ )
   {
@@ -418,6 +810,15 @@ void FMKGPHyperparameterOptimization::computeMatricesAndLUTs ( const GPLikelihoo
     
     //TODO update the variance-related matrices as well here - currently it is done before in the outer method!!!
   }
+  
+  if ( this->precomputedTForVarEst != NULL )
+  {
+    this->prepareVarianceApproximationRough();
+  }
+  else if ( this->precomputedAForVarEst.size() > 0) 
+  {
+     this->prepareVarianceApproximationFine();
+  }
 
 }
 
@@ -618,7 +1019,9 @@ void FMKGPHyperparameterOptimization::optimize ( std::map<int, NICE::Vector> & b
   }
 
   t1.start();
-  this->updateEigenDecomposition(  this->nrOfEigenvaluesToConsider );
+  // we compute all needed eigenvectors for standard classification and variance prediction at ones.
+  // nrOfEigenvaluesToConsiderForVarApprox should NOT be larger than 1 if a method different than approximate_fine is used!
+  this->updateEigenDecomposition(  std::max ( this->nrOfEigenvaluesToConsider, this->nrOfEigenvaluesToConsiderForVarApprox) );
   t1.stop();
   if (verboseTime)
     std::cerr << "Time used for setting up the eigenvectors-objects: " << t1.getLast() << std::endl;
@@ -671,13 +1074,17 @@ void FMKGPHyperparameterOptimization::prepareVarianceApproximationRough()
   if ( q != NULL )
   {   
     double *T = fmk->hikPrepareLookupTableForKVNApproximation ( *q, pf );
-    precomputedTForVarEst = T;
+    this->precomputedTForVarEst = T;
   }
 }
 
 void FMKGPHyperparameterOptimization::prepareVarianceApproximationFine()
 {
-  this->updateEigenDecomposition(  this->nrOfEigenvaluesToConsiderForVarApprox ); 
+  if ( this->eigenMax.size() != this->nrOfEigenvaluesToConsiderForVarApprox) 
+  {
+    std::cerr << "not enough eigenvectors computed for fine approximation of predictive variance. Compute missing ones!" << std::endl;
+    this->updateEigenDecomposition(  this->nrOfEigenvaluesToConsiderForVarApprox ); 
+  }
 }
 
 int FMKGPHyperparameterOptimization::classify ( const NICE::SparseVector & xstar, NICE::SparseVector & scores ) const
@@ -1170,7 +1577,9 @@ t.start();*/
   predVariance = kSelf - currentSecondTerm;
 }    
 
-// ---------------------- STORE AND RESTORE FUNCTIONS ----------------------
+///////////////////// INTERFACE PERSISTENT /////////////////////
+// interface specific methods for store and restore
+///////////////////// INTERFACE PERSISTENT ///////////////////// 
 
 void FMKGPHyperparameterOptimization::restore ( std::istream & is, int format )
 {
@@ -1431,7 +1840,13 @@ void FMKGPHyperparameterOptimization::restore ( std::istream & is, int format )
         is >> labels;        
 	is >> tmp; // end of block 
 	tmp = this->removeEndTag ( tmp );
-      }       
+      }
+      else if  ( tmp.compare("b_usePreviousAlphas") == 0 )
+      {
+        is >> b_usePreviousAlphas;        
+	is >> tmp; // end of block 
+	tmp = this->removeEndTag ( tmp );
+      }          
       else
       {
 	std::cerr << "WARNING -- unexpected FMKGPHyper object -- " << tmp << " -- for restoration... aborting" << std::endl;
@@ -1611,8 +2026,13 @@ void FMKGPHyperparameterOptimization::store ( std::ostream & os, int format ) co
     
     os << this->createStartTag( "labels" ) << std::endl;
     os << labels << std::endl;
-    os << this->createEndTag( "labels" ) << std::endl;     
-
+    os << this->createEndTag( "labels" ) << std::endl;  
+    
+    
+    os << this->createStartTag( "b_usePreviousAlphas" ) << std::endl;
+    os << b_usePreviousAlphas << std::endl;
+    os << this->createEndTag( "b_usePreviousAlphas" ) << std::endl;      
+    
     
     // done
     os << this->createEndTag( "FMKGPHyperparameterOptimization" ) << std::endl;    
@@ -1624,3 +2044,147 @@ void FMKGPHyperparameterOptimization::store ( std::ostream & os, int format ) co
 }
 
 void FMKGPHyperparameterOptimization::clear ( ) {};
+
+///////////////////// INTERFACE ONLINE LEARNABLE /////////////////////
+// interface specific methods for incremental extensions
+///////////////////// INTERFACE ONLINE LEARNABLE /////////////////////
+
+void FMKGPHyperparameterOptimization::addExample( const NICE::SparseVector * example, 
+			     const double & label, 
+			     const bool & performOptimizationAfterIncrement
+			   )
+{
+  
+  std::cerr << " --- FMKGPHyperparameterOptimization::addExample --- " << std::endl;  
+
+  std::set< int > newClasses;
+  
+  this->labels.append ( label );
+  //have we seen this class already?
+  if ( this->knownClasses.find( label ) == this->knownClasses.end() )
+  {
+    this->knownClasses.insert( label );
+    newClasses.insert( label );
+  }    
+
+  std::cerr << "call addExample of fmk object " << std::endl;
+  // add the new example to our data structure
+  // It is necessary to do this already here and not lateron for internal reasons (see GMHIKernel for more details)
+  NICE::Timer t;
+  t.start();
+  this->fmk->addExample ( example, pf );
+  t.stop();
+  if ( this->verboseTime)
+    std::cerr << "Time used for adding the data to the fmk object: " << t.getLast() << std::endl;
+
+  //TODO update the matrix for variance computations as well!!!
+  
+  // add examples to all implicite kernel matrices we currently use
+  this->ikmsum->addExample ( example, label, performOptimizationAfterIncrement );
+  
+  std::cerr << "call updateAfterSingleIncrement " << std::endl;
+  
+  // update the corresponding matrices A, B and lookup tables T  
+  // optional: do the optimization again using the previously known solutions as initialization
+  this->updateAfterSingleIncrement ( *example, newClasses, performOptimizationAfterIncrement );
+  
+  //clean up
+  newClasses.clear();
+  
+  std::cerr << " --- FMKGPHyperparameterOptimization::addExample done --- " << std::endl;   
+}
+
+void FMKGPHyperparameterOptimization::addMultipleExamples( const std::vector< const NICE::SparseVector * > & newExamples,
+				      const NICE::Vector & newLabels,
+				      const bool & performOptimizationAfterIncrement
+				    )
+{
+// // //   //TODO check whether this set is actually needed
+// // //   std::set< int > newClasses;
+// // // 
+// // //   if (this->knownClasses.size() == 1) //binary setting
+// // //   {
+// // //     int oldSize ( this->labels.size() );
+// // //     this->labels.resize ( this->labels.size() + newLabels.size() );
+// // //     for ( uint i = 0; i < newLabels.size(); i++ )
+// // //     {
+// // //       this->labels[i+oldSize] = newLabels[i];
+// // //       //have we seen this class already?
+// // //       if ( (newLabels[i]  != this->binaryLabelPositive) && (newLabels[i]  != this->binaryLabelNegative) )
+// // //       {
+// // //         fthrow(Exception, "Binary setting does not allow adding new classes so far");
+// // // //         knownClasses.insert( newLabels[i] );
+// // // //         newClasses.insert( newLabels[i] );
+// // //       }
+// // //     }      
+// // //   }
+// // //   else //multi-class setting
+// // //   {
+// // //     int oldSize ( this->labels.size() );
+// // //     this->labels.resize ( this->labels.size() + newLabels.size() );
+// // //     for ( uint i = 0; i < newLabels.size(); i++ )
+// // //     {
+// // //       this->labels[i+oldSize] = newLabels[i];
+// // //       //have we seen this class already?
+// // //       if (knownClasses.find( newLabels[i] ) == knownClasses.end() )
+// // //       {
+// // //         knownClasses.insert( newLabels[i] );
+// // //         newClasses.insert( newLabels[i] );
+// // //       }
+// // //     }    
+// // //   }
+// // //   
+// // // 
+// // // 
+// // //   // add the new example to our data structure
+// // //   // It is necessary to do this already here and not lateron for internal reasons (see GMHIKernel for more details)
+// // //   Timer t;
+// // //   t.start();
+// // //   
+// // //   fmk->addMultipleExamples ( newExamples, pf );  
+// // //   t.stop();
+// // //   if (verboseTime)
+// // //     std::cerr << "Time used for adding the data to the fmk object: " << t.getLast() << std::endl;
+// // //   
+// // //     // add examples to all implicite kernel matrices we currently use
+// // //   this->ikmsum->addExample ( example, label, performOptimizationAfterIncrement );
+// // //   
+// // //   Timer tVar;
+// // //   tVar.start();  
+// // //   //do we need  to update our matrices?
+// // //   if ( precomputedAForVarEst.size() != 0)
+// // //   {
+// // //     std::cerr << "update the variance matrices " << std::endl;
+// // //     //this computes everything from the scratch
+// // //     this->prepareVarianceApproximation();
+// // //     //this would perform a more sophisticated update
+// // //     //unfortunately, there is a bug somewhere
+// // //     //TODO fixme!
+// // // //     std::cerr << "update the LUTs needed for variance computation" << std::endl;
+// // // //     for ( std::vector<const NICE::SparseVector*>::const_iterator exampleIt = newExamples.begin(); exampleIt != newExamples.end(); exampleIt++ )
+// // // //     {  
+// // // //       std::cerr << "new example: " << std::endl;
+// // // //       (**exampleIt).store(std::cerr);
+// // // //       std::cerr << "now update the LUT for var est" << std::endl;
+// // // //       fmk->updatePreparationForKVNApproximation( **exampleIt, precomputedAForVarEst, pf );  
+// // // //       if ( q != NULL )
+// // // //       {
+// // // //         fmk->updateLookupTableForKVNApproximation( **exampleIt, precomputedTForVarEst, *q, pf );
+// // // //       }
+// // // //     }
+// // // //     std::cerr << "update of LUTs for variance compuation done" << std::endl;
+// // //   }
+// // //   tVar.stop();
+// // //   if (verboseTime)
+// // //     std::cerr << "Time used for computing the Variance Matrix and LUT: " << tVar.getLast() << std::endl;  
+// // //   
+// // // 
+// // // 
+// // //   // update the corresponding matrices A, B and lookup tables T
+// // //   // optional: do the optimization again using the previously known solutions as initialization
+// // //   updateAfterMultipleIncrements ( newExamples, newClasses, performOptimizationAfterIncrement );
+// // //   
+// // //   //clean up
+// // //   newClasses.clear();
+  
+}

+ 48 - 9
FMKGPHyperparameterOptimization.h

@@ -25,10 +25,12 @@
 #endif
 
 // gp-hik-core includes
-#include "FastMinKernel.h"
-#include "GPLikelihoodApprox.h"
-#include "IKMLinearCombination.h"
-#include "Quantization.h"
+#include "gp-hik-core/FastMinKernel.h"
+#include "gp-hik-core/GPLikelihoodApprox.h"
+#include "gp-hik-core/IKMLinearCombination.h"
+#include "gp-hik-core/OnlineLearnable.h"
+#include "gp-hik-core/Quantization.h"
+
 
 #include "gp-hik-core/parameterizedFunctions/ParameterizedFunction.h"
 
@@ -40,7 +42,7 @@ namespace NICE {
  * @author Erik Rodner, Alexander Freytag
  */
   
-class FMKGPHyperparameterOptimization : NICE::Persistent
+class FMKGPHyperparameterOptimization : public NICE::Persistent, public NICE::OnlineLearnable
 {
   protected:
     enum {
@@ -146,6 +148,24 @@ class FMKGPHyperparameterOptimization : NICE::Persistent
     
     //! contains all class numbers of the currently known classes
     std::set<int> knownClasses;
+    
+    bool b_usePreviousAlphas;
+    
+    //! we store the alpha vectors for good initializations in the IL setting
+    std::map<int, NICE::Vector> lastAlphas;  
+
+    //! Update matrices (A, B, LUTs) and optionally find optimal parameters after adding a new example.  
+    void updateAfterSingleIncrement (
+      const NICE::SparseVector & x, 
+      const std::set<int> newClasses,
+      const bool & performOptimizationAfterIncrement = false
+    );    
+    //! Update matrices (A, B, LUTs) and optionally find optimal parameters after adding multiple examples.  
+    void updateAfterMultipleIncrements (
+      const std::vector<const NICE::SparseVector*> & x, 
+      const std::set<int> newClasses,
+      const bool & performOptimizationAfterIncrement = false
+    );     
 
     
   public:  
@@ -165,13 +185,17 @@ class FMKGPHyperparameterOptimization : NICE::Persistent
     /** simple destructor */
     virtual ~FMKGPHyperparameterOptimization();
     
-    // get and set methods
+    ///////////////////// ///////////////////// /////////////////////
+    //                         GET / SET
+    ///////////////////// ///////////////////// ///////////////////// 
     void setParameterUpperBound(const double & _parameterUpperBound);
     void setParameterLowerBound(const double & _parameterLowerBound);  
     
     std::set<int> getKnownClassNumbers ( ) const;
     
-    //high level methods
+    ///////////////////// ///////////////////// /////////////////////
+    //                      CLASSIFIER STUFF
+    ///////////////////// ///////////////////// /////////////////////  
     
     void initialize( const Config *conf, ParameterizedFunction *pf, FastMinKernel *fmk = NULL, const std::string & confSection = "GPHIKClassifier" );
        
@@ -325,12 +349,27 @@ class FMKGPHyperparameterOptimization : NICE::Persistent
     
     
     
-    /** Persistent interface */
+    ///////////////////// INTERFACE PERSISTENT /////////////////////
+    // interface specific methods for store and restore
+    ///////////////////// INTERFACE PERSISTENT ///////////////////// 
+    
     void restore ( std::istream & is, int format = 0 );
     void store ( std::ostream & os, int format = 0 ) const;
     void clear ( ) ;
     
-        
+    ///////////////////// INTERFACE ONLINE LEARNABLE /////////////////////
+    // interface specific methods for incremental extensions
+    ///////////////////// INTERFACE ONLINE LEARNABLE /////////////////////    
+    
+    virtual void addExample( const NICE::SparseVector * example, 
+			     const double & label, 
+			     const bool & performOptimizationAfterIncrement = true
+			   );
+			   
+    virtual void addMultipleExamples( const std::vector< const NICE::SparseVector * > & newExamples,
+				      const NICE::Vector & newLabels,
+				      const bool & performOptimizationAfterIncrement = true
+				    );         
 };
 
 }

+ 67 - 11
FastMinKernel.cpp

@@ -72,6 +72,36 @@ FastMinKernel::~FastMinKernel()
 {
 }
 
+
+///////////////////// ///////////////////// /////////////////////
+//                         GET / SET
+///////////////////// ///////////////////// ///////////////////// 
+
+void FastMinKernel::setVerbose( const bool & _verbose)
+{
+  verbose = _verbose;
+}
+
+bool FastMinKernel::getVerbose( )   const
+{
+  return verbose;
+}
+
+void FastMinKernel::setDebug( const bool & _debug)
+{
+  debug = _debug;
+  X_sorted.setDebug( _debug );
+}
+
+bool FastMinKernel::getDebug( )   const
+{
+  return debug;
+}
+
+///////////////////// ///////////////////// /////////////////////
+//                      CLASSIFIER STUFF
+///////////////////// ///////////////////// /////////////////////
+
 void FastMinKernel::applyFunctionToFeatureMatrix ( const NICE::ParameterizedFunction *pf)
 {
   this->X_sorted.applyFunctionToFeatureMatrix(pf);
@@ -1375,7 +1405,9 @@ void FastMinKernel::hikComputeKernelVector( const NICE::Vector & xstar, NICE::Ve
   }  
 }
 
-// ---------------------- STORE AND RESTORE FUNCTIONS ----------------------
+///////////////////// INTERFACE PERSISTENT /////////////////////
+// interface specific methods for store and restore
+///////////////////// INTERFACE PERSISTENT ///////////////////// 
 
 void FastMinKernel::restore ( std::istream & is, int format )
 {
@@ -1507,23 +1539,47 @@ void FastMinKernel::clear ()
   std::cerr << "FastMinKernel clear-function called" << std::endl;
 }
 
-void FastMinKernel::setVerbose( const bool & _verbose)
+///////////////////// INTERFACE ONLINE LEARNABLE /////////////////////
+// interface specific methods for incremental extensions
+///////////////////// INTERFACE ONLINE LEARNABLE /////////////////////
+
+void FastMinKernel::addExample( const NICE::SparseVector * example, 
+			     const double & label, 
+			     const bool & performOptimizationAfterIncrement
+			   )
 {
-  verbose = _verbose;
+  // no parameterized function was given - use default 
+  this->addExample ( example );
 }
 
-bool FastMinKernel::getVerbose( )   const
+
+void FastMinKernel::addMultipleExamples( const std::vector< const NICE::SparseVector * > & newExamples,
+				      const NICE::Vector & newLabels,
+				      const bool & performOptimizationAfterIncrement
+				    )
 {
-  return verbose;
+  // no parameterized function was given - use default   
+  this->addMultipleExamples ( newExamples );
 }
 
-void FastMinKernel::setDebug( const bool & _debug)
-{
-  debug = _debug;
-  X_sorted.setDebug( _debug );
+void FastMinKernel::addExample( const NICE::SparseVector * example, 
+			          const NICE::ParameterizedFunction *pf
+			        )
+{ 
+  X_sorted.add_feature( *example, pf );
+  n++;
 }
 
-bool FastMinKernel::getDebug( )   const
+void FastMinKernel::addMultipleExamples( const std::vector< const NICE::SparseVector * > & newExamples,
+				           const NICE::ParameterizedFunction *pf
+				         )
 {
-  return debug;
+  for ( std::vector< const NICE::SparseVector * >::const_iterator exIt = newExamples.begin();
+        exIt != newExamples.end();
+        exIt++ )
+  {
+    X_sorted.add_feature( **exIt, pf );
+    n++;     
+  } 
 }
+

+ 41 - 3
FastMinKernel.h

@@ -11,16 +11,20 @@
 #include <iostream>
 
 // NICE-core includes
+#include <core/basics/Config.h>
 #include <core/basics/Exception.h>
 #include <core/basics/Persistent.h>
 // 
+// 
 #include <core/vector/MatrixT.h>
 #include <core/vector/SparseVectorT.h>
+#include <core/vector/VectorT.h>
 #include <core/vector/VVector.h>
 
 // gp-hik-core includes
-#include "FeatureMatrixT.h"
-#include "Quantization.h"
+#include "gp-hik-core/FeatureMatrixT.h"
+#include "gp-hik-core/OnlineLearnable.h"
+#include "gp-hik-core/Quantization.h"
 #include "gp-hik-core/parameterizedFunctions/ParameterizedFunction.h"
 
 namespace NICE {
@@ -33,7 +37,7 @@ namespace NICE {
  */  
   
   /** interface to FastMinKernel implementation*/
-  class FastMinKernel : NICE::Persistent
+  class FastMinKernel : public NICE::Persistent, public OnlineLearnable
   {
 
     protected:
@@ -437,6 +441,40 @@ namespace NICE {
       virtual void store ( std::ostream & os, int format = 0 ) const; 
       virtual void clear ();
       
+    ///////////////////// INTERFACE ONLINE LEARNABLE /////////////////////
+    // interface specific methods for incremental extensions
+    ///////////////////// INTERFACE ONLINE LEARNABLE /////////////////////
+      
+    virtual void addExample( const NICE::SparseVector * example, 
+			     const double & label, 
+			     const bool & performOptimizationAfterIncrement = true
+			   );
+			   
+    virtual void addMultipleExamples( const std::vector< const NICE::SparseVector * > & newExamples,
+				      const NICE::Vector & newLabels,
+				      const bool & performOptimizationAfterIncrement = true
+				    );  
+    
+
+      /**
+      * @brief Add a new example to the feature-storage. You have to update the corresponding variables explicitely after that.
+      * @author Alexander Freytag
+      * @date 02-01-2014 (dd-mm-yyyy)
+      *
+      * @param example new feature vector
+      */       
+      void addExample(const NICE::SparseVector * example, const NICE::ParameterizedFunction *pf = NULL);
+      
+      /**
+      * @brief Add multiple new example to the feature-storage. You have to update the corresponding variables explicitely after that.
+      * @author Alexander Freytag
+      * @date 02-01-2014 (dd-mm-yyyy)
+      *
+      * @param newExamples new feature vectors
+      */       
+      void addMultipleExamples(const std::vector<const NICE::SparseVector * > & newExamples, const NICE::ParameterizedFunction *pf = NULL);        
+      
+      
      
 
   };

+ 20 - 0
GMHIKernel.cpp

@@ -198,3 +198,23 @@ void GMHIKernel::setApproximationScheme(const int & _approxScheme)
 {
   this->fmk->setApproximationScheme(_approxScheme);
 }
+
+///////////////////// INTERFACE ONLINE LEARNABLE /////////////////////
+// interface specific methods for incremental extensions
+///////////////////// INTERFACE ONLINE LEARNABLE /////////////////////
+
+void GMHIKernel::addExample( const NICE::SparseVector * example, 
+			     const double & label, 
+			     const bool & performOptimizationAfterIncrement
+			   )
+{
+  //nothing has to be done here, the fmk-object got new examples already in outer struct (FMKGPHyperparameterOptimization)
+}
+
+void GMHIKernel::addMultipleExamples( const std::vector< const NICE::SparseVector * > & newExamples,
+				      const NICE::Vector & newLabels,
+				      const bool & performOptimizationAfterIncrement
+				    )
+{
+  //nothing has to be done here, the fmk-object got new examples already in outer struct (FMKGPHyperparameterOptimization)
+}

+ 20 - 2
GMHIKernel.h

@@ -73,12 +73,30 @@ class GMHIKernel : public ImplicitKernelMatrix
     virtual double approxFrobNorm() const;
     virtual void setApproximationScheme(const int & _approxScheme);
     
-    /** Persistent interface */
+    void setFastMinKernel(NICE::FastMinKernel * _fmk){fmk = _fmk;};
+    
+    ///////////////////// INTERFACE PERSISTENT /////////////////////
+    // interface specific methods for store and restore
+    ///////////////////// INTERFACE PERSISTENT /////////////////////
     virtual void restore ( std::istream & is, int format = 0 ) {};//fmk->restore( is, format );};
     virtual void store ( std::ostream & os, int format = 0 ) const {};//fmk->store( os, format );};
     virtual void clear () {};
     
-    void setFastMinKernel(NICE::FastMinKernel * _fmk){fmk = _fmk;};
+
+    
+    ///////////////////// INTERFACE ONLINE LEARNABLE /////////////////////
+    // interface specific methods for incremental extensions
+    ///////////////////// INTERFACE ONLINE LEARNABLE /////////////////////    
+    
+    virtual void addExample( const NICE::SparseVector * example, 
+			     const double & label, 
+			     const bool & performOptimizationAfterIncrement = true
+			   );
+			   
+    virtual void addMultipleExamples( const std::vector< const NICE::SparseVector * > & newExamples,
+				      const NICE::Vector & newLabels,
+				      const bool & performOptimizationAfterIncrement = true
+				    );     
      
 };
 

+ 122 - 57
GPHIKClassifier.cpp

@@ -22,35 +22,11 @@
 using namespace std;
 using namespace NICE;
 
-
-GPHIKClassifier::GPHIKClassifier( const Config *conf, const string & s_confSection ) 
-{
-  //default settings, may be overwritten lateron
-  gphyper = NULL;
-  pf = NULL;
-  confCopy = NULL;
-  //just a default value
-  uncertaintyPredictionForClassification = false;
-  
-  this->confSection = s_confSection;
-  
-  // if no config file was given, we either restore the classifier from an external file, or run ::init with 
-  // an emtpy config (using default values thereby) when calling the train-method
-  if ( conf != NULL )
-    this->init(conf, confSection);
-}
-
-GPHIKClassifier::~GPHIKClassifier()
-{
-  if ( gphyper != NULL )
-    delete gphyper;
-  
-  if (pf != NULL)
-    delete pf;
-
-  if ( confCopy != NULL )
-    delete confCopy;
-}
+/////////////////////////////////////////////////////
+/////////////////////////////////////////////////////
+//                 PROTECTED METHODS
+/////////////////////////////////////////////////////
+/////////////////////////////////////////////////////
 
 void GPHIKClassifier::init(const Config *conf, const string & s_confSection)
 {
@@ -115,6 +91,57 @@ void GPHIKClassifier::init(const Config *conf, const string & s_confSection)
     std::cerr << "varianceApproximationStrategy: " << s_varianceApproximation  << std::endl;
 }
 
+/////////////////////////////////////////////////////
+/////////////////////////////////////////////////////
+//                 PUBLIC METHODS
+/////////////////////////////////////////////////////
+/////////////////////////////////////////////////////
+GPHIKClassifier::GPHIKClassifier( const Config *conf, const string & s_confSection ) 
+{
+  //default settings, may be overwritten lateron
+  gphyper = NULL;
+  pf = NULL;
+  confCopy = NULL;
+  //just a default value
+  uncertaintyPredictionForClassification = false;
+  
+  this->confSection = s_confSection;
+  
+  // if no config file was given, we either restore the classifier from an external file, or run ::init with 
+  // an emtpy config (using default values thereby) when calling the train-method
+  if ( conf != NULL )
+    this->init(conf, confSection);
+}
+
+GPHIKClassifier::~GPHIKClassifier()
+{
+  if ( gphyper != NULL )
+    delete gphyper;
+  
+  if (pf != NULL)
+    delete pf;
+
+  if ( confCopy != NULL )
+    delete confCopy;
+}
+
+///////////////////// ///////////////////// /////////////////////
+//                         GET / SET
+///////////////////// ///////////////////// ///////////////////// 
+
+std::set<int> GPHIKClassifier::getKnownClassNumbers ( ) const
+{
+  if (gphyper == NULL)
+     fthrow(Exception, "Classifier not trained yet -- aborting!" );  
+  
+  return gphyper->getKnownClassNumbers();
+}
+
+
+///////////////////// ///////////////////// /////////////////////
+//                      CLASSIFIER STUFF
+///////////////////// ///////////////////// /////////////////////
+
 void GPHIKClassifier::classify ( const SparseVector * example,  int & result, SparseVector & scores )
 {
   double tmpUncertainty;
@@ -220,6 +247,11 @@ void GPHIKClassifier::train ( const std::vector< NICE::SparseVector *> & example
   
   if (gphyper != NULL)
      delete gphyper;
+  
+  
+  if ( ( varianceApproximation != APPROXIMATE_FINE) )
+    confCopy->sI ( confSection, "nrOfEigenvaluesToConsiderForVarApprox", 0);
+  
   gphyper = new FMKGPHyperparameterOptimization ( confCopy, pf, fmk, confSection ); 
 
   if (verbose)
@@ -328,27 +360,6 @@ void GPHIKClassifier::train ( const std::vector< SparseVector *> & examples, std
     std::cerr << "Learning finished" << std::endl;
 }
 
-void GPHIKClassifier::clear ()
-{
-  if ( gphyper != NULL )
-  {
-    delete gphyper;
-    gphyper = NULL;
-  }
-  
-  if (pf != NULL)
-  {
-    delete pf;
-    pf = NULL;
-  }
-
-  if ( confCopy != NULL )
-  {
-    delete confCopy; 
-    confCopy = NULL;
-  } 
-}
-
 GPHIKClassifier *GPHIKClassifier::clone () const
 {
   fthrow(Exception, "GPHIKClassifier: clone() not yet implemented" );
@@ -422,9 +433,10 @@ void GPHIKClassifier::predictUncertainty( const NICE::Vector * example, double &
   }
 }
 
-//---------------------------------------------------------------------
-//                           protected methods
-//---------------------------------------------------------------------
+///////////////////// INTERFACE PERSISTENT /////////////////////
+// interface specific methods for store and restore
+///////////////////// INTERFACE PERSISTENT ///////////////////// 
+
 void GPHIKClassifier::restore ( std::istream & is, int format )
 {
   //delete everything we knew so far...
@@ -609,10 +621,63 @@ void GPHIKClassifier::store ( std::ostream & os, int format ) const
   }
 }
 
-std::set<int> GPHIKClassifier::getKnownClassNumbers ( ) const
+void GPHIKClassifier::clear ()
 {
-  if (gphyper == NULL)
-     fthrow(Exception, "Classifier not trained yet -- aborting!" );  
+  if ( gphyper != NULL )
+  {
+    delete gphyper;
+    gphyper = NULL;
+  }
   
-  return gphyper->getKnownClassNumbers();
+  if (pf != NULL)
+  {
+    delete pf;
+    pf = NULL;
+  }
+
+  if ( confCopy != NULL )
+  {
+    delete confCopy; 
+    confCopy = NULL;
+  } 
+}
+
+///////////////////// INTERFACE ONLINE LEARNABLE /////////////////////
+// interface specific methods for incremental extensions
+///////////////////// INTERFACE ONLINE LEARNABLE /////////////////////
+
+void GPHIKClassifier::addExample( const NICE::SparseVector * example, 
+			     const double & label, 
+			     const bool & performOptimizationAfterIncrement
+			   )
+{
+  if ( this->gphyper == NULL )
+     fthrow(Exception, "Classifier not initially trained yet -- aborting!" );     
+  //TODO add option for starting with empty classifier!
+    // -> call train() with converted input here
+  //***done*** // TODO add option to go from 2 to 3 classes!  ***done***
+  // TODO add option going from 1 to 2 classes without adding new alpha vector
+  //***done*** // TODO check variance matrices in update ***done***
+  // TODO add check option for variance update
+  // TODO adapt code for addMultipleExamples
+
+  this->gphyper->addExample( example, label, performOptimizationAfterIncrement );
+
+  std::cerr << " --- GPHIKClassifierIL::addExample done --- " << std::endl;  
+}
+
+void GPHIKClassifier::addMultipleExamples( const std::vector< const NICE::SparseVector * > & newExamples,
+				      const NICE::Vector & newLabels,
+				      const bool & performOptimizationAfterIncrement
+				    )
+{
+  //are new examples available? If not, nothing has to be done
+  if ( newExamples.size() < 1)
+    return;
+
+  if ( this->gphyper == NULL )
+     fthrow(Exception, "Classifier not initially trained yet -- aborting!" );     
+  //TODO add option for starting with empty classifier!
+  
+  this->gphyper->addMultipleExamples( newExamples, newLabels, performOptimizationAfterIncrement );  
 }

+ 39 - 9
GPHIKClassifier.h

@@ -19,7 +19,8 @@
 #include <core/vector/SparseVectorT.h>
 
 // gp-hik-core includes
-#include "FMKGPHyperparameterOptimization.h"
+#include "gp-hik-core/FMKGPHyperparameterOptimization.h"
+#include "gp-hik-core/OnlineLearnable.h"
 #include "gp-hik-core/parameterizedFunctions/ParameterizedFunction.h"
 
 namespace NICE {
@@ -30,7 +31,7 @@ namespace NICE {
  * @author Erik Rodner, Alexander Freytag
  */
  
-class GPHIKClassifier : NICE::Persistent
+class GPHIKClassifier : public NICE::Persistent, public NICE::OnlineLearnable
 {
 
   protected:
@@ -72,8 +73,17 @@ class GPHIKClassifier : NICE::Persistent
       
     /** simple destructor */
     ~GPHIKClassifier();
+    
+    ///////////////////// ///////////////////// /////////////////////
+    //                         GET / SET
+    ///////////////////// ///////////////////// /////////////////////      
+    
+    std::set<int> getKnownClassNumbers ( ) const;    
    
-
+    ///////////////////// ///////////////////// /////////////////////
+    //                      CLASSIFIER STUFF
+    ///////////////////// ///////////////////// /////////////////////      
+    
     /** 
      * @brief classify a given example with the previously learnt model
      * @date 19-06-2012 (dd-mm-yyyy)
@@ -136,11 +146,6 @@ class GPHIKClassifier : NICE::Persistent
      */
     void train ( const std::vector< NICE::SparseVector *> & examples, std::map<int, NICE::Vector> & binLabels );
     
-    /** Persistent interface */
-    void restore ( std::istream & is, int format = 0 );
-    void store ( std::ostream & os, int format = 0 ) const;
-    void clear ();
-
     GPHIKClassifier *clone () const;
 
     /** 
@@ -161,7 +166,32 @@ class GPHIKClassifier : NICE::Persistent
      */       
     void predictUncertainty( const NICE::Vector * example, double & uncertainty );    
     
-    std::set<int> getKnownClassNumbers ( ) const;
+
+
+    ///////////////////// INTERFACE PERSISTENT /////////////////////
+    // interface specific methods for store and restore
+    ///////////////////// INTERFACE PERSISTENT /////////////////////   
+    
+    void restore ( std::istream & is, int format = 0 );
+    void store ( std::ostream & os, int format = 0 ) const;
+    void clear ();
+    
+    
+    ///////////////////// INTERFACE ONLINE LEARNABLE /////////////////////
+    // interface specific methods for incremental extensions
+    ///////////////////// INTERFACE ONLINE LEARNABLE /////////////////////
+    
+    virtual void addExample( const NICE::SparseVector * example, 
+			     const double & label, 
+			     const bool & performOptimizationAfterIncrement = true
+			   );
+			   
+    virtual void addMultipleExamples( const std::vector< const NICE::SparseVector * > & newExamples,
+				      const NICE::Vector & newLabels,
+				      const bool & performOptimizationAfterIncrement = true
+				    );       
+
+
 
 };
 

+ 52 - 9
GPLikelihoodApprox.cpp

@@ -5,22 +5,28 @@
 * @date 02/09/2012
 
 */
+
+// STL includes
 #include <iostream>
 
+// NICE-core includes
 #include <core/algebra/CholeskyRobust.h>
+#include <core/algebra/ILSConjugateGradients.h>
+// 
+#include <core/basics/Timer.h>
+// 
 #include <core/vector/Algorithms.h>
 #include <core/vector/Eigen.h>
 
-#include <core/basics/Timer.h>
-#include <core/algebra/ILSConjugateGradients.h>
+//stuff used for verification only
 #include "kernels/GeneralizedIntersectionKernelFunction.h"
 #include "kernels/IntersectionKernelFunction.h"
 
-
-#include "GPLikelihoodApprox.h"
-#include "IKMLinearCombination.h"
-#include "GMHIKernel.h"
-#include "algebra/LogDetApproxBaiAndGolub.h"
+// gp-hik-core includes
+#include "gp-hik-core/GPLikelihoodApprox.h"
+#include "gp-hik-core/IKMLinearCombination.h"
+#include "gp-hik-core/GMHIKernel.h"
+#include "gp-hik-core/algebra/LogDetApproxBaiAndGolub.h"
 
 
 using namespace std;
@@ -28,7 +34,7 @@ using namespace NICE;
 using namespace OPTIMIZATION;
 
 
-GPLikelihoodApprox::GPLikelihoodApprox( const map<int, Vector> & binaryLabels,
+GPLikelihoodApprox::GPLikelihoodApprox( const std::map<int, NICE::Vector> & binaryLabels,
                                         ImplicitKernelMatrix *ikm,
                                         IterativeLinearSolver *linsolver, 
                                         EigValues *eig,
@@ -55,10 +61,34 @@ GPLikelihoodApprox::GPLikelihoodApprox( const map<int, Vector> & binaryLabels,
     
   this->verbose = false;
   this->debug = false;
+  
+  this->initialAlphaGuess = NULL;
 }
 
 GPLikelihoodApprox::~GPLikelihoodApprox()
 {
+  //we do not have to delete the memory here, since it will be handled externally...
+  // TODO however, if we should copy the whole vector, than we also have to delete it here accordingly! Check this!
+  if ( this->initialAlphaGuess != NULL )
+    this->initialAlphaGuess = NULL;
+}
+
+const std::map<int, Vector> & GPLikelihoodApprox::getBestAlphas () const
+{
+  if ( this->min_alphas.size() > 0 )
+  {
+  // did we already computed a local optimal solution?
+    return this->min_alphas;
+  }
+  else if ( this->initialAlphaGuess != NULL)
+  {
+    std::cerr << "no known alpha vectors so far, take initial guess instaed" << std::endl;
+    // computation not started, but initial guess was given, so use this one
+    return *(this->initialAlphaGuess);
+  }  
+  
+  // nothing known, min_alphas will be empty
+  return this->min_alphas;
 }
 
 void GPLikelihoodApprox::calculateLikelihood ( double mypara, const FeatureMatrix & f, const std::map< int, NICE::Vector > & yset, double noise, double lambdaMax )
@@ -275,8 +305,16 @@ double GPLikelihoodApprox::evaluate(const OPTIMIZATION::matrix_type & x)
      *  This reduces the number of iterations by 5 or 8
      */
     NICE::Vector alpha;
+    if ( this->initialAlphaGuess != NULL )
+    {
+      alpha = this->initialAlphaGuess->find(classCnt)->second;
+    }
+    else
+    {
+      alpha = (binaryLabels[classCnt] * (1.0 / eigenmax[0]) );      
+    }
     
-    alpha = (binaryLabels[classCnt] * (1.0 / eigenmax[0]) );
+
     
     if ( verbose )
       cerr << "Using the standard solver ..." << endl;
@@ -357,6 +395,11 @@ void GPLikelihoodApprox::setParameterUpperBound(const double & _parameterUpperBo
   parameterUpperBound = _parameterUpperBound;
 }
 
+void GPLikelihoodApprox::setInitialAlphaGuess(std::map< int, NICE::Vector >* _initialAlphaGuess)
+{
+  this->initialAlphaGuess = _initialAlphaGuess;
+}
+
 
 void GPLikelihoodApprox::setBinaryLabels(const std::map<int, Vector> & _binaryLabels)
 {

+ 12 - 10
GPLikelihoodApprox.h

@@ -8,18 +8,21 @@
 #ifndef _NICE_GPLIKELIHOODAPPROXINCLUDE
 #define _NICE_GPLIKELIHOODAPPROXINCLUDE
 
+// STL includes
 #include <map>
 
-#include <core/vector/VectorT.h>
-#include <core/basics/Config.h>
+// NICE-core includes
 #include <core/algebra/EigValues.h>
 #include <core/algebra/IterativeLinearSolver.h>
-
+// 
+#include <core/basics/Config.h>
 #include <core/optimization/blackbox/CostFunction.h>
+// 
+#include <core/vector/VectorT.h>
 
-#include "FastMinKernel.h"
-#include "ImplicitKernelMatrix.h"
-
+// gp-hik-core includes
+#include "gp-hik-core/FastMinKernel.h"
+#include "gp-hik-core/ImplicitKernelMatrix.h"
 #include "gp-hik-core/parameterizedFunctions/ParameterizedFunction.h"
 
 namespace NICE {
@@ -57,7 +60,7 @@ class GPLikelihoodApprox : public OPTIMIZATION::CostFunction
     void calculateLikelihood ( double mypara, const FeatureMatrix & f, const std::map< int, NICE::Vector > & yset, double noise, double lambdaMax );
 
     //! last alpha vectors computed (from previous IL-step)
-    std::map<int, Vector> * lastAlphas;
+    std::map<int, NICE::Vector> * initialAlphaGuess;
     
     //! alpha vectors of the best solution
     std::map<int, Vector> min_alphas;
@@ -84,8 +87,6 @@ class GPLikelihoodApprox : public OPTIMIZATION::CostFunction
     /** debug flag for several outputs useful for debugging*/
     bool debug;  
     
-    /** after adding new examples, shall the previous alpha solution be used as an initial guess?*/
-    bool usePreviousAlphas;
 
   public:
 
@@ -125,11 +126,12 @@ class GPLikelihoodApprox : public OPTIMIZATION::CostFunction
     
     // ------ get and set methods ------
     const NICE::Vector & getBestParameters () const { return min_parameter; };
-    const std::map<int, Vector> & getBestAlphas () const { return min_alphas; };
+    const std::map<int, Vector> & getBestAlphas () const;
     
     void setParameterLowerBound(const double & _parameterLowerBound);
     void setParameterUpperBound(const double & _parameterUpperBound);
     
+    void setInitialAlphaGuess(std::map<int, NICE::Vector> * _initialAlphaGuess);
     void setBinaryLabels(const std::map<int, Vector> & _binaryLabels);
     
     void setVerbose( const bool & _verbose );

+ 42 - 11
IKMLinearCombination.cpp

@@ -3,11 +3,13 @@
 * @brief Combination of several (implicit) kernel matrices, such as noise matrix and gp-hik kernel matrix (Implementation)
 * @author Erik Rodner, Alexander Freytag
 * @date 02/14/2012
-
 */
+
+// STL includes
 #include <iostream>
 
-#include "IKMLinearCombination.h"
+// gp-hik-core includes
+#include "gp-hik-core/IKMLinearCombination.h"
 
 using namespace NICE;
 using namespace std;
@@ -15,28 +17,31 @@ using namespace std;
 
 IKMLinearCombination::IKMLinearCombination()
 {
-  verbose = false;
+  this->verbose = false;
 }
 
 IKMLinearCombination::~IKMLinearCombination()
 {
-  if (matrices.size() != 0)
+  if ( this->matrices.size() != 0)
   {
-    for (int i = 0; i < matrices.size(); i++)
-      delete matrices[i];
+    for (int i = 0; i < this->matrices.size(); i++)
+      delete this->matrices[i];
   }
 }
 
+///////////////////// ///////////////////// /////////////////////
+//                         GET / SET
+///////////////////// ///////////////////// ///////////////////
 
 void IKMLinearCombination::getDiagonalElements ( Vector & diagonalElements ) const
 {
   diagonalElements.resize ( rows() );
   diagonalElements.set(0.0);
   
-  for ( vector<ImplicitKernelMatrix *>::const_iterator i = matrices.begin(); i != matrices.end(); i++ )
+  for ( std::vector<NICE::ImplicitKernelMatrix *>::const_iterator i = this->matrices.begin(); i != this->matrices.end(); i++ )
   {
-    ImplicitKernelMatrix *ikm = *i;
-    Vector diagonalElementsSingle;
+    NICE::ImplicitKernelMatrix *ikm = *i;
+    NICE::Vector diagonalElementsSingle;
     ikm->getDiagonalElements ( diagonalElementsSingle );
     diagonalElements += diagonalElementsSingle;
   }
@@ -45,9 +50,9 @@ void IKMLinearCombination::getDiagonalElements ( Vector & diagonalElements ) con
 void IKMLinearCombination::getFirstDiagonalElement ( double & diagonalElement ) const
 {
   diagonalElement = 0.0;
-  for ( vector<ImplicitKernelMatrix *>::const_iterator i = matrices.begin(); i != matrices.end(); i++ )
+  for ( std::vector<NICE::ImplicitKernelMatrix *>::const_iterator i = this->matrices.begin(); i != this->matrices.end(); i++ )
   {
-    ImplicitKernelMatrix *ikm = *i;
+    NICE::ImplicitKernelMatrix *ikm = *i;
     double firstElem;
     ikm->getFirstDiagonalElement(firstElem);
     diagonalElement += firstElem;
@@ -264,3 +269,29 @@ void IKMLinearCombination::store ( std::ostream & os, int format ) const
     std::cerr << "OutStream not initialized - storing not possible!" << std::endl;
   }  
 }
+
+///////////////////// INTERFACE ONLINE LEARNABLE /////////////////////
+// interface specific methods for incremental extensions
+///////////////////// INTERFACE ONLINE LEARNABLE /////////////////////
+
+void IKMLinearCombination::addExample( const NICE::SparseVector * example, 
+			     const double & label, 
+			     const bool & performOptimizationAfterIncrement
+			   )
+{
+  for ( std::vector<NICE::ImplicitKernelMatrix *>::iterator i = matrices.begin(); i != matrices.end(); i++ )
+  {
+    (*i)->addExample( example, label);
+  }  
+}
+
+void IKMLinearCombination::addMultipleExamples( const std::vector< const NICE::SparseVector * > & newExamples,
+				      const NICE::Vector & newLabels,
+				      const bool & performOptimizationAfterIncrement
+				    )
+{
+  for ( std::vector<NICE::ImplicitKernelMatrix *>::iterator i = matrices.begin(); i != matrices.end(); i++ )
+  {
+    (*i)->addMultipleExamples( newExamples, newLabels);
+  }  
+}

+ 22 - 2
IKMLinearCombination.h

@@ -8,8 +8,12 @@
 #ifndef _NICE_IKMLINEARCOMBINATIONINCLUDE
 #define _NICE_IKMLINEARCOMBINATIONINCLUDE
 
+// STL includes
 #include <vector>
-#include "ImplicitKernelMatrix.h"
+
+// gp-hik-core includes
+#include "gp-hik-core/ImplicitKernelMatrix.h"
+#include "gp-hik-core/OnlineLearnable.h"
 
 namespace NICE {
 
@@ -67,10 +71,26 @@ class IKMLinearCombination : public ImplicitKernelMatrix
     ImplicitKernelMatrix * getModel(const uint & idx) const;
     inline int getNumberOfModels(){return matrices.size();};
     
-    /** Persistent interface */
+    ///////////////////// INTERFACE PERSISTENT /////////////////////
+    // interface specific methods for store and restore
+    ///////////////////// INTERFACE PERSISTENT /////////////////////
     virtual void restore ( std::istream & is, int format = 0 ) ;
     virtual void store ( std::ostream & os, int format = 0 ) const;  
     virtual void clear () {};
+    
+    ///////////////////// INTERFACE ONLINE LEARNABLE /////////////////////
+    // interface specific methods for incremental extensions
+    ///////////////////// INTERFACE ONLINE LEARNABLE /////////////////////    
+    
+    virtual void addExample( const NICE::SparseVector * example, 
+			     const double & label, 
+			     const bool & performOptimizationAfterIncrement = true
+			   );
+			   
+    virtual void addMultipleExamples( const std::vector< const NICE::SparseVector * > & newExamples,
+				      const NICE::Vector & newLabels,
+				      const bool & performOptimizationAfterIncrement = true
+				    );      
 
 };
 

+ 27 - 97
IKMNoise.cpp

@@ -20,8 +20,6 @@ IKMNoise::IKMNoise()
   this->size = 0;
   this->noise = 0.1;
   this->optimizeNoise = false;
-  this->np = 0;
-  this->nn = 0;
   this->verbose = false;
 }
 
@@ -30,32 +28,9 @@ IKMNoise::IKMNoise( uint size, double noise, bool optimizeNoise )
   this->size = size;
   this->noise = noise;
   this->optimizeNoise = optimizeNoise;
-  this->np = 0;
-  this->nn = 0;
   this->verbose = false;
 }
 
-IKMNoise::IKMNoise( const Vector & labels, double noise, bool optimizeNoise )
-{
-  this->size = labels.size();
-  this->noise = noise;
-  this->optimizeNoise = optimizeNoise;
-  this->labels = labels;
-  this->np = 0;
-  this->nn = 0;
-  this->verbose = false;
-  for ( uint i = 0 ; i < labels.size(); i++ )
-    if ( labels[i] == 1 ) 
-      this->np++;
-    else
-      this->nn++;
-    
-  if (verbose)
-  {
-    std::cerr << "IKMNoise np : " << np << " nn: " << nn << std::endl;
-  }
-}
-
 
 IKMNoise::~IKMNoise()
 {
@@ -65,47 +40,16 @@ IKMNoise::~IKMNoise()
 void IKMNoise::getDiagonalElements ( Vector & diagonalElements ) const
 {
   diagonalElements.resize( size );
-  if ( labels.size() == 0 ) {
-    diagonalElements.set( noise );
-  } else {
-    for ( uint i = 0 ; i < labels.size(); i++ )
-      if ( labels[i] == 1 ) {
-        diagonalElements[i] = 2*np*noise/size;
-      } else {
-        diagonalElements[i] = 2*nn*noise/size;
-      }
-  }
+  diagonalElements.set( noise );
 }
 
 void IKMNoise::getFirstDiagonalElement ( double & diagonalElement ) const
 {
-  if ( labels.size() == 0 )
-  {
-    if (verbose)
-    {    
-      std::cerr << "IKMNoise::getFirstDiagonalElement  and labels.size() is zero" << std::endl;
-    }
-    diagonalElement = noise ;
-  }
-  else
-  {
-    if ( labels[0] == 1 )
-    {
-      if (verbose)
-      {          
-        std::cerr << "IKMNoise::getFirstDiagonalElement -- and first entry is +1" << std::endl;
-      }
-      diagonalElement = 2*np*noise/size;
-    } 
-    else
-    {
-      if (verbose)
-      {                
-        std::cerr << "IKMNoise::getFirstDiagonalElement -- and first entry is -1" << std::endl;
-      }
-      diagonalElement = 2*nn*noise/size;
-    }
+  if (verbose)
+  {    
+    std::cerr << "IKMNoise::getFirstDiagonalElement  and labels.size() is zero" << std::endl;
   }
+  diagonalElement = noise ;
 }
 
 
@@ -161,17 +105,7 @@ void IKMNoise::multiply (NICE::Vector & y, const NICE::Vector & x) const
 {
   y.resize( rows() );
   
-  if ( labels.size() == 0 )
-  {
-    y = noise * x;
-  } else {
-    for ( uint i = 0 ; i < labels.size(); i++ )
-      if ( labels[i] == 1 ) {
-        y[i] = 2*np*noise/size * x[i];
-      } else {
-        y[i] = 2*nn*noise/size * x[i];
-      }
-  }
+  y = noise * x;
 }
 
 uint IKMNoise::rows () const
@@ -228,18 +162,6 @@ void IKMNoise::restore ( std::istream & is, int format )
       {
           is >> optimizeNoise;
       }
-      else if ( tmp.compare("np") == 0 )
-      {
-          is >> np;
-      }
-      else if ( tmp.compare("nn") == 0 )
-      {
-          is >> nn;
-      }
-      else if ( tmp.compare("labels") == 0 )
-      {
-          is >> labels;
-      }
       else
       {
 	std::cerr << "WARNING -- unexpected IKMNoise object -- " << tmp << " -- for restoration... aborting" << std::endl;
@@ -273,20 +195,28 @@ void IKMNoise::store ( std::ostream & os, int format ) const
   
   os << this->createStartTag( "optimizeNoise" ) << std::endl;
   os << optimizeNoise << std::endl;
-  os << this->createEndTag( "optimizeNoise" ) << std::endl;
-  
-  os << this->createStartTag( "np" ) << std::endl;
-  os << np << std::endl;
-  os << this->createEndTag( "np" ) << std::endl;
-  
-  os << this->createStartTag( "nn" ) << std::endl;
-  os << nn << std::endl;
-  os << this->createEndTag( "nn" ) << std::endl;
-  
-  os << this->createStartTag( "labels" ) << std::endl;
-  os << labels << std::endl;
-  os << this->createEndTag( "labels" ) << std::endl;   
+  os << this->createEndTag( "optimizeNoise" ) << std::endl; 
   
   // done
   os << this->createEndTag( "IKMNoise" ) << std::endl;
 }
+
+///////////////////// INTERFACE ONLINE LEARNABLE /////////////////////
+// interface specific methods for incremental extensions
+///////////////////// INTERFACE ONLINE LEARNABLE /////////////////////
+
+void IKMNoise::addExample( const NICE::SparseVector * example, 
+			     const double & label, 
+			     const bool & performOptimizationAfterIncrement
+			   )
+{
+ this->size++;
+}
+
+void IKMNoise::addMultipleExamples( const std::vector< const NICE::SparseVector * > & newExamples,
+				      const NICE::Vector & newLabels,
+				      const bool & performOptimizationAfterIncrement
+				    )
+{
+  this->size += newExamples.size();
+}

+ 18 - 6
IKMNoise.h

@@ -23,7 +23,6 @@ class IKMNoise : public ImplicitKernelMatrix
 {
 
   protected:
-    NICE::Vector labels;
 
     uint size;
 
@@ -31,9 +30,7 @@ class IKMNoise : public ImplicitKernelMatrix
 
     bool optimizeNoise;
 
-    uint np;
-    uint nn;
-    
+   
     /** give some debug outputs. There is not set function so far... */
     bool verbose;
   
@@ -43,7 +40,6 @@ class IKMNoise : public ImplicitKernelMatrix
     
     IKMNoise( uint size, double noise, bool optimizeNoise );
     
-    IKMNoise( const NICE::Vector & labels, double noise, bool optimizeNoise );
       
     virtual ~IKMNoise();
 
@@ -70,11 +66,27 @@ class IKMNoise : public ImplicitKernelMatrix
     virtual double approxFrobNorm() const;
     virtual void setApproximationScheme(const int & _approxScheme) {};
     
-    /** Persistent interface */
+    ///////////////////// INTERFACE PERSISTENT /////////////////////
+    // interface specific methods for store and restore
+    ///////////////////// INTERFACE PERSISTENT /////////////////////
     virtual void restore ( std::istream & is, int format = 0 );
     virtual void store ( std::ostream & os, int format = 0 ) const; 
     virtual void clear () {};
     
+    ///////////////////// INTERFACE ONLINE LEARNABLE /////////////////////
+    // interface specific methods for incremental extensions
+    ///////////////////// INTERFACE ONLINE LEARNABLE /////////////////////    
+    
+    virtual void addExample( const NICE::SparseVector * example, 
+			     const double & label, 
+			     const bool & performOptimizationAfterIncrement = true
+			   );
+			   
+    virtual void addMultipleExamples( const std::vector< const NICE::SparseVector * > & newExamples,
+				      const NICE::Vector & newLabels,
+				      const bool & performOptimizationAfterIncrement = true
+				    );        
+    
 
 };
 

+ 24 - 3
ImplicitKernelMatrix.h

@@ -8,11 +8,16 @@
 #ifndef _NICE_IMPLICITKERNELMATRIXINCLUDE
 #define _NICE_IMPLICITKERNELMATRIXINCLUDE
 
+// STL includes
 #include <iostream>
 
+// NICE-core includes
 #include <core/algebra/GenericMatrix.h>
+// 
+#include <core/basics/Persistent.h>
 
-#include "core/basics/Persistent.h"
+// gp-hik-core includes
+#include "gp-hik-core/OnlineLearnable.h"
 
 namespace NICE {
   
@@ -22,7 +27,7 @@ namespace NICE {
  * @date 02/14/2012
  */
 
-class ImplicitKernelMatrix : public GenericMatrix, public NICE::Persistent
+class ImplicitKernelMatrix : public GenericMatrix, public NICE::Persistent, public NICE::OnlineLearnable
 {
 
   protected:
@@ -51,11 +56,27 @@ class ImplicitKernelMatrix : public GenericMatrix, public NICE::Persistent
     virtual double approxFrobNorm() const = 0;
     virtual void setApproximationScheme(const int & _approxScheme) = 0;
     
-    /** Persistent interface */
+    ///////////////////// INTERFACE PERSISTENT /////////////////////
+    // interface specific methods for store and restore
+    ///////////////////// INTERFACE PERSISTENT /////////////////////
     virtual void restore ( std::istream & is, int format = 0 ) = 0;
     virtual void store ( std::ostream & os, int format = 0 )  const = 0;
     virtual void clear () = 0;
     
+    ///////////////////// INTERFACE ONLINE LEARNABLE /////////////////////
+    // interface specific methods for incremental extensions
+    ///////////////////// INTERFACE ONLINE LEARNABLE /////////////////////    
+    
+    virtual void addExample( const NICE::SparseVector * example, 
+			     const double & label, 
+			     const bool & performOptimizationAfterIncrement = true
+			   ) = 0;
+			   
+    virtual void addMultipleExamples( const std::vector< const NICE::SparseVector * > & newExamples,
+				      const NICE::Vector & newLabels,
+				      const bool & performOptimizationAfterIncrement = true
+				    ) = 0;      
+    
     //high order methods
     virtual void  multiply (NICE::Vector &y, const NICE::Vector &x) const = 0;
 };

+ 46 - 0
OnlineLearnable.h

@@ -0,0 +1,46 @@
+#ifndef _NICE_ONLINELEARNABLEINCLUDE
+#define _NICE_ONLINELEARNABLEINCLUDE
+
+
+// NICE-core includes
+#include <core/vector/SparseVectorT.h>
+#include <core/vector/VectorT.h>
+
+namespace NICE {
+
+
+ /** 
+ * @class OnlineLearnable
+ * @brief Interface specifying learning algorithms implementing methods for online updates
+ * @author Alexander Freytag
+ * @date 01-01-2014 (dd-mm-yyyy)
+ */ 
+ 
+class OnlineLearnable {
+
+ 
+  public:
+    // Interface specifications
+    virtual void addExample( const NICE::SparseVector * example, 
+			     const double & label, 
+			     const bool & performOptimizationAfterIncrement = true
+			   ) = 0;
+			   
+    virtual void addMultipleExamples( const std::vector< const NICE::SparseVector * > & newExamples,
+				      const NICE::Vector & newLabels,
+				      const bool & performOptimizationAfterIncrement = true
+				    ) = 0;    
+
+
+    // Provided functions and overloaded stream operators
+    virtual ~OnlineLearnable () {};
+    
+    // just to prevent senseless compiler warnings
+    OnlineLearnable() {};   
+
+};
+
+
+} // namespace
+
+#endif

+ 204 - 0
progs/toyExampleStoreRestore.cpp

@@ -0,0 +1,204 @@
+/** 
+* @file toyExampleStoreRestore.cpp
+* @brief 
+* @author Alexander Freytag
+* @date 21-12-2013
+*/
+
+// STL includes
+#include <iostream>
+#include <vector>
+
+// NICE-core includes
+#include <core/basics/Config.h>
+#include <core/basics/Timer.h>
+
+// gp-hik-core includes
+#include "gp-hik-core/GPHIKClassifier.h"
+
+using namespace std; //C basics
+using namespace NICE;  // nice-core
+
+int main (int argc, char* argv[])
+{  
+  
+  NICE::Config conf ( argc, argv );
+  std::string trainData = conf.gS( "main", "trainData", "progs/toyExampleSmallScaleTrain.data" );
+  NICE::GPHIKClassifier * classifier;  
+  
+  //------------- read the training data --------------
+  
+  NICE::Matrix dataTrain;
+  NICE::Vector yBinTrain;
+  NICE::Vector yMultiTrain; 
+
+  std::ifstream ifsTrain ( trainData.c_str() , ios::in );
+
+  if (ifsTrain.good() )
+  {
+    ifsTrain >> dataTrain;
+    ifsTrain >> yBinTrain;
+    ifsTrain >> yMultiTrain;
+    ifsTrain.close();  
+  }
+  else 
+  {
+    std::cerr << "Unable to read training data, aborting." << std::endl;
+    return -1;
+  } 
+  
+  //----------------- convert data to sparse data structures ---------
+  std::vector< NICE::SparseVector *> examplesTrain;
+  examplesTrain.resize( dataTrain.rows() );
+  
+  std::vector< NICE::SparseVector *>::iterator exTrainIt = examplesTrain.begin();
+  for (int i = 0; i < (int)dataTrain.rows(); i++, exTrainIt++)
+  {
+    *exTrainIt =  new NICE::SparseVector( dataTrain.getRow(i) );
+  }  
+  
+  // TRAIN CLASSIFIER FROM SCRATCH
+  
+  classifier = new GPHIKClassifier ( &conf );  
+    
+  classifier->train ( examplesTrain , yMultiTrain );
+  
+  
+  // TEST STORING ABILITIES
+  
+  std::string s_destination_save ( "/home/alex/code/nice/gp-hik-core/progs/myClassifier.txt" );
+  
+  std::filebuf fbOut;
+  fbOut.open ( s_destination_save.c_str(), ios::out );
+  std::ostream os (&fbOut);
+  //
+  classifier->store( os );
+  //   
+  fbOut.close(); 
+  
+  
+  // TEST RESTORING ABILITIES
+    
+  NICE::GPHIKClassifier * classifierRestored = new GPHIKClassifier;  
+      
+  std::string s_destination_load ( "/home/alex/code/nice/gp-hik-core/progs/myClassifier.txt" );
+  
+  std::filebuf fbIn;
+  fbIn.open ( s_destination_load.c_str(), ios::in );
+  std::istream is (&fbIn);
+  //
+  classifierRestored->restore( is );
+  //   
+  fbIn.close();   
+  
+  // TEST both classifiers to produce equal results
+  
+  //------------- read the test data --------------
+  
+  
+  NICE::Matrix dataTest;
+  NICE::Vector yBinTest;
+  NICE::Vector yMultiTest; 
+
+  std::string testData = conf.gS( "main", "testData", "progs/toyExampleTest.data" );  
+  std::ifstream ifsTest ( testData.c_str(), ios::in );
+  if (ifsTest.good() )
+  {
+    ifsTest >> dataTest;
+    ifsTest >> yBinTest;
+    ifsTest >> yMultiTest;
+    ifsTest.close();  
+  }
+  else 
+  {
+    std::cerr << "Unable to read test data, aborting." << std::endl;
+    return -1;
+  }
+  
+  // ------------------------------------------
+  // ------------- PREPARATION --------------
+  // ------------------------------------------   
+  
+  // determine classes known during training and corresponding mapping
+  // thereby allow for non-continous class labels
+  std::set<int> classesKnownTraining = classifier->getKnownClassNumbers();
+  
+  int noClassesKnownTraining ( classesKnownTraining.size() );
+  std::map<int,int> mapClNoToIdxTrain;
+  std::set<int>::const_iterator clTrIt = classesKnownTraining.begin();
+  for ( int i=0; i < noClassesKnownTraining; i++, clTrIt++ )
+      mapClNoToIdxTrain.insert ( std::pair<int,int> ( *clTrIt, i )  );
+  
+  // determine classes known during testing and corresponding mapping
+  // thereby allow for non-continous class labels
+  std::set<int> classesKnownTest;
+  classesKnownTest.clear();
+  
+
+  // determine which classes we have in our label vector
+  // -> MATLAB: myClasses = unique(y);
+  for ( NICE::Vector::const_iterator it = yMultiTest.begin(); it != yMultiTest.end(); it++ )
+  {
+    if ( classesKnownTest.find ( *it ) == classesKnownTest.end() )
+    {
+      classesKnownTest.insert ( *it );
+    }
+  }          
+  
+  int noClassesKnownTest ( classesKnownTest.size() );  
+  std::map<int,int> mapClNoToIdxTest;
+  std::set<int>::const_iterator clTestIt = classesKnownTest.begin();
+  for ( int i=0; i < noClassesKnownTest; i++, clTestIt++ )
+      mapClNoToIdxTest.insert ( std::pair<int,int> ( *clTestIt, i )  ); 
+          
+  
+  NICE::Matrix confusionMatrix         ( noClassesKnownTraining, noClassesKnownTest, 0.0);
+  NICE::Matrix confusionMatrixRestored ( noClassesKnownTraining, noClassesKnownTest, 0.0);
+  
+  NICE::Timer t;
+  double testTime (0.0);
+  
+  double uncertainty;
+  
+  int i_loopEnd  ( (int)dataTest.rows() );
+  
+  
+  for (int i = 0; i < i_loopEnd ; i++)
+  {
+    NICE::Vector example ( dataTest.getRow(i) );
+    NICE::SparseVector scores;
+    int result;
+    
+    // classify with trained classifier 
+    t.start();
+    classifier->classify( &example, result, scores );
+    t.stop();
+    testTime += t.getLast();
+     
+    
+    confusionMatrix( mapClNoToIdxTrain.find(result)->second, mapClNoToIdxTest.find(yMultiTest[i])->second ) += 1.0;
+
+    // classify with restored classifier 
+    t.start();
+    classifierRestored->classify( &example, result, scores );
+    t.stop();
+    testTime += t.getLast();  
+    
+    confusionMatrixRestored( mapClNoToIdxTrain.find(result)->second, mapClNoToIdxTest.find(yMultiTest[i])->second ) += 1.0;
+    
+    
+  }  
+  
+  confusionMatrix.normalizeColumnsL1();
+  std::cerr << confusionMatrix << std::endl;
+
+  std::cerr << "average recognition rate: " << confusionMatrix.trace()/confusionMatrix.cols() << std::endl;
+
+  confusionMatrixRestored.normalizeColumnsL1();
+  std::cerr << confusionMatrixRestored << std::endl;
+
+  std::cerr << "average recognition rate of restored classifier: " << confusionMatrixRestored.trace()/confusionMatrixRestored.cols() << std::endl;
+  
+  
+  return 0;
+}

+ 250 - 0
tests/TestGPHIKOnlineLearnable.cpp

@@ -0,0 +1,250 @@
+/** 
+ * @file TestGPHIKOnlineLearnable.cpp
+ * @brief CppUnit-Testcase to verify that GPHIKClassifier methods herited from Persistent (store and restore) work as desired.
+ * @author Alexander Freytag
+ * @date 21-12-2013
+*/
+
+#ifdef NICE_USELIB_CPPUNIT
+
+// STL includes
+#include <iostream>
+#include <vector>
+
+// NICE-core includes
+#include <core/basics/Config.h>
+#include <core/basics/Timer.h>
+
+// gp-hik-core includes
+#include "gp-hik-core/GPHIKClassifier.h"
+
+#include "TestGPHIKOnlineLearnable.h"
+
+using namespace std; //C basics
+using namespace NICE;  // nice-core
+
+const bool verboseStartEnd = true;
+
+
+CPPUNIT_TEST_SUITE_REGISTRATION( TestGPHIKOnlineLearnable );
+
+void TestGPHIKOnlineLearnable::setUp() {
+}
+
+void TestGPHIKOnlineLearnable::tearDown() {
+}
+void TestGPHIKOnlineLearnable::testOnlineLearningMethods()
+{
+  
+  if (verboseStartEnd)
+    std::cerr << "================== TestGPHIKOnlineLearnable::testOnlineLearningMethods ===================== " << std::endl;  
+  
+  NICE::Config conf;
+  
+  conf.sB ( "GPHIKClassifier", "eig_verbose", false);
+  conf.sS ( "GPHIKClassifier", "optimization_method", "downhillsimplex");
+  
+  std::string trainData = conf.gS( "main", "trainData", "toyExampleSmallScaleTrain.data" );
+  NICE::GPHIKClassifier * classifier;  
+  
+  //------------- read the training data --------------
+  
+  NICE::Matrix dataTrain;
+  NICE::Vector yBinTrain;
+  NICE::Vector yMultiTrain; 
+
+  std::ifstream ifsTrain ( trainData.c_str() , ios::in );
+
+  if ( ifsTrain.good() )
+  {
+    ifsTrain >> dataTrain;
+    ifsTrain >> yBinTrain;
+    ifsTrain >> yMultiTrain;
+    ifsTrain.close();  
+  }
+  else 
+  {
+    std::cerr << "Unable to read training data from file " << trainData << " -- aborting." << std::endl;
+    CPPUNIT_ASSERT ( ifsTrain.good() );
+  } 
+  
+  //----------------- convert data to sparse data structures ---------
+  std::vector< NICE::SparseVector *> examplesTrain;
+  examplesTrain.resize( dataTrain.rows()-1 );
+  
+  std::vector< NICE::SparseVector *>::iterator exTrainIt = examplesTrain.begin();
+  for (int i = 0; i < (int)dataTrain.rows()-1; i++, exTrainIt++)
+  {
+    *exTrainIt =  new NICE::SparseVector( dataTrain.getRow(i) );
+  }  
+  
+  // TRAIN INITIAL CLASSIFIER FROM SCRATCH
+  
+  classifier = new NICE::GPHIKClassifier ( &conf );
+
+  //use all but the first example for training and add the first one lateron
+  NICE::Vector yMultiRelevantTrain  ( yMultiTrain.getRangeRef( 0, yMultiTrain.size()-2  ) );
+
+  std::cerr << "yMultiRelevantTrain: " << yMultiRelevantTrain << std::endl;
+  
+  classifier->train ( examplesTrain , yMultiRelevantTrain );
+  
+  std::cerr << "Training done -- start incremental learning " << std::endl;
+  
+  // RUN INCREMENTAL LEARNING
+  
+  bool performOptimizationAfterIncrement ( false );
+  
+  NICE::SparseVector * exampleToAdd = new NICE::SparseVector ( dataTrain.getRow( (int)dataTrain.rows()-1 ) );
+  classifier->addExample ( exampleToAdd, yMultiTrain[ (int)dataTrain.rows()-2 ], performOptimizationAfterIncrement );
+  
+  std::cerr << "label of example to add: " << yMultiTrain[ (int)dataTrain.rows()-1 ] << std::endl;
+  
+  // TRAIN SECOND CLASSIFIER FROM SCRATCH USING THE SAME OVERALL AMOUNT OF EXAMPLES
+  examplesTrain.push_back(  exampleToAdd );
+
+  NICE::GPHIKClassifier * classifierScratch = new NICE::GPHIKClassifier ( &conf );
+  classifierScratch->train ( examplesTrain, yMultiTrain );
+  
+  std::cerr << "trained both classifiers - now start evaluating them" << std::endl;
+  
+  
+  // TEST that both classifiers produce equal store-files
+   std::string s_destination_save_IL ( "myClassifierIL.txt" );
+  
+  std::filebuf fbOut;
+  fbOut.open ( s_destination_save_IL.c_str(), ios::out );
+  std::ostream os (&fbOut);
+  //
+  classifier->store( os );
+  //   
+  fbOut.close(); 
+  
+  std::string s_destination_save_scratch ( "myClassifierScratch.txt" );
+  
+  std::filebuf fbOutScratch;
+  fbOutScratch.open ( s_destination_save_scratch.c_str(), ios::out );
+  std::ostream osScratch (&fbOutScratch);
+  //
+  classifierScratch->store( osScratch );
+  //   
+  fbOutScratch.close(); 
+  
+  
+  // TEST both classifiers to produce equal results
+  
+  //------------- read the test data --------------
+  
+  
+  NICE::Matrix dataTest;
+  NICE::Vector yBinTest;
+  NICE::Vector yMultiTest; 
+
+  std::string testData = conf.gS( "main", "testData", "toyExampleTest.data" );  
+  std::ifstream ifsTest ( testData.c_str(), ios::in );
+  if ( ifsTest.good() )
+  {
+    ifsTest >> dataTest;
+    ifsTest >> yBinTest;
+    ifsTest >> yMultiTest;
+    ifsTest.close();  
+  }
+  else 
+  {
+    std::cerr << "Unable to read test data, aborting." << std::endl;
+    CPPUNIT_ASSERT ( ifsTest.good() );
+  }
+  
+  // ------------------------------------------
+  // ------------- PREPARATION --------------
+  // ------------------------------------------   
+  
+  // determine classes known during training and corresponding mapping
+  // thereby allow for non-continous class labels
+  std::set<int> classesKnownTraining = classifier->getKnownClassNumbers();
+  
+  int noClassesKnownTraining ( classesKnownTraining.size() );
+  std::map<int,int> mapClNoToIdxTrain;
+  std::set<int>::const_iterator clTrIt = classesKnownTraining.begin();
+  for ( int i=0; i < noClassesKnownTraining; i++, clTrIt++ )
+      mapClNoToIdxTrain.insert ( std::pair<int,int> ( *clTrIt, i )  );
+  
+  // determine classes known during testing and corresponding mapping
+  // thereby allow for non-continous class labels
+  std::set<int> classesKnownTest;
+  classesKnownTest.clear();
+  
+
+  // determine which classes we have in our label vector
+  // -> MATLAB: myClasses = unique(y);
+  for ( NICE::Vector::const_iterator it = yMultiTest.begin(); it != yMultiTest.end(); it++ )
+  {
+    if ( classesKnownTest.find ( *it ) == classesKnownTest.end() )
+    {
+      classesKnownTest.insert ( *it );
+    }
+  }          
+  
+  int noClassesKnownTest ( classesKnownTest.size() );  
+  std::map<int,int> mapClNoToIdxTest;
+  std::set<int>::const_iterator clTestIt = classesKnownTest.begin();
+  for ( int i=0; i < noClassesKnownTest; i++, clTestIt++ )
+      mapClNoToIdxTest.insert ( std::pair<int,int> ( *clTestIt, i )  ); 
+          
+  
+  NICE::Matrix confusionMatrix         ( noClassesKnownTraining, noClassesKnownTest, 0.0);
+  NICE::Matrix confusionMatrixScratch    ( noClassesKnownTraining, noClassesKnownTest, 0.0);
+  
+  std::cerr << "data preparation for testing is done "<< std::endl;
+  
+  int i_loopEnd  ( (int)dataTest.rows() );
+  
+  
+  for (int i = 0; i < i_loopEnd ; i++)
+  {
+    NICE::Vector example ( dataTest.getRow(i) );
+    NICE::SparseVector scores;
+    int result;
+    
+    
+    // classify with incrementally trained classifier 
+    classifier->classify( &example, result, scores );
+    std::cerr << "results with IL classifier: " << std::endl;
+    scores.store ( std::cerr );   
+    
+    confusionMatrix( mapClNoToIdxTrain.find(result)->second, mapClNoToIdxTest.find(yMultiTest[i])->second ) += 1.0;
+
+    // classify with classifier learned from scratch
+    scores.clear();
+    classifierScratch->classify( &example, result, scores );
+    std::cerr << "Results with scratch classifier: " << std::endl;
+    scores.store( std::cerr );
+    std::cerr << std::endl;
+    
+    confusionMatrixScratch( mapClNoToIdxTrain.find(result)->second, mapClNoToIdxTest.find(yMultiTest[i])->second ) += 1.0;
+  }  
+  
+  //TODO also check that both classifiers result in the same store-files
+    
+  std::cerr <<  "postprocess confusion matrices " << std::endl;
+  confusionMatrix.normalizeColumnsL1();
+  double arr ( confusionMatrix.trace()/confusionMatrix.cols() );
+
+  confusionMatrixScratch.normalizeColumnsL1();
+  double arrScratch ( confusionMatrixScratch.trace()/confusionMatrixScratch.cols() );
+
+  
+  CPPUNIT_ASSERT_DOUBLES_EQUAL( arr, arrScratch, 1e-8);
+  
+  // don't waste memory
+  //TODO clean up of training data, also in TestGPHIKPersistent
+  
+  delete classifier;
+  delete classifierScratch;
+  
+  if (verboseStartEnd)
+    std::cerr << "================== TestGPHIKOnlineLearnable::testOnlineLearningMethods done ===================== " << std::endl;  
+  
+}
+
+#endif

+ 30 - 0
tests/TestGPHIKOnlineLearnable.h

@@ -0,0 +1,30 @@
+#ifndef _TESTGPHIKONLINELEARNABLE_H
+#define _TESTGPHIKONLINELEARNABLE_H
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <gp-hik-core/GPHIKClassifier.h>
+
+/**
+ * CppUnit-Testcase. 
+ * @brief CppUnit-Testcase to verify that GPHIKClassifierIL methods herited from OnlineLearnable (addExample and addMultipleExamples) work as desired.
+ * @author Alexander Freytag
+ * @date 03-11-2014 (dd-mm-yyyy)
+ */
+class TestGPHIKOnlineLearnable : public CppUnit::TestFixture {
+
+    CPPUNIT_TEST_SUITE( TestGPHIKOnlineLearnable );
+	 CPPUNIT_TEST(testOnlineLearningMethods);
+      
+    CPPUNIT_TEST_SUITE_END();
+  
+ private:
+ 
+ public:
+    void setUp();
+    void tearDown();
+
+
+    void testOnlineLearningMethods();
+};
+
+#endif // _TESTGPHIKONLINELEARNABLE_H