OptimizeConfigParameters.cxx

Go to the documentation of this file.
00001 /**********************************************************************************
00002  * Project: TMVA - a Root-integrated toolkit for multivariate data analysis       *
00003  * Package: TMVA                                                                  *
00004  * Class  : OptimizeConfigParameters                                              *
00005  * Web    : http://tmva.sourceforge.net                                           *
00006  *                                                                                *
00007  * Description: The OptimizeConfigParameters takes care of "scanning/fitting"     *
00008  *              different tuning parameters in order to find the best set of      *
00009  *              tuning paraemters which will be used in the end                   *
00010  *                                                                                *
00011  * Authors (alphabetical):                                                        *
00012  *      Helge Voss      <Helge.Voss@cern.ch>     - MPI-K Heidelberg, Germany      *
00013  *                                                                                *
00014  * Copyright (c) 2005:                                                            *
00015  *      CERN, Switzerland                                                         * 
00016  *      MPI-K Heidelberg, Germany                                                 * 
00017  *                                                                                *
00018  * Redistribution and use in source and binary forms, with or without             *
00019  * modification, are permitted according to the terms listed in LICENSE           *
00020  * (http://ttmva.sourceforge.net/LICENSE)                                         *
00021  **********************************************************************************/
00022 
00023 #include "TMVA/OptimizeConfigParameters.h"
00024 
00025 #include <limits>
00026 #include <cstdlib>
00027 #include "TMath.h"
00028 #include "TGraph.h"
00029 #include "TH1.h"
00030 #include "TH2.h"
00031 #include "TDirectory.h"
00032 
00033 #include "TMVA/IMethod.h"   
00034 #include "TMVA/MethodBase.h"   
00035 #include "TMVA/GeneticFitter.h"
00036 #include "TMVA/MinuitFitter.h"
00037 #include "TMVA/Interval.h"
00038 #include "TMVA/PDF.h"   
00039 #include "TMVA/MsgLogger.h"
00040 #include "TMVA/Tools.h"   
00041 
00042 ClassImp(TMVA::OptimizeConfigParameters)
00043    
00044 //_______________________________________________________________________
00045 TMVA::OptimizeConfigParameters::OptimizeConfigParameters(MethodBase * const method, std::map<TString,TMVA::Interval> tuneParameters, TString fomType, TString optimizationFitType) 
00046 :  fMethod(method),
00047    fTuneParameters(tuneParameters),
00048    fFOMType(fomType),
00049    fOptimizationFitType(optimizationFitType),
00050    fMvaSig(NULL),
00051    fMvaBkg(NULL),
00052    fMvaSigFineBin(NULL),
00053    fMvaBkgFineBin(NULL)
00054 {
00055    // Constructor which sets either "Classification or Regression"
00056   std::string name = "OptimizeConfigParameters_";
00057   name += std::string(GetMethod()->GetName());
00058   fLogger = new MsgLogger(name);
00059    if (fMethod->DoRegression()){
00060       Log() << kFATAL << " ERROR: Sorry, Regression is not yet implement for automatic parameter optimization"
00061             << " --> exit" << Endl;
00062    }
00063 }
00064 
00065 //_______________________________________________________________________
00066 TMVA::OptimizeConfigParameters::~OptimizeConfigParameters() 
00067 {
00068    // the destructor (delete the OptimizeConfigParameters, store the graph and .. delete it)
00069    
00070    GetMethod()->BaseDir()->cd();
00071    Int_t n=Int_t(fFOMvsIter.size());
00072    Float_t *x = new Float_t[n];
00073    Float_t *y = new Float_t[n];
00074    Float_t  ymin=+999999999;
00075    Float_t  ymax=-999999999;
00076 
00077    for (Int_t i=0;i<n;i++){
00078       x[i] = Float_t(i);
00079       y[i] = fFOMvsIter[i];
00080       if (ymin>y[i]) ymin=y[i];
00081       if (ymax<y[i]) ymax=y[i];
00082    }
00083 
00084    TH2D   *h=new TH2D(TString(GetMethod()->GetName())+"_FOMvsIterFrame","",2,0,n,2,ymin*0.95,ymax*1.05);
00085    h->SetXTitle("#iteration "+fOptimizationFitType);
00086    h->SetYTitle(fFOMType);
00087    TGraph *gFOMvsIter = new TGraph(n,x,y);
00088    gFOMvsIter->SetName((TString(GetMethod()->GetName())+"_FOMvsIter").Data());
00089    gFOMvsIter->Write();
00090    h->Write();
00091 
00092    // delete fFOMvsIter;
00093 } 
00094 //_______________________________________________________________________
00095 std::map<TString,Double_t> TMVA::OptimizeConfigParameters::optimize()
00096 {
00097    if      (fOptimizationFitType == "Scan"    ) this->optimizeScan();
00098    else if (fOptimizationFitType == "FitGA" || fOptimizationFitType == "Minuit" ) this->optimizeFit();
00099    else {
00100       Log() << kFATAL << "You have chosen as optimization type " << fOptimizationFitType
00101                 << " that is not (yet) coded --> exit()" << Endl;
00102    }
00103    
00104    Log() << kINFO << "For " << GetMethod()->GetName() << " the optimized Parameters are: " << Endl;
00105    std::map<TString,Double_t>::iterator it;
00106    for(it=fTunedParameters.begin(); it!= fTunedParameters.end(); it++){
00107      Log() << kINFO << it->first << " = " << it->second << Endl;
00108    }
00109    return fTunedParameters;
00110 
00111 }//_______________________________________________________________________
00112 void TMVA::OptimizeConfigParameters::optimizeScan()
00113 {
00114    // do the actual optimization using a simple scan method, 
00115    // i.e. calcualte the FOM for 
00116    // different tuning paraemters and remember which one is
00117    // gave the best FOM
00118 
00119    Double_t      bestFOM=-1000000, currentFOM;
00120 
00121    std::map<TString,Double_t> currentParameters;
00122    std::map<TString,TMVA::Interval>::iterator it;
00123 
00124    // for the scan, start at the lower end of the interval and then "move upwards" 
00125    // initialize all parameters in currentParameter
00126    currentParameters.clear();
00127    fTunedParameters.clear();
00128    for (it=fTuneParameters.begin(); it!=fTuneParameters.end(); it++){
00129       currentParameters.insert(std::pair<TString,Double_t>(it->first,it->second.GetMin()));
00130       fTunedParameters.insert(std::pair<TString,Double_t>(it->first,it->second.GetMin()));
00131    }
00132    // now loop over all the parameters and get for each combination the figure of merit
00133    for (it=fTuneParameters.begin(); it!=fTuneParameters.end(); it++){
00134       for (Int_t ibin=0; ibin<it->second.GetNbins(); ibin++){
00135          currentParameters[it->first] = it->second.GetElement(ibin);
00136          GetMethod()->Reset();
00137          GetMethod()->SetTuneParameters(currentParameters);
00138          // now do the training for the current parameters:
00139          GetMethod()->BaseDir()->cd();
00140          GetMethod()->GetTransformationHandler().CalcTransformations(
00141                                                                     GetMethod()->Data()->GetEventCollection());
00142          GetMethod()->Train();
00143          currentFOM = GetFOM(); 
00144      
00145          if (currentFOM > bestFOM) {
00146             bestFOM = currentFOM;
00147             for (std::map<TString,Double_t>::iterator iter=currentParameters.begin();
00148                  iter != currentParameters.end(); iter++){
00149               fTunedParameters[iter->first]=iter->second;
00150             }
00151          }
00152       }
00153    }
00154 
00155    GetMethod()->Reset();
00156    GetMethod()->SetTuneParameters(fTunedParameters);
00157 }
00158 
00159 void TMVA::OptimizeConfigParameters::optimizeFit()
00160 {
00161    // ranges (intervals) in which the fit varies the parameters
00162    std::vector<TMVA::Interval*> ranges; // intervals of the fit ranges
00163    std::map<TString, TMVA::Interval>::iterator it;
00164    std::vector<Double_t> pars;    // current (starting) fit parameters
00165 
00166    for (it=fTuneParameters.begin(); it != fTuneParameters.end(); it++){
00167       ranges.push_back(new TMVA::Interval(it->second)); 
00168       pars.push_back( (it->second).GetMean() );  // like this the order is "right". Always keep the
00169                                                  // order in the vector "pars" the same as the iterator
00170                                                  // iterates through the tuneParameters !!!!
00171    }
00172 
00173    // create the fitter
00174 
00175    FitterBase* fitter = NULL;
00176 
00177    if ( fOptimizationFitType == "Minuit"  ) {
00178      TString opt="";
00179      fitter = new MinuitFitter(  *this, 
00180                                  "FitterMinuit_BDTOptimize", 
00181                                  ranges, opt );
00182    }else if ( fOptimizationFitType == "FitGA"  ) {
00183      TString opt="PopSize=20:Steps=30:Cycles=3:ConvCrit=0.01:SaveBestCycle=5";
00184      fitter = new GeneticFitter( *this, 
00185                                  "FitterGA_BDTOptimize", 
00186                                  ranges, opt );
00187    } else {
00188       Log() << kWARNING << " you did not specify a valid OptimizationFitType " 
00189             << " will use the default (FitGA) " << Endl;
00190       TString opt="PopSize=20:Steps=30:Cycles=3:ConvCrit=0.01:SaveBestCycle=5";
00191       fitter = new GeneticFitter( *this, 
00192                                   "FitterGA_BDTOptimize", 
00193                                   ranges, opt );      
00194    } 
00195    
00196    fitter->CheckForUnusedOptions();
00197    
00198    // perform the fit
00199    fitter->Run(pars);      
00200    
00201    // clean up
00202    for (UInt_t ipar=0; ipar<ranges.size(); ipar++) delete ranges[ipar];
00203    
00204    
00205    GetMethod()->Reset();
00206    
00207    fTunedParameters.clear();
00208    Int_t jcount=0;
00209    for (it=fTuneParameters.begin(); it!=fTuneParameters.end(); it++){
00210       fTunedParameters.insert(std::pair<TString,Double_t>(it->first,pars[jcount++]));
00211    }
00212    
00213    GetMethod()->SetTuneParameters(fTunedParameters);
00214       
00215 }
00216 
00217 //_______________________________________________________________________
00218 Double_t TMVA::OptimizeConfigParameters::EstimatorFunction( std::vector<Double_t> & pars)
00219 {
00220    // return the estimator (from current FOM) for the fitting interface
00221 
00222    std::map< std::vector<Double_t> , Double_t>::const_iterator iter;
00223    iter = fAlreadyTrainedParCombination.find(pars);
00224 
00225    if (iter != fAlreadyTrainedParCombination.end()) {
00226       // std::cout << "I  had trained  Depth=" <<Int_t(pars[0])
00227       //           <<" MinEv=" <<Int_t(pars[1])
00228       //           <<" already --> FOM="<< iter->second <<std::endl; 
00229       return iter->second;
00230    }else{
00231       std::map<TString,Double_t> currentParameters;
00232       Int_t icount =0; // map "pars" to the  map of Tuneparameter, make sure
00233                        // you never screw up this order!!
00234       std::map<TString, TMVA::Interval>::iterator it;
00235       for (it=fTuneParameters.begin(); it!=fTuneParameters.end(); it++){
00236          currentParameters[it->first] = pars[icount++];
00237       }
00238       GetMethod()->Reset();
00239       GetMethod()->SetTuneParameters(currentParameters);
00240       GetMethod()->BaseDir()->cd();
00241       
00242       GetMethod()->GetTransformationHandler().CalcTransformations(
00243                                                                   GetMethod()->Data()->GetEventCollection());
00244       
00245       GetMethod()->Train();
00246       
00247       Double_t currentFOM = GetFOM(); 
00248       
00249       fAlreadyTrainedParCombination.insert(std::make_pair(pars,-currentFOM));
00250       return  -currentFOM;
00251    }
00252 }
00253 
00254 //_______________________________________________________________________
00255 Double_t TMVA::OptimizeConfigParameters::GetFOM()
00256 {
00257   // Return the Figure of Merit (FOM) used in the parameter 
00258   //  optimization process
00259   
00260    Double_t fom=0;
00261    if (fMethod->DoRegression()){
00262       std::cout << " ERROR: Sorry, Regression is not yet implement for automatic parameter optimisation"
00263                 << " --> exit" << std::endl;
00264       std::exit(1);
00265    }else{
00266       if      (fFOMType == "Separation")  fom = GetSeparation();
00267       else if (fFOMType == "ROCIntegral") fom = GetROCIntegral();
00268       else if (fFOMType == "SigEffAt01")  fom = GetSigEffAt(0.1);
00269       else if (fFOMType == "SigEffAt001") fom = GetSigEffAt(0.01);
00270       else {
00271          Log()<<kFATAL << " ERROR, you've specified as Figure of Merit in the \n"
00272               << " parameter optimisation " << fFOMType << " which has not\n"
00273               << " been implemented yet!! ---> exit " << Endl;
00274       }
00275    }
00276    fFOMvsIter.push_back(fom);
00277    return fom;
00278 }
00279 
00280 //_______________________________________________________________________
00281 void TMVA::OptimizeConfigParameters::GetMVADists()
00282 {
00283    // fill the private histograms with the mva distributinos for sig/bkg
00284 
00285    if (fMvaSig) fMvaSig->Delete();
00286    if (fMvaBkg) fMvaBkg->Delete();
00287    if (fMvaSigFineBin) fMvaSigFineBin->Delete();
00288    if (fMvaBkgFineBin) fMvaBkgFineBin->Delete();
00289  
00290    // maybe later on this should be done a bit more clever (time consuming) by
00291    // first determining proper ranges, removing outliers, as we do in the 
00292    // MVA output calculation in MethodBase::TestClassifier...
00293    // --> then it might be possible also to use the splined PDF's which currently
00294    // doesn't seem to work
00295 
00296    fMvaSig        = new TH1D("fMvaSig","",100,-1.5,1.5); //used for spline fit
00297    fMvaBkg        = new TH1D("fMvaBkg","",100,-1.5,1.5); //used for spline fit
00298    fMvaSigFineBin = new TH1D("fMvaSigFineBin","",100000,-1.5,1.5);
00299    fMvaBkgFineBin = new TH1D("fMvaBkgFineBin","",100000,-1.5,1.5);
00300 
00301    const std::vector<Event*> events=fMethod->Data()->GetEventCollection(Types::kTesting);
00302    
00303    UInt_t signalClassNr = fMethod->DataInfo().GetClassInfo("Signal")->GetNumber();
00304 
00305    //   fMethod->GetTransformationHandler().CalcTransformations(fMethod->Data()->GetEventCollection(Types::kTesting));
00306 
00307    for (UInt_t iev=0; iev < events.size() ; iev++){
00308       //      std::cout << " GetMVADists event " << iev << std::endl;
00309       //      std::cout << " Class  = " << events[iev]->GetClass() << std::endl;
00310       //         std::cout << " MVA Value = " << fMethod->GetMvaValue(events[iev]) << std::endl;
00311       if (events[iev]->GetClass() == signalClassNr) {
00312          fMvaSig->Fill(fMethod->GetMvaValue(events[iev]),events[iev]->GetWeight());
00313          fMvaSigFineBin->Fill(fMethod->GetMvaValue(events[iev]),events[iev]->GetWeight());
00314       } else {
00315          fMvaBkg->Fill(fMethod->GetMvaValue(events[iev]),events[iev]->GetWeight());
00316          fMvaBkgFineBin->Fill(fMethod->GetMvaValue(events[iev]),events[iev]->GetWeight());
00317       }
00318    }
00319 }
00320 //_______________________________________________________________________
00321 Double_t TMVA::OptimizeConfigParameters::GetSeparation()
00322 {
00323    // return the searation between the signal and background 
00324    // MVA ouput distribution
00325    GetMVADists();
00326    if (1){
00327       PDF *splS = new PDF( " PDF Sig", fMvaSig, PDF::kSpline2 );
00328       PDF *splB = new PDF( " PDF Bkg", fMvaBkg, PDF::kSpline2 );
00329       return gTools().GetSeparation(*splS,*splB);
00330    }else{
00331       std::cout << "Separation caclulcaton via histograms (not PDFs) seems to give still strange results!! Don't do that, check!!"<<std::endl;
00332       return gTools().GetSeparation(fMvaSigFineBin,fMvaBkgFineBin); // somehow sitll gives strange results!!!! Check!!!
00333    }
00334 }
00335 
00336 
00337 //_______________________________________________________________________
00338 Double_t TMVA::OptimizeConfigParameters::GetROCIntegral() 
00339 {
00340    // calculate the area (integral) under the ROC curve as a
00341    // overall quality measure of the classification
00342    //
00343    // makeing pdfs out of the MVA-ouput distributions doesn't work
00344    // reliably for cases where the MVA-ouput isn't a smooth distribution.
00345    // this happens "frequently" in BDTs for example when the number of
00346    // trees is small resulting in only some discrete possible MVA ouput values.
00347    // (I still leave the code here, but use this with care!!! The default
00348    // however is to use the distributions!!!
00349 
00350    GetMVADists();
00351 
00352    Double_t integral = 0;
00353    if (0){
00354       PDF *pdfS = new PDF( " PDF Sig", fMvaSig, PDF::kSpline2 );
00355       PDF *pdfB = new PDF( " PDF Bkg", fMvaBkg, PDF::kSpline2 );
00356 
00357       Double_t xmin = TMath::Min(pdfS->GetXmin(), pdfB->GetXmin());
00358       Double_t xmax = TMath::Max(pdfS->GetXmax(), pdfB->GetXmax());
00359       
00360       UInt_t   nsteps = 1000;
00361       Double_t step = (xmax-xmin)/Double_t(nsteps);
00362       Double_t cut = xmin;
00363       for (UInt_t i=0; i<nsteps; i++){
00364          integral += (1-pdfB->GetIntegral(cut,xmax)) * pdfS->GetVal(cut);
00365          cut+=step;
00366       } 
00367       integral*=step;
00368    }else{
00369       // sanity checks
00370       if ( (fMvaSigFineBin->GetXaxis()->GetXmin() !=  fMvaBkgFineBin->GetXaxis()->GetXmin()) ||
00371            (fMvaSigFineBin->GetNbinsX() !=  fMvaBkgFineBin->GetNbinsX()) ){
00372          std::cout << " Error in OptimizeConfigParameters GetROCIntegral, unequal histograms for sig and bkg.." << std::endl;
00373          std::exit(1);
00374       }else{
00375           
00376          Double_t *cumulator  = fMvaBkgFineBin->GetIntegral();
00377          Int_t    nbins       = fMvaSigFineBin->GetNbinsX();
00378          // get the true signal integral (CompuetIntegral just return 1 as they 
00379          // automatically normalize. IN ADDITION, they do not account for variable
00380          // bin sizes (which you migh perhaps use later on for the fMvaSig/Bkg histograms)
00381          Double_t sigIntegral = 0;
00382          for (Int_t ibin=1; ibin<=nbins; ibin++){
00383             sigIntegral += fMvaSigFineBin->GetBinContent(ibin) * fMvaSigFineBin->GetBinWidth(ibin);
00384          }
00385          //gTools().NormHist( fMvaSigFineBin  ); // also doesn't  use variable bin width. And callse TH1::Scale, which oddly enough does not change the SumOfWeights !!!
00386 
00387          for (Int_t ibin=1; ibin <= nbins; ibin++){ // don't include under- and overflow bin
00388             integral += (cumulator[ibin]) * fMvaSigFineBin->GetBinContent(ibin)/sigIntegral * fMvaSigFineBin->GetBinWidth(ibin) ;
00389          }
00390       }
00391    }
00392 
00393    return integral;
00394 }
00395 
00396 
00397 //_______________________________________________________________________
00398 Double_t TMVA::OptimizeConfigParameters::GetSigEffAt(Double_t bkgEff) 
00399 {
00400    // calculate the signal efficiency for a given background efficiency 
00401 
00402    GetMVADists();
00403    Double_t sigEff=0;
00404 
00405    // sanity checks
00406    if ( (fMvaSigFineBin->GetXaxis()->GetXmin() !=  fMvaBkgFineBin->GetXaxis()->GetXmin()) ||
00407         (fMvaSigFineBin->GetNbinsX() !=  fMvaBkgFineBin->GetNbinsX()) ){
00408       std::cout << " Error in OptimizeConfigParameters GetSigEffAt, unequal histograms for sig and bkg.." << std::endl;
00409       std::exit(1);
00410    }else{
00411      Double_t *bkgCumulator   = fMvaBkgFineBin->GetIntegral();
00412       Double_t *sigCumulator   = fMvaSigFineBin->GetIntegral();
00413 
00414       Int_t nbins=fMvaBkgFineBin->GetNbinsX();
00415       Int_t ibin=0;
00416    
00417       // std::cout << " bkgIntegral="<<bkgIntegral
00418       //           << " sigIntegral="<<sigIntegral
00419       //           << " bkgCumulator[nbins]="<<bkgCumulator[nbins]
00420       //           << " sigCumulator[nbins]="<<sigCumulator[nbins]
00421       //           << std::endl;
00422 
00423       while (bkgCumulator[nbins-ibin] > (1-bkgEff)) {
00424          sigEff = sigCumulator[nbins]-sigCumulator[nbins-ibin];
00425          ibin++;
00426       }
00427    } 
00428    return sigEff;
00429 }
00430 
00431 

Generated on Tue Jul 5 15:25:12 2011 for ROOT_528-00b_version by  doxygen 1.5.1