151 #include <unordered_map>
154 using std::make_pair;
158 ClassImp(TMVA::MethodBDT);
160 const Int_t TMVA::MethodBDT::fgDebugLevel = 0;
165 TMVA::MethodBDT::MethodBDT( const TString& jobName,
166 const TString& methodTitle,
167 DataSetInfo& theData,
168 const TString& theOption ) :
169 TMVA::MethodBase( jobName, Types::kBDT, methodTitle, theData, theOption)
172 , fSigToBkgFraction(0)
176 , fBaggedBoost(kFALSE)
177 , fBaggedGradBoost(kFALSE)
181 , fMinNodeSizeS("5%")
184 , fMinLinCorrForFisher(.8)
185 , fUseExclusiveVars(0)
186 , fUseYesNoLeaf(kFALSE)
187 , fNodePurityLimit(0)
190 , fPruneMethod(DecisionTree::kNoPruning)
192 , fFValidationEvents(0)
194 , fRandomisedTrees(kFALSE)
196 , fUsePoissonNvars(0)
197 , fUseNTrainEvents(0)
198 , fBaggedSampleFraction(0)
199 , fNoNegWeightsInTraining(kFALSE)
200 , fInverseBoostNegWeights(kFALSE)
201 , fPairNegWeightsGlobal(kFALSE)
202 , fTrainWithNegWeights(kFALSE)
203 , fDoBoostMonitor(kFALSE)
211 , fDoPreselection(kFALSE)
212 , fSkipNormalization(kFALSE)
213 , fHistoricBool(kFALSE)
215 fMonitorNtuple = NULL;
217 fRegressionLossFunctionBDTG =
nullptr;
222 TMVA::MethodBDT::MethodBDT( DataSetInfo& theData,
223 const TString& theWeightFile)
224 : TMVA::MethodBase( Types::kBDT, theData, theWeightFile)
227 , fSigToBkgFraction(0)
231 , fBaggedBoost(kFALSE)
232 , fBaggedGradBoost(kFALSE)
236 , fMinNodeSizeS(
"5%")
239 , fMinLinCorrForFisher(.8)
240 , fUseExclusiveVars(0)
241 , fUseYesNoLeaf(kFALSE)
242 , fNodePurityLimit(0)
245 , fPruneMethod(DecisionTree::kNoPruning)
247 , fFValidationEvents(0)
249 , fRandomisedTrees(kFALSE)
251 , fUsePoissonNvars(0)
252 , fUseNTrainEvents(0)
253 , fBaggedSampleFraction(0)
254 , fNoNegWeightsInTraining(kFALSE)
255 , fInverseBoostNegWeights(kFALSE)
256 , fPairNegWeightsGlobal(kFALSE)
257 , fTrainWithNegWeights(kFALSE)
258 , fDoBoostMonitor(kFALSE)
266 , fDoPreselection(kFALSE)
267 , fSkipNormalization(kFALSE)
268 , fHistoricBool(kFALSE)
270 fMonitorNtuple = NULL;
272 fRegressionLossFunctionBDTG =
nullptr;
282 Bool_t TMVA::MethodBDT::HasAnalysisType( Types::EAnalysisType type, UInt_t numberClasses, UInt_t numberTargets )
284 if (type == Types::kClassification && numberClasses == 2)
return kTRUE;
285 if (type == Types::kMulticlass )
return kTRUE;
286 if( type == Types::kRegression && numberTargets == 1 )
return kTRUE;
335 void TMVA::MethodBDT::DeclareOptions()
337 DeclareOptionRef(fNTrees,
"NTrees",
"Number of trees in the forest");
338 if (DoRegression()) {
339 DeclareOptionRef(fMaxDepth=50,
"MaxDepth",
"Max depth of the decision tree allowed");
341 DeclareOptionRef(fMaxDepth=3,
"MaxDepth",
"Max depth of the decision tree allowed");
344 TString tmp=
"5%";
if (DoRegression()) tmp=
"0.2%";
345 DeclareOptionRef(fMinNodeSizeS=tmp,
"MinNodeSize",
"Minimum percentage of training events required in a leaf node (default: Classification: 5%, Regression: 0.2%)");
347 DeclareOptionRef(fNCuts,
"nCuts",
"Number of grid points in variable range used in finding optimal cut in node splitting");
349 DeclareOptionRef(fBoostType,
"BoostType",
"Boosting type for the trees in the forest (note: AdaCost is still experimental)");
351 AddPreDefVal(TString(
"AdaBoost"));
352 AddPreDefVal(TString(
"RealAdaBoost"));
353 AddPreDefVal(TString(
"AdaCost"));
354 AddPreDefVal(TString(
"Bagging"));
356 AddPreDefVal(TString(
"AdaBoostR2"));
357 AddPreDefVal(TString(
"Grad"));
358 if (DoRegression()) {
359 fBoostType =
"AdaBoostR2";
361 fBoostType =
"AdaBoost";
363 DeclareOptionRef(fAdaBoostR2Loss=
"Quadratic",
"AdaBoostR2Loss",
"Type of Loss function in AdaBoostR2");
364 AddPreDefVal(TString(
"Linear"));
365 AddPreDefVal(TString(
"Quadratic"));
366 AddPreDefVal(TString(
"Exponential"));
368 DeclareOptionRef(fBaggedBoost=kFALSE,
"UseBaggedBoost",
"Use only a random subsample of all events for growing the trees in each boost iteration.");
369 DeclareOptionRef(fShrinkage = 1.0,
"Shrinkage",
"Learning rate for BoostType=Grad algorithm");
370 DeclareOptionRef(fAdaBoostBeta=.5,
"AdaBoostBeta",
"Learning rate for AdaBoost algorithm");
371 DeclareOptionRef(fRandomisedTrees,
"UseRandomisedTrees",
"Determine at each node splitting the cut variable only as the best out of a random subset of variables (like in RandomForests)");
372 DeclareOptionRef(fUseNvars,
"UseNvars",
"Size of the subset of variables used with RandomisedTree option");
373 DeclareOptionRef(fUsePoissonNvars,
"UsePoissonNvars",
"Interpret \"UseNvars\" not as fixed number but as mean of a Poisson distribution in each split with RandomisedTree option");
374 DeclareOptionRef(fBaggedSampleFraction=.6,
"BaggedSampleFraction",
"Relative size of bagged event sample to original size of the data sample (used whenever bagging is used (i.e. UseBaggedBoost, Bagging,)" );
376 DeclareOptionRef(fUseYesNoLeaf=kTRUE,
"UseYesNoLeaf",
377 "Use Sig or Bkg categories, or the purity=S/(S+B) as classification of the leaf node -> Real-AdaBoost");
378 if (DoRegression()) {
379 fUseYesNoLeaf = kFALSE;
382 DeclareOptionRef(fNegWeightTreatment=
"InverseBoostNegWeights",
"NegWeightTreatment",
"How to treat events with negative weights in the BDT training (particular the boosting) : IgnoreInTraining; Boost With inverse boostweight; Pair events with negative and positive weights in training sample and *annihilate* them (experimental!)");
383 AddPreDefVal(TString(
"InverseBoostNegWeights"));
384 AddPreDefVal(TString(
"IgnoreNegWeightsInTraining"));
385 AddPreDefVal(TString(
"NoNegWeightsInTraining"));
386 AddPreDefVal(TString(
"PairNegWeightsGlobal"));
387 AddPreDefVal(TString(
"Pray"));
391 DeclareOptionRef(fCss=1.,
"Css",
"AdaCost: cost of true signal selected signal");
392 DeclareOptionRef(fCts_sb=1.,
"Cts_sb",
"AdaCost: cost of true signal selected bkg");
393 DeclareOptionRef(fCtb_ss=1.,
"Ctb_ss",
"AdaCost: cost of true bkg selected signal");
394 DeclareOptionRef(fCbb=1.,
"Cbb",
"AdaCost: cost of true bkg selected bkg ");
396 DeclareOptionRef(fNodePurityLimit=0.5,
"NodePurityLimit",
"In boosting/pruning, nodes with purity > NodePurityLimit are signal; background otherwise.");
399 DeclareOptionRef(fSepTypeS,
"SeparationType",
"Separation criterion for node splitting");
400 AddPreDefVal(TString(
"CrossEntropy"));
401 AddPreDefVal(TString(
"GiniIndex"));
402 AddPreDefVal(TString(
"GiniIndexWithLaplace"));
403 AddPreDefVal(TString(
"MisClassificationError"));
404 AddPreDefVal(TString(
"SDivSqrtSPlusB"));
405 AddPreDefVal(TString(
"RegressionVariance"));
406 if (DoRegression()) {
407 fSepTypeS =
"RegressionVariance";
409 fSepTypeS =
"GiniIndex";
412 DeclareOptionRef(fRegressionLossFunctionBDTGS =
"Huber",
"RegressionLossFunctionBDTG",
"Loss function for BDTG regression.");
413 AddPreDefVal(TString(
"Huber"));
414 AddPreDefVal(TString(
"AbsoluteDeviation"));
415 AddPreDefVal(TString(
"LeastSquares"));
417 DeclareOptionRef(fHuberQuantile = 0.7,
"HuberQuantile",
"In the Huber loss function this is the quantile that separates the core from the tails in the residuals distribution.");
419 DeclareOptionRef(fDoBoostMonitor=kFALSE,
"DoBoostMonitor",
"Create control plot with ROC integral vs tree number");
421 DeclareOptionRef(fUseFisherCuts=kFALSE,
"UseFisherCuts",
"Use multivariate splits using the Fisher criterion");
422 DeclareOptionRef(fMinLinCorrForFisher=.8,
"MinLinCorrForFisher",
"The minimum linear correlation between two variables demanded for use in Fisher criterion in node splitting");
423 DeclareOptionRef(fUseExclusiveVars=kFALSE,
"UseExclusiveVars",
"Variables already used in fisher criterion are not anymore analysed individually for node splitting");
426 DeclareOptionRef(fDoPreselection=kFALSE,
"DoPreselection",
"and and apply automatic pre-selection for 100% efficient signal (bkg) cuts prior to training");
429 DeclareOptionRef(fSigToBkgFraction=1,
"SigToBkgFraction",
"Sig to Bkg ratio used in Training (similar to NodePurityLimit, which cannot be used in real adaboost");
431 DeclareOptionRef(fPruneMethodS,
"PruneMethod",
"Note: for BDTs use small trees (e.g.MaxDepth=3) and NoPruning: Pruning: Method used for pruning (removal) of statistically insignificant branches ");
432 AddPreDefVal(TString(
"NoPruning"));
433 AddPreDefVal(TString(
"ExpectedError"));
434 AddPreDefVal(TString(
"CostComplexity"));
436 DeclareOptionRef(fPruneStrength,
"PruneStrength",
"Pruning strength");
438 DeclareOptionRef(fFValidationEvents=0.5,
"PruningValFraction",
"Fraction of events to use for optimizing automatic pruning.");
440 DeclareOptionRef(fSkipNormalization=kFALSE,
"SkipNormalization",
"Skip normalization at initialization, to keep expectation value of BDT output according to the fraction of events");
443 DeclareOptionRef(fMinNodeEvents=0,
"nEventsMin",
"deprecated: Use MinNodeSize (in % of training events) instead");
445 DeclareOptionRef(fBaggedGradBoost=kFALSE,
"UseBaggedGrad",
"deprecated: Use *UseBaggedBoost* instead: Use only a random subsample of all events for growing the trees in each iteration.");
446 DeclareOptionRef(fBaggedSampleFraction,
"GradBaggingFraction",
"deprecated: Use *BaggedSampleFraction* instead: Defines the fraction of events to be used in each iteration, e.g. when UseBaggedGrad=kTRUE. ");
447 DeclareOptionRef(fUseNTrainEvents,
"UseNTrainEvents",
"deprecated: Use *BaggedSampleFraction* instead: Number of randomly picked training events used in randomised (and bagged) trees");
448 DeclareOptionRef(fNNodesMax,
"NNodesMax",
"deprecated: Use MaxDepth instead to limit the tree size" );
456 void TMVA::MethodBDT::DeclareCompatibilityOptions() {
457 MethodBase::DeclareCompatibilityOptions();
460 DeclareOptionRef(fHistoricBool=kTRUE,
"UseWeightedTrees",
461 "Use weighted trees or simple average in classification from the forest");
462 DeclareOptionRef(fHistoricBool=kFALSE,
"PruneBeforeBoost",
"Flag to prune the tree before applying boosting algorithm");
463 DeclareOptionRef(fHistoricBool=kFALSE,
"RenormByClass",
"Individually re-normalize each event class to the original size after boosting");
465 AddPreDefVal(TString(
"NegWeightTreatment"),TString(
"IgnoreNegWeights"));
472 void TMVA::MethodBDT::ProcessOptions()
475 if (fSepTypeS ==
"misclassificationerror") fSepType =
new MisClassificationError();
476 else if (fSepTypeS ==
"giniindex") fSepType =
new GiniIndex();
477 else if (fSepTypeS ==
"giniindexwithlaplace") fSepType =
new GiniIndexWithLaplace();
478 else if (fSepTypeS ==
"crossentropy") fSepType =
new CrossEntropy();
479 else if (fSepTypeS ==
"sdivsqrtsplusb") fSepType =
new SdivSqrtSplusB();
480 else if (fSepTypeS ==
"regressionvariance") fSepType = NULL;
482 Log() << kINFO << GetOptions() << Endl;
483 Log() << kFATAL <<
"<ProcessOptions> unknown Separation Index option " << fSepTypeS <<
" called" << Endl;
486 if(!(fHuberQuantile >= 0.0 && fHuberQuantile <= 1.0)){
487 Log() << kINFO << GetOptions() << Endl;
488 Log() << kFATAL <<
"<ProcessOptions> Huber Quantile must be in range [0,1]. Value given, " << fHuberQuantile <<
", does not match this criteria" << Endl;
492 fRegressionLossFunctionBDTGS.ToLower();
493 if (fRegressionLossFunctionBDTGS ==
"huber") fRegressionLossFunctionBDTG =
new HuberLossFunctionBDT(fHuberQuantile);
494 else if (fRegressionLossFunctionBDTGS ==
"leastsquares") fRegressionLossFunctionBDTG =
new LeastSquaresLossFunctionBDT();
495 else if (fRegressionLossFunctionBDTGS ==
"absolutedeviation") fRegressionLossFunctionBDTG =
new AbsoluteDeviationLossFunctionBDT();
497 Log() << kINFO << GetOptions() << Endl;
498 Log() << kFATAL <<
"<ProcessOptions> unknown Regression Loss Function BDT option " << fRegressionLossFunctionBDTGS <<
" called" << Endl;
501 fPruneMethodS.ToLower();
502 if (fPruneMethodS ==
"expectederror") fPruneMethod = DecisionTree::kExpectedErrorPruning;
503 else if (fPruneMethodS ==
"costcomplexity") fPruneMethod = DecisionTree::kCostComplexityPruning;
504 else if (fPruneMethodS ==
"nopruning") fPruneMethod = DecisionTree::kNoPruning;
506 Log() << kINFO << GetOptions() << Endl;
507 Log() << kFATAL <<
"<ProcessOptions> unknown PruneMethod " << fPruneMethodS <<
" option called" << Endl;
509 if (fPruneStrength < 0 && (fPruneMethod != DecisionTree::kNoPruning) && fBoostType!=
"Grad") fAutomatic = kTRUE;
510 else fAutomatic = kFALSE;
511 if (fAutomatic && fPruneMethod==DecisionTree::kExpectedErrorPruning){
513 <<
"Sorry automatic pruning strength determination is not implemented yet for ExpectedErrorPruning" << Endl;
517 if (fMinNodeEvents > 0){
518 fMinNodeSize = Double_t(fMinNodeEvents*100.) / Data()->GetNTrainingEvents();
519 Log() << kWARNING <<
"You have explicitly set ** nEventsMin = " << fMinNodeEvents<<
" ** the min absolute number \n"
520 <<
"of events in a leaf node. This is DEPRECATED, please use the option \n"
521 <<
"*MinNodeSize* giving the relative number as percentage of training \n"
522 <<
"events instead. \n"
523 <<
"nEventsMin="<<fMinNodeEvents<<
"--> MinNodeSize="<<fMinNodeSize<<
"%"
525 Log() << kWARNING <<
"Note also that explicitly setting *nEventsMin* so far OVERWRITES the option recommended \n"
526 <<
" *MinNodeSize* = " << fMinNodeSizeS <<
" option !!" << Endl ;
527 fMinNodeSizeS = Form(
"%F3.2",fMinNodeSize);
530 SetMinNodeSize(fMinNodeSizeS);
534 fAdaBoostR2Loss.ToLower();
536 if (fBoostType==
"Grad") {
537 fPruneMethod = DecisionTree::kNoPruning;
538 if (fNegWeightTreatment==
"InverseBoostNegWeights"){
539 Log() << kINFO <<
"the option NegWeightTreatment=InverseBoostNegWeights does"
540 <<
" not exist for BoostType=Grad" << Endl;
541 Log() << kINFO <<
"--> change to new default NegWeightTreatment=Pray" << Endl;
542 Log() << kDEBUG <<
"i.e. simply keep them as if which should work fine for Grad Boost" << Endl;
543 fNegWeightTreatment=
"Pray";
544 fNoNegWeightsInTraining=kFALSE;
546 }
else if (fBoostType==
"RealAdaBoost"){
547 fBoostType =
"AdaBoost";
548 fUseYesNoLeaf = kFALSE;
549 }
else if (fBoostType==
"AdaCost"){
550 fUseYesNoLeaf = kFALSE;
553 if (fFValidationEvents < 0.0) fFValidationEvents = 0.0;
554 if (fAutomatic && fFValidationEvents > 0.5) {
555 Log() << kWARNING <<
"You have chosen to use more than half of your training sample "
556 <<
"to optimize the automatic pruning algorithm. This is probably wasteful "
557 <<
"and your overall results will be degraded. Are you sure you want this?"
562 if (this->Data()->HasNegativeEventWeights()){
563 Log() << kINFO <<
" You are using a Monte Carlo that has also negative weights. "
564 <<
"That should in principle be fine as long as on average you end up with "
565 <<
"something positive. For this you have to make sure that the minimal number "
566 <<
"of (un-weighted) events demanded for a tree node (currently you use: MinNodeSize="
567 << fMinNodeSizeS <<
" ("<< fMinNodeSize <<
"%)"
568 <<
", (or the deprecated equivalent nEventsMin) you can set this via the "
569 <<
"BDT option string when booking the "
570 <<
"classifier) is large enough to allow for reasonable averaging!!! "
571 <<
" If this does not help.. maybe you want to try the option: IgnoreNegWeightsInTraining "
572 <<
"which ignores events with negative weight in the training. " << Endl
573 << Endl <<
"Note: You'll get a WARNING message during the training if that should ever happen" << Endl;
576 if (DoRegression()) {
577 if (fUseYesNoLeaf && !IsConstructedFromWeightFile()){
578 Log() << kWARNING <<
"Regression Trees do not work with fUseYesNoLeaf=TRUE --> I will set it to FALSE" << Endl;
579 fUseYesNoLeaf = kFALSE;
582 if (fSepType != NULL){
583 Log() << kWARNING <<
"Regression Trees do not work with Separation type other than <RegressionVariance> --> I will use it instead" << Endl;
587 Log() << kWARNING <<
"Sorry, UseFisherCuts is not available for regression analysis, I will ignore it!" << Endl;
588 fUseFisherCuts = kFALSE;
591 Log() << kWARNING <<
"Sorry, the option of nCuts<0 using a more elaborate node splitting algorithm " << Endl;
592 Log() << kWARNING <<
"is not implemented for regression analysis ! " << Endl;
593 Log() << kWARNING <<
"--> I switch do default nCuts = 20 and use standard node splitting"<<Endl;
597 if (fRandomisedTrees){
598 Log() << kINFO <<
" Randomised trees use no pruning" << Endl;
599 fPruneMethod = DecisionTree::kNoPruning;
603 if (fUseFisherCuts) {
604 Log() << kWARNING <<
"When using the option UseFisherCuts, the other option nCuts<0 (i.e. using" << Endl;
605 Log() <<
" a more elaborate node splitting algorithm) is not implemented. " << Endl;
612 Log() << kERROR <<
" Zero Decision Trees demanded... that does not work !! "
613 <<
" I set it to 1 .. just so that the program does not crash"
618 fNegWeightTreatment.ToLower();
619 if (fNegWeightTreatment ==
"ignorenegweightsintraining") fNoNegWeightsInTraining = kTRUE;
620 else if (fNegWeightTreatment ==
"nonegweightsintraining") fNoNegWeightsInTraining = kTRUE;
621 else if (fNegWeightTreatment ==
"inverseboostnegweights") fInverseBoostNegWeights = kTRUE;
622 else if (fNegWeightTreatment ==
"pairnegweightsglobal") fPairNegWeightsGlobal = kTRUE;
623 else if (fNegWeightTreatment ==
"pray") Log() << kDEBUG <<
"Yes, good luck with praying " << Endl;
625 Log() << kINFO << GetOptions() << Endl;
626 Log() << kFATAL <<
"<ProcessOptions> unknown option for treating negative event weights during training " << fNegWeightTreatment <<
" requested" << Endl;
629 if (fNegWeightTreatment ==
"pairnegweightsglobal")
630 Log() << kWARNING <<
" you specified the option NegWeightTreatment=PairNegWeightsGlobal : This option is still considered EXPERIMENTAL !! " << Endl;
637 while (tmp < fNNodesMax){
641 Log() << kWARNING <<
"You have specified a deprecated option *NNodesMax="<<fNNodesMax
642 <<
"* \n this has been translated to MaxDepth="<<fMaxDepth<<Endl;
646 if (fUseNTrainEvents>0){
647 fBaggedSampleFraction = (Double_t) fUseNTrainEvents/Data()->GetNTrainingEvents();
648 Log() << kWARNING <<
"You have specified a deprecated option *UseNTrainEvents="<<fUseNTrainEvents
649 <<
"* \n this has been translated to BaggedSampleFraction="<<fBaggedSampleFraction<<
"(%)"<<Endl;
652 if (fBoostType==
"Bagging") fBaggedBoost = kTRUE;
653 if (fBaggedGradBoost){
654 fBaggedBoost = kTRUE;
655 Log() << kWARNING <<
"You have specified a deprecated option *UseBaggedGrad* --> please use *UseBaggedBoost* instead" << Endl;
662 void TMVA::MethodBDT::SetMinNodeSize(Double_t sizeInPercent){
663 if (sizeInPercent > 0 && sizeInPercent < 50){
664 fMinNodeSize=sizeInPercent;
667 Log() << kFATAL <<
"you have demanded a minimal node size of "
668 << sizeInPercent <<
"% of the training events.. \n"
669 <<
" that somehow does not make sense "<<Endl;
676 void TMVA::MethodBDT::SetMinNodeSize(TString sizeInPercent){
677 sizeInPercent.ReplaceAll(
"%",
"");
678 sizeInPercent.ReplaceAll(
" ",
"");
679 if (sizeInPercent.IsFloat()) SetMinNodeSize(sizeInPercent.Atof());
681 Log() << kFATAL <<
"I had problems reading the option MinNodeEvents, which "
682 <<
"after removing a possible % sign now reads " << sizeInPercent << Endl;
689 void TMVA::MethodBDT::Init(
void )
692 if (fAnalysisType == Types::kClassification || fAnalysisType == Types::kMulticlass ) {
694 fBoostType =
"AdaBoost";
695 if(DataInfo().GetNClasses()!=0)
699 fBoostType =
"AdaBoostR2";
700 fAdaBoostR2Loss =
"Quadratic";
701 if(DataInfo().GetNClasses()!=0)
707 fPruneMethodS =
"NoPruning";
708 fPruneMethod = DecisionTree::kNoPruning;
711 fFValidationEvents = 0.5;
712 fRandomisedTrees = kFALSE;
714 fUseNvars = UInt_t(TMath::Sqrt(GetNvar())+0.6);
715 fUsePoissonNvars = kTRUE;
720 SetSignalReferenceCut( 0 );
727 void TMVA::MethodBDT::Reset(
void )
733 for (UInt_t i=0; i<fForest.size(); i++)
delete fForest[i];
736 fBoostWeights.clear();
737 if (fMonitorNtuple) { fMonitorNtuple->Delete(); fMonitorNtuple=NULL; }
738 fVariableImportance.clear();
740 fLossFunctionEventInfo.clear();
744 if (Data()) Data()->DeleteResults(GetMethodName(), Types::kTraining, GetAnalysisType());
745 Log() << kDEBUG <<
" successfully(?) reset the method " << Endl;
755 TMVA::MethodBDT::~MethodBDT(
void )
757 for (UInt_t i=0; i<fForest.size(); i++)
delete fForest[i];
763 void TMVA::MethodBDT::InitEventSample(
void )
765 if (!HasTrainingTree()) Log() << kFATAL <<
"<Init> Data().TrainingTree() is zero pointer" << Endl;
767 if (fEventSample.size() > 0) {
769 for (UInt_t iev=0; iev<fEventSample.size(); iev++) fEventSample[iev]->SetBoostWeight(1.);
771 Data()->SetCurrentType(Types::kTraining);
772 UInt_t nevents = Data()->GetNTrainingEvents();
774 std::vector<const TMVA::Event*> tmpEventSample;
775 for (Long64_t ievt=0; ievt<nevents; ievt++) {
777 Event*
event =
new Event( *GetTrainingEvent(ievt) );
778 tmpEventSample.push_back(event);
781 if (!DoRegression()) DeterminePreselectionCuts(tmpEventSample);
782 else fDoPreselection = kFALSE;
784 for (UInt_t i=0; i<tmpEventSample.size(); i++)
delete tmpEventSample[i];
787 Bool_t firstNegWeight=kTRUE;
788 Bool_t firstZeroWeight=kTRUE;
789 for (Long64_t ievt=0; ievt<nevents; ievt++) {
792 Event*
event =
new Event( *GetTrainingEvent(ievt) );
793 if (fDoPreselection){
794 if (TMath::Abs(ApplyPreselectionCuts(event)) > 0.05) {
800 if (event->GetWeight() < 0 && (IgnoreEventsWithNegWeightsInTraining() || fNoNegWeightsInTraining)){
801 if (firstNegWeight) {
802 Log() << kWARNING <<
" Note, you have events with negative event weight in the sample, but you've chosen to ignore them" << Endl;
803 firstNegWeight=kFALSE;
806 }
else if (event->GetWeight()==0){
807 if (firstZeroWeight) {
808 firstZeroWeight = kFALSE;
809 Log() <<
"Events with weight == 0 are going to be simply ignored " << Endl;
813 if (event->GetWeight() < 0) {
814 fTrainWithNegWeights=kTRUE;
816 firstNegWeight = kFALSE;
817 if (fPairNegWeightsGlobal){
818 Log() << kWARNING <<
"Events with negative event weights are found and "
819 <<
" will be removed prior to the actual BDT training by global "
820 <<
" paring (and subsequent annihilation) with positiv weight events"
823 Log() << kWARNING <<
"Events with negative event weights are USED during "
824 <<
"the BDT training. This might cause problems with small node sizes "
825 <<
"or with the boosting. Please remove negative events from training "
826 <<
"using the option *IgnoreEventsWithNegWeightsInTraining* in case you "
827 <<
"observe problems with the boosting"
834 Double_t modulo = 1.0/(fFValidationEvents);
835 Int_t imodulo =
static_cast<Int_t
>( fmod(modulo,1.0) > 0.5 ? ceil(modulo) : floor(modulo) );
836 if (ievt % imodulo == 0) fValidationSample.push_back( event );
837 else fEventSample.push_back( event );
840 fEventSample.push_back(event);
846 Log() << kINFO <<
"<InitEventSample> Internally I use " << fEventSample.size()
847 <<
" for Training and " << fValidationSample.size()
848 <<
" for Pruning Validation (" << ((Float_t)fValidationSample.size())/((Float_t)fEventSample.size()+fValidationSample.size())*100.0
849 <<
"% of training used for validation)" << Endl;
853 if (fPairNegWeightsGlobal) PreProcessNegativeEventWeights();
856 if (DoRegression()) {
858 }
else if (DoMulticlass()) {
860 }
else if (!fSkipNormalization) {
862 Log() << kDEBUG <<
"\t<InitEventSample> For classification trees, "<< Endl;
863 Log() << kDEBUG <<
" \tthe effective number of backgrounds is scaled to match "<<Endl;
864 Log() << kDEBUG <<
" \tthe signal. Otherwise the first boosting step would do 'just that'!"<<Endl;
878 Double_t nevents = fEventSample.size();
879 Double_t sumSigW=0, sumBkgW=0;
880 Int_t sumSig=0, sumBkg=0;
881 for (UInt_t ievt=0; ievt<fEventSample.size(); ievt++) {
882 if ((DataInfo().IsSignal(fEventSample[ievt])) ) {
883 sumSigW += fEventSample[ievt]->GetWeight();
886 sumBkgW += fEventSample[ievt]->GetWeight();
890 if (sumSigW && sumBkgW){
891 Double_t normSig = nevents/((1+fSigToBkgFraction)*sumSigW)*fSigToBkgFraction;
892 Double_t normBkg = nevents/((1+fSigToBkgFraction)*sumBkgW); ;
893 Log() << kDEBUG <<
"\tre-normalise events such that Sig and Bkg have respective sum of weights = "
894 << fSigToBkgFraction << Endl;
895 Log() << kDEBUG <<
" \tsig->sig*"<<normSig <<
"ev. bkg->bkg*"<<normBkg <<
"ev." <<Endl;
896 Log() << kHEADER <<
"#events: (reweighted) sig: "<< sumSigW*normSig <<
" bkg: " << sumBkgW*normBkg << Endl;
897 Log() << kINFO <<
"#events: (unweighted) sig: "<< sumSig <<
" bkg: " << sumBkg << Endl;
898 for (Long64_t ievt=0; ievt<nevents; ievt++) {
899 if ((DataInfo().IsSignal(fEventSample[ievt])) ) fEventSample[ievt]->SetBoostWeight(normSig);
900 else fEventSample[ievt]->SetBoostWeight(normBkg);
903 Log() << kINFO <<
"--> could not determine scaling factors as either there are " << Endl;
904 Log() << kINFO <<
" no signal events (sumSigW="<<sumSigW<<
") or no bkg ev. (sumBkgW="<<sumBkgW<<
")"<<Endl;
909 fTrainSample = &fEventSample;
911 GetBaggedSubSample(fEventSample);
912 fTrainSample = &fSubSample;
934 void TMVA::MethodBDT::PreProcessNegativeEventWeights(){
935 Double_t totalNegWeights = 0;
936 Double_t totalPosWeights = 0;
937 Double_t totalWeights = 0;
938 std::vector<const Event*> negEvents;
939 for (UInt_t iev = 0; iev < fEventSample.size(); iev++){
940 if (fEventSample[iev]->GetWeight() < 0) {
941 totalNegWeights += fEventSample[iev]->GetWeight();
942 negEvents.push_back(fEventSample[iev]);
944 totalPosWeights += fEventSample[iev]->GetWeight();
946 totalWeights += fEventSample[iev]->GetWeight();
948 if (totalNegWeights == 0 ) {
949 Log() << kINFO <<
"no negative event weights found .. no preprocessing necessary" << Endl;
952 Log() << kINFO <<
"found a total of " << totalNegWeights <<
" of negative event weights which I am going to try to pair with positive events to annihilate them" << Endl;
953 Log() << kINFO <<
"found a total of " << totalPosWeights <<
" of events with positive weights" << Endl;
954 Log() << kINFO <<
"--> total sum of weights = " << totalWeights <<
" = " << totalNegWeights+totalPosWeights << Endl;
957 std::vector<TMatrixDSym*>* cov = gTools().CalcCovarianceMatrices( fEventSample, 2);
961 for (Int_t i=0; i<2; i++){
962 invCov = ((*cov)[i]);
963 if ( TMath::Abs(invCov->Determinant()) < 10E-24 ) {
964 std::cout <<
"<MethodBDT::PreProcessNeg...> matrix is almost singular with determinant="
965 << TMath::Abs(invCov->Determinant())
966 <<
" did you use the variables that are linear combinations or highly correlated?"
969 if ( TMath::Abs(invCov->Determinant()) < 10E-120 ) {
970 std::cout <<
"<MethodBDT::PreProcessNeg...> matrix is singular with determinant="
971 << TMath::Abs(invCov->Determinant())
972 <<
" did you use the variables that are linear combinations?"
981 Log() << kINFO <<
"Found a total of " << totalNegWeights <<
" in negative weights out of " << fEventSample.size() <<
" training events " << Endl;
982 Timer timer(negEvents.size(),
"Negative Event paired");
983 for (UInt_t nev = 0; nev < negEvents.size(); nev++){
984 timer.DrawProgressBar( nev );
985 Double_t weight = negEvents[nev]->GetWeight();
986 UInt_t iClassID = negEvents[nev]->GetClass();
987 invCov = ((*cov)[iClassID]);
992 Double_t dist, minDist=10E270;
993 for (UInt_t iev = 0; iev < fEventSample.size(); iev++){
994 if (iClassID==fEventSample[iev]->GetClass() && fEventSample[iev]->GetWeight() > 0){
996 for (UInt_t ivar=0; ivar < GetNvar(); ivar++){
997 for (UInt_t jvar=0; jvar<GetNvar(); jvar++){
998 dist += (negEvents[nev]->GetValue(ivar)-fEventSample[iev]->GetValue(ivar))*
999 (*invCov)[ivar][jvar]*
1000 (negEvents[nev]->GetValue(jvar)-fEventSample[iev]->GetValue(jvar));
1003 if (dist < minDist) { iMin=iev; minDist=dist;}
1009 Double_t newWeight = (negEvents[nev]->GetWeight() + fEventSample[iMin]->GetWeight());
1011 negEvents[nev]->SetBoostWeight( 0 );
1012 fEventSample[iMin]->SetBoostWeight( newWeight/fEventSample[iMin]->GetOriginalWeight() );
1014 negEvents[nev]->SetBoostWeight( newWeight/negEvents[nev]->GetOriginalWeight() );
1015 fEventSample[iMin]->SetBoostWeight( 0 );
1018 }
else Log() << kFATAL <<
"preprocessing didn't find event to pair with the negative weight ... probably a bug" << Endl;
1019 weight = negEvents[nev]->GetWeight();
1022 Log() << kINFO <<
"<Negative Event Pairing> took: " << timer.GetElapsedTime()
1026 totalNegWeights = 0;
1027 totalPosWeights = 0;
1029 Double_t sigWeight=0;
1030 Double_t bkgWeight=0;
1034 std::vector<const Event*> newEventSample;
1036 for (UInt_t iev = 0; iev < fEventSample.size(); iev++){
1037 if (fEventSample[iev]->GetWeight() < 0) {
1038 totalNegWeights += fEventSample[iev]->GetWeight();
1039 totalWeights += fEventSample[iev]->GetWeight();
1041 totalPosWeights += fEventSample[iev]->GetWeight();
1042 totalWeights += fEventSample[iev]->GetWeight();
1044 if (fEventSample[iev]->GetWeight() > 0) {
1045 newEventSample.push_back(
new Event(*fEventSample[iev]));
1046 if (fEventSample[iev]->GetClass() == fSignalClass){
1047 sigWeight += fEventSample[iev]->GetWeight();
1050 bkgWeight += fEventSample[iev]->GetWeight();
1055 if (totalNegWeights < 0) Log() << kFATAL <<
" compensation of negative event weights with positive ones did not work " << totalNegWeights << Endl;
1057 for (UInt_t i=0; i<fEventSample.size(); i++)
delete fEventSample[i];
1058 fEventSample = newEventSample;
1060 Log() << kINFO <<
" after PreProcessing, the Event sample is left with " << fEventSample.size() <<
" events (unweighted), all with positive weights, adding up to " << totalWeights << Endl;
1061 Log() << kINFO <<
" nSig="<<nSig <<
" sigWeight="<<sigWeight <<
" nBkg="<<nBkg <<
" bkgWeight="<<bkgWeight << Endl;
1070 std::map<TString,Double_t> TMVA::MethodBDT::OptimizeTuningParameters(TString fomType, TString fitType)
1073 std::map<TString,TMVA::Interval*> tuneParameters;
1074 std::map<TString,Double_t> tunedParameters;
1083 tuneParameters.insert(std::pair<TString,Interval*>(
"NTrees",
new Interval(10,1000,5)));
1084 tuneParameters.insert(std::pair<TString,Interval*>(
"MaxDepth",
new Interval(2,4,3)));
1085 tuneParameters.insert(std::pair<TString,Interval*>(
"MinNodeSize",
new LogInterval(1,30,30)));
1090 if (fBoostType==
"AdaBoost"){
1091 tuneParameters.insert(std::pair<TString,Interval*>(
"AdaBoostBeta",
new Interval(.2,1.,5)));
1093 }
else if (fBoostType==
"Grad"){
1094 tuneParameters.insert(std::pair<TString,Interval*>(
"Shrinkage",
new Interval(0.05,0.50,5)));
1096 }
else if (fBoostType==
"Bagging" && fRandomisedTrees){
1097 Int_t min_var = TMath::FloorNint( GetNvar() * .25 );
1098 Int_t max_var = TMath::CeilNint( GetNvar() * .75 );
1099 tuneParameters.insert(std::pair<TString,Interval*>(
"UseNvars",
new Interval(min_var,max_var,4)));
1103 Log()<<kINFO <<
" the following BDT parameters will be tuned on the respective *grid*\n"<<Endl;
1104 std::map<TString,TMVA::Interval*>::iterator it;
1105 for(it=tuneParameters.begin(); it!= tuneParameters.end(); ++it){
1106 Log() << kWARNING << it->first << Endl;
1107 std::ostringstream oss;
1108 (it->second)->Print(oss);
1113 OptimizeConfigParameters optimize(
this, tuneParameters, fomType, fitType);
1114 tunedParameters=optimize.optimize();
1116 return tunedParameters;
1123 void TMVA::MethodBDT::SetTuneParameters(std::map<TString,Double_t> tuneParameters)
1125 std::map<TString,Double_t>::iterator it;
1126 for(it=tuneParameters.begin(); it!= tuneParameters.end(); ++it){
1127 Log() << kWARNING << it->first <<
" = " << it->second << Endl;
1128 if (it->first ==
"MaxDepth" ) SetMaxDepth ((Int_t)it->second);
1129 else if (it->first ==
"MinNodeSize" ) SetMinNodeSize (it->second);
1130 else if (it->first ==
"NTrees" ) SetNTrees ((Int_t)it->second);
1131 else if (it->first ==
"NodePurityLimit") SetNodePurityLimit (it->second);
1132 else if (it->first ==
"AdaBoostBeta" ) SetAdaBoostBeta (it->second);
1133 else if (it->first ==
"Shrinkage" ) SetShrinkage (it->second);
1134 else if (it->first ==
"UseNvars" ) SetUseNvars ((Int_t)it->second);
1135 else if (it->first ==
"BaggedSampleFraction" ) SetBaggedSampleFraction (it->second);
1136 else Log() << kFATAL <<
" SetParameter for " << it->first <<
" not yet implemented " <<Endl;
1144 void TMVA::MethodBDT::Train()
1146 TMVA::DecisionTreeNode::fgIsTraining=
true;
1154 Log() << kERROR <<
" Zero Decision Trees demanded... that does not work !! "
1155 <<
" I set it to 1 .. just so that the program does not crash"
1160 if (fInteractive && fInteractive->NotInitialized()){
1161 std::vector<TString> titles = {
"Boost weight",
"Error Fraction"};
1162 fInteractive->Init(titles);
1164 fIPyMaxIter = fNTrees;
1165 fExitFromTraining =
false;
1169 if (IsNormalised()) Log() << kFATAL <<
"\"Normalise\" option cannot be used with BDT; "
1170 <<
"please remove the option from the configuration string, or "
1171 <<
"use \"!Normalise\""
1175 Log() << kINFO <<
"Regression Loss Function: "<< fRegressionLossFunctionBDTG->Name() << Endl;
1177 Log() << kINFO <<
"Training "<< fNTrees <<
" Decision Trees ... patience please" << Endl;
1179 Log() << kDEBUG <<
"Training with maximal depth = " <<fMaxDepth
1180 <<
", MinNodeEvents=" << fMinNodeEvents
1181 <<
", NTrees="<<fNTrees
1182 <<
", NodePurityLimit="<<fNodePurityLimit
1183 <<
", AdaBoostBeta="<<fAdaBoostBeta
1189 TString hname =
"AdaBooost weight distribution";
1195 if (DoRegression()) {
1199 hname=
"Boost event weights distribution";
1204 TH1* h =
new TH1F(Form(
"%s_BoostWeight",DataInfo().GetName()),hname,nBins,xMin,xMax);
1205 TH1* nodesBeforePruningVsTree =
new TH1I(Form(
"%s_NodesBeforePruning",DataInfo().GetName()),
"nodes before pruning",fNTrees,0,fNTrees);
1206 TH1* nodesAfterPruningVsTree =
new TH1I(Form(
"%s_NodesAfterPruning",DataInfo().GetName()),
"nodes after pruning",fNTrees,0,fNTrees);
1210 if(!DoMulticlass()){
1211 Results* results = Data()->GetResults(GetMethodName(), Types::kTraining, GetAnalysisType());
1213 h->SetXTitle(
"boost weight");
1214 results->Store(h,
"BoostWeights");
1218 if (fDoBoostMonitor){
1219 TH2* boostMonitor =
new TH2F(
"BoostMonitor",
"ROC Integral Vs iTree",2,0,fNTrees,2,0,1.05);
1220 boostMonitor->SetXTitle(
"#tree");
1221 boostMonitor->SetYTitle(
"ROC Integral");
1222 results->Store(boostMonitor,
"BoostMonitor");
1223 TGraph *boostMonitorGraph =
new TGraph();
1224 boostMonitorGraph->SetName(
"BoostMonitorGraph");
1225 boostMonitorGraph->SetTitle(
"ROCIntegralVsNTrees");
1226 results->Store(boostMonitorGraph,
"BoostMonitorGraph");
1230 h =
new TH1F(
"BoostWeightVsTree",
"Boost weights vs tree",fNTrees,0,fNTrees);
1231 h->SetXTitle(
"#tree");
1232 h->SetYTitle(
"boost weight");
1233 results->Store(h,
"BoostWeightsVsTree");
1236 h =
new TH1F(
"ErrFractHist",
"error fraction vs tree number",fNTrees,0,fNTrees);
1237 h->SetXTitle(
"#tree");
1238 h->SetYTitle(
"error fraction");
1239 results->Store(h,
"ErrorFrac");
1242 nodesBeforePruningVsTree->SetXTitle(
"#tree");
1243 nodesBeforePruningVsTree->SetYTitle(
"#tree nodes");
1244 results->Store(nodesBeforePruningVsTree);
1247 nodesAfterPruningVsTree->SetXTitle(
"#tree");
1248 nodesAfterPruningVsTree->SetYTitle(
"#tree nodes");
1249 results->Store(nodesAfterPruningVsTree);
1253 fMonitorNtuple=
new TTree(
"MonitorNtuple",
"BDT variables");
1254 fMonitorNtuple->Branch(
"iTree",&fITree,
"iTree/I");
1255 fMonitorNtuple->Branch(
"boostWeight",&fBoostWeight,
"boostWeight/D");
1256 fMonitorNtuple->Branch(
"errorFraction",&fErrorFraction,
"errorFraction/D");
1258 Timer timer( fNTrees, GetName() );
1259 Int_t nNodesBeforePruningCount = 0;
1260 Int_t nNodesAfterPruningCount = 0;
1262 Int_t nNodesBeforePruning = 0;
1263 Int_t nNodesAfterPruning = 0;
1265 if(fBoostType==
"Grad"){
1266 InitGradBoost(fEventSample);
1270 Bool_t continueBoost=kTRUE;
1273 while (itree < fNTrees && continueBoost){
1274 if (fExitFromTraining)
break;
1275 fIPyCurrentIter = itree;
1276 timer.DrawProgressBar( itree );
1288 if (fBoostType!=
"Grad"){
1289 Log() << kFATAL <<
"Multiclass is currently only supported by gradient boost. "
1290 <<
"Please change boost option accordingly (BoostType=Grad)." << Endl;
1293 UInt_t nClasses = DataInfo().GetNClasses();
1294 for (UInt_t i=0;i<nClasses;i++){
1298 fForest.push_back(
new DecisionTree( fSepType, fMinNodeSize, fNCuts, &(DataInfo()), i,
1299 fRandomisedTrees, fUseNvars, fUsePoissonNvars, fMaxDepth,
1300 itree*nClasses+i, fNodePurityLimit, itree*nClasses+1));
1301 fForest.back()->SetNVars(GetNvar());
1302 if (fUseFisherCuts) {
1303 fForest.back()->SetUseFisherCuts();
1304 fForest.back()->SetMinLinCorrForFisher(fMinLinCorrForFisher);
1305 fForest.back()->SetUseExclusiveVars(fUseExclusiveVars);
1309 nNodesBeforePruning = fForest.back()->BuildTree(*fTrainSample);
1310 Double_t bw = this->Boost(*fTrainSample, fForest.back(),i);
1312 fBoostWeights.push_back(bw);
1314 fBoostWeights.push_back(0);
1315 Log() << kWARNING <<
"stopped boosting at itree="<<itree << Endl;
1317 continueBoost=kFALSE;
1323 DecisionTree* dt =
new DecisionTree( fSepType, fMinNodeSize, fNCuts, &(DataInfo()), fSignalClass,
1324 fRandomisedTrees, fUseNvars, fUsePoissonNvars, fMaxDepth,
1325 itree, fNodePurityLimit, itree);
1327 fForest.push_back(dt);
1328 fForest.back()->SetNVars(GetNvar());
1329 if (fUseFisherCuts) {
1330 fForest.back()->SetUseFisherCuts();
1331 fForest.back()->SetMinLinCorrForFisher(fMinLinCorrForFisher);
1332 fForest.back()->SetUseExclusiveVars(fUseExclusiveVars);
1335 nNodesBeforePruning = fForest.back()->BuildTree(*fTrainSample);
1337 if (fUseYesNoLeaf && !DoRegression() && fBoostType!=
"Grad") {
1338 nNodesBeforePruning = fForest.back()->CleanTree();
1341 nNodesBeforePruningCount += nNodesBeforePruning;
1342 nodesBeforePruningVsTree->SetBinContent(itree+1,nNodesBeforePruning);
1344 fForest.back()->SetPruneMethod(fPruneMethod);
1345 fForest.back()->SetPruneStrength(fPruneStrength);
1347 std::vector<const Event*> * validationSample = NULL;
1348 if(fAutomatic) validationSample = &fValidationSample;
1349 Double_t bw = this->Boost(*fTrainSample, fForest.back());
1351 fBoostWeights.push_back(bw);
1353 fBoostWeights.push_back(0);
1354 Log() << kWARNING <<
"stopped boosting at itree="<<itree << Endl;
1355 continueBoost=kFALSE;
1361 if (fPruneMethod != DecisionTree::kNoPruning) fForest.back()->PruneTree(validationSample);
1363 if (fUseYesNoLeaf && !DoRegression() && fBoostType!=
"Grad"){
1364 fForest.back()->CleanTree();
1366 nNodesAfterPruning = fForest.back()->GetNNodes();
1367 nNodesAfterPruningCount += nNodesAfterPruning;
1368 nodesAfterPruningVsTree->SetBinContent(itree+1,nNodesAfterPruning);
1371 fInteractive->AddPoint(itree, fBoostWeight, fErrorFraction);
1374 fMonitorNtuple->Fill();
1375 if (fDoBoostMonitor){
1376 if (! DoRegression() ){
1377 if ( itree==fNTrees-1 || (!(itree%500)) ||
1378 (!(itree%250) && itree <1000)||
1379 (!(itree%100) && itree < 500)||
1380 (!(itree%50) && itree < 250)||
1381 (!(itree%25) && itree < 150)||
1382 (!(itree%10) && itree < 50)||
1383 (!(itree%5) && itree < 20)
1384 ) BoostMonitor(itree);
1392 Log() << kDEBUG <<
"\t<Train> elapsed time: " << timer.GetElapsedTime()
1394 if (fPruneMethod == DecisionTree::kNoPruning) {
1395 Log() << kDEBUG <<
"\t<Train> average number of nodes (w/o pruning) : "
1396 << nNodesBeforePruningCount/GetNTrees() << Endl;
1399 Log() << kDEBUG <<
"\t<Train> average number of nodes before/after pruning : "
1400 << nNodesBeforePruningCount/GetNTrees() <<
" / "
1401 << nNodesAfterPruningCount/GetNTrees()
1404 TMVA::DecisionTreeNode::fgIsTraining=
false;
1409 Log() << kDEBUG <<
"Now I delete the privat data sample"<< Endl;
1410 for (UInt_t i=0; i<fEventSample.size(); i++)
delete fEventSample[i];
1411 for (UInt_t i=0; i<fValidationSample.size(); i++)
delete fValidationSample[i];
1412 fEventSample.clear();
1413 fValidationSample.clear();
1415 if (!fExitFromTraining) fIPyMaxIter = fIPyCurrentIter;
1423 Double_t TMVA::MethodBDT::GetGradBoostMVA(
const TMVA::Event* e, UInt_t nTrees)
1426 for (UInt_t itree=0; itree<nTrees; itree++) {
1428 sum += fForest[itree]->CheckEvent(e,kFALSE);
1431 return 2.0/(1.0+exp(-2.0*sum))-1;
1437 void TMVA::MethodBDT::UpdateTargets(std::vector<const TMVA::Event*>& eventSample, UInt_t cls)
1439 if (DoMulticlass()) {
1440 UInt_t nClasses = DataInfo().GetNClasses();
1441 Bool_t isLastClass = (cls == nClasses - 1);
1453 std::map<const TMVA::Event *, std::vector<double>> & residuals = this->fResiduals;
1454 DecisionTree & lastTree = *(this->fForest.back());
1456 auto update_residuals = [&residuals, &lastTree, cls](
const TMVA::Event * e) {
1457 residuals[e].at(cls) += lastTree.CheckEvent(e, kFALSE);
1460 auto update_residuals_last = [&residuals, &lastTree, cls, nClasses](
const TMVA::Event * e) {
1461 residuals[e].at(cls) += lastTree.CheckEvent(e, kFALSE);
1463 auto &residualsThisEvent = residuals[e];
1465 std::vector<Double_t> expCache(nClasses, 0.0);
1466 std::transform(residualsThisEvent.begin(),
1467 residualsThisEvent.begin() + nClasses,
1468 expCache.begin(), [](Double_t d) {
return exp(d); });
1470 Double_t exp_sum = std::accumulate(expCache.begin(),
1471 expCache.begin() + nClasses,
1474 for (UInt_t i = 0; i < nClasses; i++) {
1475 Double_t p_cls = expCache[i] / exp_sum;
1477 Double_t res = (e->GetClass() == i) ? (1.0 - p_cls) : (-p_cls);
1478 const_cast<TMVA::Event *
>(e)->SetTarget(i, res);
1483 TMVA::Config::Instance().GetThreadExecutor()
1484 .Foreach(update_residuals_last, eventSample);
1486 TMVA::Config::Instance().GetThreadExecutor()
1487 .Foreach(update_residuals, eventSample);
1493 std::vector<Double_t> expCache;
1495 expCache.resize(nClasses);
1498 for (
auto e : eventSample) {
1499 fResiduals[e].at(cls) += fForest.back()->CheckEvent(e, kFALSE);
1501 auto &residualsThisEvent = fResiduals[e];
1502 std::transform(residualsThisEvent.begin(),
1503 residualsThisEvent.begin() + nClasses,
1504 expCache.begin(), [](Double_t d) {
return exp(d); });
1506 Double_t exp_sum = std::accumulate(expCache.begin(),
1507 expCache.begin() + nClasses,
1510 for (UInt_t i = 0; i < nClasses; i++) {
1511 Double_t p_cls = expCache[i] / exp_sum;
1513 Double_t res = (e->GetClass() == i) ? (1.0 - p_cls) : (-p_cls);
1514 const_cast<TMVA::Event *
>(e)->SetTarget(i, res);
1520 std::map<const TMVA::Event *, std::vector<double>> & residuals = this->fResiduals;
1521 DecisionTree & lastTree = *(this->fForest.back());
1523 UInt_t signalClass = DataInfo().GetSignalClassIndex();
1526 auto update_residuals = [&residuals, &lastTree, signalClass](
const TMVA::Event * e) {
1527 double & residualAt0 = residuals[e].at(0);
1528 residualAt0 += lastTree.CheckEvent(e, kFALSE);
1530 Double_t p_sig = 1.0 / (1.0 + exp(-2.0 * residualAt0));
1531 Double_t res = ((e->GetClass() == signalClass) ? (1.0 - p_sig) : (-p_sig));
1533 const_cast<TMVA::Event *
>(e)->SetTarget(0, res);
1536 TMVA::Config::Instance().GetThreadExecutor()
1537 .Foreach(update_residuals, eventSample);
1539 for (
auto e : eventSample) {
1540 double & residualAt0 = residuals[e].at(0);
1541 residualAt0 += lastTree.CheckEvent(e, kFALSE);
1543 Double_t p_sig = 1.0 / (1.0 + exp(-2.0 * residualAt0));
1544 Double_t res = ((e->GetClass() == signalClass) ? (1.0 - p_sig) : (-p_sig));
1546 const_cast<TMVA::Event *
>(e)->SetTarget(0, res);
1559 void TMVA::MethodBDT::UpdateTargetsRegression(std::vector<const TMVA::Event*>& eventSample, Bool_t first)
1563 UInt_t nPartitions = TMVA::Config::Instance().GetThreadExecutor().GetPoolSize();
1564 auto seeds = ROOT::TSeqU(nPartitions);
1567 auto f = [
this, &nPartitions](UInt_t partition = 0) -> Int_t {
1568 Int_t start = 1.0 * partition / nPartitions * this->fEventSample.size();
1569 Int_t end = (partition + 1.0) / nPartitions * this->fEventSample.size();
1571 for (Int_t i = start; i < end; ++i) {
1572 const TMVA::Event *e = fEventSample[i];
1573 LossFunctionEventInfo & lossInfo = fLossFunctionEventInfo.at(e);
1574 lossInfo.predictedValue += fForest.back()->CheckEvent(e, kFALSE);
1580 TMVA::Config::Instance().GetThreadExecutor().Map(f, seeds);
1582 for (
const TMVA::Event *e : fEventSample) {
1583 LossFunctionEventInfo & lossInfo = fLossFunctionEventInfo.at(e);
1584 lossInfo.predictedValue += fForest.back()->CheckEvent(e, kFALSE);
1590 fRegressionLossFunctionBDTG->SetTargets(eventSample, fLossFunctionEventInfo);
1597 Double_t TMVA::MethodBDT::GradBoost(std::vector<const TMVA::Event*>& eventSample, DecisionTree *dt, UInt_t cls)
1600 Double_t sumWeightTarget = 0;
1604 std::unordered_map<TMVA::DecisionTreeNode*, LeafInfo> leaves;
1605 for (
auto e : eventSample) {
1606 Double_t weight = e->GetWeight();
1607 TMVA::DecisionTreeNode* node = dt->GetEventNode(*e);
1608 auto &v = leaves[node];
1609 auto target = e->GetTarget(cls);
1610 v.sumWeightTarget += target * weight;
1611 v.sum2 += fabs(target) * (1.0 - fabs(target)) * weight;
1613 for (
auto &iLeave : leaves) {
1614 constexpr
auto minValue = 1e-30;
1615 if (iLeave.second.sum2 < minValue) {
1616 iLeave.second.sum2 = minValue;
1618 const Double_t K = DataInfo().GetNClasses();
1619 iLeave.first->SetResponse(fShrinkage * (K - 1) / K * iLeave.second.sumWeightTarget / iLeave.second.sum2);
1624 DoMulticlass() ? UpdateTargets(fEventSample, cls) : UpdateTargets(fEventSample);
1631 Double_t TMVA::MethodBDT::GradBoostRegression(std::vector<const TMVA::Event*>& eventSample, DecisionTree *dt )
1636 std::map<TMVA::DecisionTreeNode*,vector< TMVA::LossFunctionEventInfo > > leaves;
1637 for (std::vector<const TMVA::Event*>::const_iterator e=eventSample.begin(); e!=eventSample.end();++e) {
1638 TMVA::DecisionTreeNode* node = dt->GetEventNode(*(*e));
1639 (leaves[node]).push_back(fLossFunctionEventInfo[*e]);
1646 for (std::map<TMVA::DecisionTreeNode*,vector< TMVA::LossFunctionEventInfo > >::iterator iLeave=leaves.begin();
1647 iLeave!=leaves.end();++iLeave){
1648 Double_t fit = fRegressionLossFunctionBDTG->Fit(iLeave->second);
1649 (iLeave->first)->SetResponse(fShrinkage*fit);
1652 UpdateTargetsRegression(*fTrainSample);
1660 void TMVA::MethodBDT::InitGradBoost( std::vector<const TMVA::Event*>& eventSample)
1667 for (std::vector<const TMVA::Event*>::const_iterator e=eventSample.begin(); e!=eventSample.end();++e) {
1668 fLossFunctionEventInfo[*e]= TMVA::LossFunctionEventInfo((*e)->GetTarget(0), 0, (*e)->GetWeight());
1671 fRegressionLossFunctionBDTG->Init(fLossFunctionEventInfo, fBoostWeights);
1672 UpdateTargetsRegression(*fTrainSample,kTRUE);
1676 else if(DoMulticlass()){
1677 UInt_t nClasses = DataInfo().GetNClasses();
1678 for (std::vector<const TMVA::Event*>::const_iterator e=eventSample.begin(); e!=eventSample.end();++e) {
1679 for (UInt_t i=0;i<nClasses;i++){
1681 Double_t r = (*e)->GetClass()==i?(1-1.0/nClasses):(-1.0/nClasses);
1682 const_cast<TMVA::Event*
>(*e)->SetTarget(i,r);
1683 fResiduals[*e].push_back(0);
1688 for (std::vector<const TMVA::Event*>::const_iterator e=eventSample.begin(); e!=eventSample.end();++e) {
1689 Double_t r = (DataInfo().IsSignal(*e)?1:0)-0.5;
1690 const_cast<TMVA::Event*
>(*e)->SetTarget(0,r);
1691 fResiduals[*e].push_back(0);
1699 Double_t TMVA::MethodBDT::TestTreeQuality( DecisionTree *dt )
1701 Double_t ncorrect=0, nfalse=0;
1702 for (UInt_t ievt=0; ievt<fValidationSample.size(); ievt++) {
1703 Bool_t isSignalType= (dt->CheckEvent(fValidationSample[ievt]) > fNodePurityLimit ) ? 1 : 0;
1705 if (isSignalType == (DataInfo().IsSignal(fValidationSample[ievt])) ) {
1706 ncorrect += fValidationSample[ievt]->GetWeight();
1709 nfalse += fValidationSample[ievt]->GetWeight();
1713 return ncorrect / (ncorrect + nfalse);
1720 Double_t TMVA::MethodBDT::Boost( std::vector<const TMVA::Event*>& eventSample, DecisionTree *dt, UInt_t cls )
1722 Double_t returnVal=-1;
1724 if (fBoostType==
"AdaBoost") returnVal = this->AdaBoost (eventSample, dt);
1725 else if (fBoostType==
"AdaCost") returnVal = this->AdaCost (eventSample, dt);
1726 else if (fBoostType==
"Bagging") returnVal = this->Bagging ( );
1727 else if (fBoostType==
"RegBoost") returnVal = this->RegBoost (eventSample, dt);
1728 else if (fBoostType==
"AdaBoostR2") returnVal = this->AdaBoostR2(eventSample, dt);
1729 else if (fBoostType==
"Grad"){
1731 returnVal = this->GradBoostRegression(eventSample, dt);
1732 else if(DoMulticlass())
1733 returnVal = this->GradBoost (eventSample, dt, cls);
1735 returnVal = this->GradBoost (eventSample, dt);
1738 Log() << kINFO << GetOptions() << Endl;
1739 Log() << kFATAL <<
"<Boost> unknown boost option " << fBoostType<<
" called" << Endl;
1743 GetBaggedSubSample(fEventSample);
1754 void TMVA::MethodBDT::BoostMonitor(Int_t iTree)
1756 Results* results = Data()->GetResults(GetMethodName(),Types::kTraining, Types::kMaxAnalysisType);
1758 TH1F *tmpS =
new TH1F(
"tmpS",
"", 100 , -1., 1.00001 );
1759 TH1F *tmpB =
new TH1F(
"tmpB",
"", 100 , -1., 1.00001 );
1763 UInt_t signalClassNr = DataInfo().GetClassInfo(
"Signal")->GetNumber();
1773 UInt_t nevents = Data()->GetNTestEvents();
1774 for (UInt_t iev=0; iev < nevents; iev++){
1775 const Event*
event = GetTestingEvent(iev);
1777 if (event->GetClass() == signalClassNr) {tmp=tmpS;}
1779 tmp->Fill(PrivateGetMvaValue(event),event->GetWeight());
1783 std::vector<TH1F*> hS;
1784 std::vector<TH1F*> hB;
1785 for (UInt_t ivar=0; ivar<GetNvar(); ivar++){
1786 hS.push_back(
new TH1F(Form(
"SigVar%dAtTree%d",ivar,iTree),Form(
"SigVar%dAtTree%d",ivar,iTree),100,DataInfo().GetVariableInfo(ivar).GetMin(),DataInfo().GetVariableInfo(ivar).GetMax()));
1787 hB.push_back(
new TH1F(Form(
"BkgVar%dAtTree%d",ivar,iTree),Form(
"BkgVar%dAtTree%d",ivar,iTree),100,DataInfo().GetVariableInfo(ivar).GetMin(),DataInfo().GetVariableInfo(ivar).GetMax()));
1788 results->Store(hS.back(),hS.back()->GetTitle());
1789 results->Store(hB.back(),hB.back()->GetTitle());
1793 for (UInt_t iev=0; iev < fEventSample.size(); iev++){
1794 if (fEventSample[iev]->GetBoostWeight() > max) max = 1.01*fEventSample[iev]->GetBoostWeight();
1796 TH1F *tmpBoostWeightsS =
new TH1F(Form(
"BoostWeightsInTreeS%d",iTree),Form(
"BoostWeightsInTreeS%d",iTree),100,0.,max);
1797 TH1F *tmpBoostWeightsB =
new TH1F(Form(
"BoostWeightsInTreeB%d",iTree),Form(
"BoostWeightsInTreeB%d",iTree),100,0.,max);
1798 results->Store(tmpBoostWeightsS,tmpBoostWeightsS->GetTitle());
1799 results->Store(tmpBoostWeightsB,tmpBoostWeightsB->GetTitle());
1801 TH1F *tmpBoostWeights;
1802 std::vector<TH1F*> *h;
1804 for (UInt_t iev=0; iev < fEventSample.size(); iev++){
1805 if (fEventSample[iev]->GetClass() == signalClassNr) {
1806 tmpBoostWeights=tmpBoostWeightsS;
1809 tmpBoostWeights=tmpBoostWeightsB;
1812 tmpBoostWeights->Fill(fEventSample[iev]->GetBoostWeight());
1813 for (UInt_t ivar=0; ivar<GetNvar(); ivar++){
1814 (*h)[ivar]->Fill(fEventSample[iev]->GetValue(ivar),fEventSample[iev]->GetWeight());
1819 TMVA::PDF *sig =
new TMVA::PDF(
" PDF Sig", tmpS, TMVA::PDF::kSpline3 );
1820 TMVA::PDF *bkg =
new TMVA::PDF(
" PDF Bkg", tmpB, TMVA::PDF::kSpline3 );
1823 TGraph* gr=results->GetGraph(
"BoostMonitorGraph");
1824 Int_t nPoints = gr->GetN();
1826 gr->SetPoint(nPoints,(Double_t)iTree+1,GetROCIntegral(sig,bkg));
1848 Double_t TMVA::MethodBDT::AdaBoost( std::vector<const TMVA::Event*>& eventSample, DecisionTree *dt )
1850 Double_t err=0, sumGlobalw=0, sumGlobalwfalse=0, sumGlobalwfalse2=0;
1852 std::vector<Double_t> sumw(DataInfo().GetNClasses(),0);
1855 for (std::vector<const TMVA::Event*>::const_iterator e=eventSample.begin(); e!=eventSample.end();++e) {
1856 Double_t w = (*e)->GetWeight();
1858 UInt_t iclass=(*e)->GetClass();
1861 if ( DoRegression() ) {
1862 Double_t tmpDev = TMath::Abs(dt->CheckEvent(*e,kFALSE) - (*e)->GetTarget(0) );
1863 sumGlobalwfalse += w * tmpDev;
1864 sumGlobalwfalse2 += w * tmpDev*tmpDev;
1865 if (tmpDev > maxDev) maxDev = tmpDev;
1869 Bool_t isSignalType = (dt->CheckEvent(*e,fUseYesNoLeaf) > fNodePurityLimit );
1870 if (!(isSignalType == DataInfo().IsSignal(*e))) {
1871 sumGlobalwfalse+= w;
1874 Double_t dtoutput = (dt->CheckEvent(*e,fUseYesNoLeaf) - 0.5)*2.;
1876 if (DataInfo().IsSignal(*e)) trueType = 1;
1878 sumGlobalwfalse+= w*trueType*dtoutput;
1883 err = sumGlobalwfalse/sumGlobalw ;
1884 if ( DoRegression() ) {
1886 if (fAdaBoostR2Loss==
"linear"){
1887 err = sumGlobalwfalse/maxDev/sumGlobalw ;
1889 else if (fAdaBoostR2Loss==
"quadratic"){
1890 err = sumGlobalwfalse2/maxDev/maxDev/sumGlobalw ;
1892 else if (fAdaBoostR2Loss==
"exponential"){
1894 for (std::vector<const TMVA::Event*>::const_iterator e=eventSample.begin(); e!=eventSample.end();++e) {
1895 Double_t w = (*e)->GetWeight();
1896 Double_t tmpDev = TMath::Abs(dt->CheckEvent(*e,kFALSE) - (*e)->GetTarget(0) );
1897 err += w * (1 - exp (-tmpDev/maxDev)) / sumGlobalw;
1902 Log() << kFATAL <<
" you've chosen a Loss type for Adaboost other than linear, quadratic or exponential "
1903 <<
" namely " << fAdaBoostR2Loss <<
"\n"
1904 <<
"and this is not implemented... a typo in the options ??" <<Endl;
1908 Log() << kDEBUG <<
"BDT AdaBoos wrong/all: " << sumGlobalwfalse <<
"/" << sumGlobalw << Endl;
1911 Double_t newSumGlobalw=0;
1912 std::vector<Double_t> newSumw(sumw.size(),0);
1914 Double_t boostWeight=1.;
1915 if (err >= 0.5 && fUseYesNoLeaf) {
1918 if (dt->GetNNodes() == 1){
1919 Log() << kERROR <<
" YOUR tree has only 1 Node... kind of a funny *tree*. I cannot "
1920 <<
"boost such a thing... if after 1 step the error rate is == 0.5"
1922 <<
"please check why this happens, maybe too many events per node requested ?"
1926 Log() << kERROR <<
" The error rate in the BDT boosting is > 0.5. ("<< err
1927 <<
") That should not happen, please check your code (i.e... the BDT code), I "
1928 <<
" stop boosting here" << Endl;
1932 }
else if (err < 0) {
1933 Log() << kERROR <<
" The error rate in the BDT boosting is < 0. That can happen"
1934 <<
" due to improper treatment of negative weights in a Monte Carlo.. (if you have"
1935 <<
" an idea on how to do it in a better way, please let me know (Helge.Voss@cern.ch)"
1936 <<
" for the time being I set it to its absolute value.. just to continue.." << Endl;
1937 err = TMath::Abs(err);
1940 boostWeight = TMath::Log((1.-err)/err)*fAdaBoostBeta;
1942 boostWeight = TMath::Log((1.+err)/(1-err))*fAdaBoostBeta;
1945 Log() << kDEBUG <<
"BDT AdaBoos wrong/all: " << sumGlobalwfalse <<
"/" << sumGlobalw <<
" 1-err/err="<<boostWeight<<
" log.."<<TMath::Log(boostWeight)<<Endl;
1947 Results* results = Data()->GetResults(GetMethodName(),Types::kTraining, Types::kMaxAnalysisType);
1950 for (std::vector<const TMVA::Event*>::const_iterator e=eventSample.begin(); e!=eventSample.end();++e) {
1952 if (fUseYesNoLeaf||DoRegression()){
1953 if ((!( (dt->CheckEvent(*e,fUseYesNoLeaf) > fNodePurityLimit ) == DataInfo().IsSignal(*e))) || DoRegression()) {
1954 Double_t boostfactor = TMath::Exp(boostWeight);
1956 if (DoRegression()) boostfactor = TMath::Power(1/boostWeight,(1.-TMath::Abs(dt->CheckEvent(*e,kFALSE) - (*e)->GetTarget(0) )/maxDev ) );
1957 if ( (*e)->GetWeight() > 0 ){
1958 (*e)->SetBoostWeight( (*e)->GetBoostWeight() * boostfactor);
1960 if (DoRegression()) results->GetHist(
"BoostWeights")->Fill(boostfactor);
1962 if ( fInverseBoostNegWeights )(*e)->ScaleBoostWeight( 1. / boostfactor);
1963 else (*e)->SetBoostWeight( (*e)->GetBoostWeight() * boostfactor);
1969 Double_t dtoutput = (dt->CheckEvent(*e,fUseYesNoLeaf) - 0.5)*2.;
1971 if (DataInfo().IsSignal(*e)) trueType = 1;
1973 Double_t boostfactor = TMath::Exp(-1*boostWeight*trueType*dtoutput);
1975 if ( (*e)->GetWeight() > 0 ){
1976 (*e)->SetBoostWeight( (*e)->GetBoostWeight() * boostfactor);
1978 if (DoRegression()) results->GetHist(
"BoostWeights")->Fill(boostfactor);
1980 if ( fInverseBoostNegWeights )(*e)->ScaleBoostWeight( 1. / boostfactor);
1981 else (*e)->SetBoostWeight( (*e)->GetBoostWeight() * boostfactor);
1984 newSumGlobalw+=(*e)->GetWeight();
1985 newSumw[(*e)->GetClass()] += (*e)->GetWeight();
1990 Double_t globalNormWeight=( (Double_t) eventSample.size())/newSumGlobalw;
1991 Log() << kDEBUG <<
"new Nsig="<<newSumw[0]*globalNormWeight <<
" new Nbkg="<<newSumw[1]*globalNormWeight << Endl;
1994 for (std::vector<const TMVA::Event*>::const_iterator e=eventSample.begin(); e!=eventSample.end();++e) {
1998 if (DataInfo().IsSignal(*e))(*e)->ScaleBoostWeight( globalNormWeight * fSigToBkgFraction );
1999 else (*e)->ScaleBoostWeight( globalNormWeight );
2002 if (!(DoRegression()))results->GetHist(
"BoostWeights")->Fill(boostWeight);
2003 results->GetHist(
"BoostWeightsVsTree")->SetBinContent(fForest.size(),boostWeight);
2004 results->GetHist(
"ErrorFrac")->SetBinContent(fForest.size(),err);
2006 fBoostWeight = boostWeight;
2007 fErrorFraction = err;
2026 Double_t TMVA::MethodBDT::AdaCost( vector<const TMVA::Event*>& eventSample, DecisionTree *dt )
2028 Double_t Css = fCss;
2029 Double_t Cbb = fCbb;
2030 Double_t Cts_sb = fCts_sb;
2031 Double_t Ctb_ss = fCtb_ss;
2033 Double_t err=0, sumGlobalWeights=0, sumGlobalCost=0;
2035 std::vector<Double_t> sumw(DataInfo().GetNClasses(),0);
2037 for (vector<const TMVA::Event*>::const_iterator e=eventSample.begin(); e!=eventSample.end();++e) {
2038 Double_t w = (*e)->GetWeight();
2039 sumGlobalWeights += w;
2040 UInt_t iclass=(*e)->GetClass();
2044 if ( DoRegression() ) {
2045 Log() << kFATAL <<
" AdaCost not implemented for regression"<<Endl;
2048 Double_t dtoutput = (dt->CheckEvent(*e,
false) - 0.5)*2.;
2050 Bool_t isTrueSignal = DataInfo().IsSignal(*e);
2051 Bool_t isSelectedSignal = (dtoutput>0);
2052 if (isTrueSignal) trueType = 1;
2056 if (isTrueSignal && isSelectedSignal) cost=Css;
2057 else if (isTrueSignal && !isSelectedSignal) cost=Cts_sb;
2058 else if (!isTrueSignal && isSelectedSignal) cost=Ctb_ss;
2059 else if (!isTrueSignal && !isSelectedSignal) cost=Cbb;
2060 else Log() << kERROR <<
"something went wrong in AdaCost" << Endl;
2062 sumGlobalCost+= w*trueType*dtoutput*cost;
2067 if ( DoRegression() ) {
2068 Log() << kFATAL <<
" AdaCost not implemented for regression"<<Endl;
2073 sumGlobalCost /= sumGlobalWeights;
2077 Double_t newSumGlobalWeights=0;
2078 vector<Double_t> newSumClassWeights(sumw.size(),0);
2080 Double_t boostWeight = TMath::Log((1+sumGlobalCost)/(1-sumGlobalCost)) * fAdaBoostBeta;
2082 Results* results = Data()->GetResults(GetMethodName(),Types::kTraining, Types::kMaxAnalysisType);
2084 for (vector<const TMVA::Event*>::const_iterator e=eventSample.begin(); e!=eventSample.end();++e) {
2085 Double_t dtoutput = (dt->CheckEvent(*e,
false) - 0.5)*2.;
2087 Bool_t isTrueSignal = DataInfo().IsSignal(*e);
2088 Bool_t isSelectedSignal = (dtoutput>0);
2089 if (isTrueSignal) trueType = 1;
2093 if (isTrueSignal && isSelectedSignal) cost=Css;
2094 else if (isTrueSignal && !isSelectedSignal) cost=Cts_sb;
2095 else if (!isTrueSignal && isSelectedSignal) cost=Ctb_ss;
2096 else if (!isTrueSignal && !isSelectedSignal) cost=Cbb;
2097 else Log() << kERROR <<
"something went wrong in AdaCost" << Endl;
2099 Double_t boostfactor = TMath::Exp(-1*boostWeight*trueType*dtoutput*cost);
2100 if (DoRegression())Log() << kFATAL <<
" AdaCost not implemented for regression"<<Endl;
2101 if ( (*e)->GetWeight() > 0 ){
2102 (*e)->SetBoostWeight( (*e)->GetBoostWeight() * boostfactor);
2104 if (DoRegression())Log() << kFATAL <<
" AdaCost not implemented for regression"<<Endl;
2106 if ( fInverseBoostNegWeights )(*e)->ScaleBoostWeight( 1. / boostfactor);
2109 newSumGlobalWeights+=(*e)->GetWeight();
2110 newSumClassWeights[(*e)->GetClass()] += (*e)->GetWeight();
2115 Double_t globalNormWeight=Double_t(eventSample.size())/newSumGlobalWeights;
2116 Log() << kDEBUG <<
"new Nsig="<<newSumClassWeights[0]*globalNormWeight <<
" new Nbkg="<<newSumClassWeights[1]*globalNormWeight << Endl;
2119 for (std::vector<const TMVA::Event*>::const_iterator e=eventSample.begin(); e!=eventSample.end();++e) {
2122 if (DataInfo().IsSignal(*e))(*e)->ScaleBoostWeight( globalNormWeight * fSigToBkgFraction );
2123 else (*e)->ScaleBoostWeight( globalNormWeight );
2127 if (!(DoRegression()))results->GetHist(
"BoostWeights")->Fill(boostWeight);
2128 results->GetHist(
"BoostWeightsVsTree")->SetBinContent(fForest.size(),boostWeight);
2129 results->GetHist(
"ErrorFrac")->SetBinContent(fForest.size(),err);
2131 fBoostWeight = boostWeight;
2132 fErrorFraction = err;
2142 Double_t TMVA::MethodBDT::Bagging( )
2153 void TMVA::MethodBDT::GetBaggedSubSample(std::vector<const TMVA::Event*>& eventSample)
2157 TRandom3 *trandom =
new TRandom3(100*fForest.size()+1234);
2159 if (!fSubSample.empty()) fSubSample.clear();
2161 for (std::vector<const TMVA::Event*>::const_iterator e=eventSample.begin(); e!=eventSample.end();++e) {
2162 n = trandom->PoissonD(fBaggedSampleFraction);
2163 for (Int_t i=0;i<n;i++) fSubSample.push_back(*e);
2187 Double_t TMVA::MethodBDT::RegBoost( std::vector<const TMVA::Event*>& , DecisionTree* )
2195 Double_t TMVA::MethodBDT::AdaBoostR2( std::vector<const TMVA::Event*>& eventSample, DecisionTree *dt )
2197 if ( !DoRegression() ) Log() << kFATAL <<
"Somehow you chose a regression boost method for a classification job" << Endl;
2199 Double_t err=0, sumw=0, sumwfalse=0, sumwfalse2=0;
2201 for (std::vector<const TMVA::Event*>::const_iterator e=eventSample.begin(); e!=eventSample.end();++e) {
2202 Double_t w = (*e)->GetWeight();
2205 Double_t tmpDev = TMath::Abs(dt->CheckEvent(*e,kFALSE) - (*e)->GetTarget(0) );
2206 sumwfalse += w * tmpDev;
2207 sumwfalse2 += w * tmpDev*tmpDev;
2208 if (tmpDev > maxDev) maxDev = tmpDev;
2212 if (fAdaBoostR2Loss==
"linear"){
2213 err = sumwfalse/maxDev/sumw ;
2215 else if (fAdaBoostR2Loss==
"quadratic"){
2216 err = sumwfalse2/maxDev/maxDev/sumw ;
2218 else if (fAdaBoostR2Loss==
"exponential"){
2220 for (std::vector<const TMVA::Event*>::const_iterator e=eventSample.begin(); e!=eventSample.end();++e) {
2221 Double_t w = (*e)->GetWeight();
2222 Double_t tmpDev = TMath::Abs(dt->CheckEvent(*e,kFALSE) - (*e)->GetTarget(0) );
2223 err += w * (1 - exp (-tmpDev/maxDev)) / sumw;
2228 Log() << kFATAL <<
" you've chosen a Loss type for Adaboost other than linear, quadratic or exponential "
2229 <<
" namely " << fAdaBoostR2Loss <<
"\n"
2230 <<
"and this is not implemented... a typo in the options ??" <<Endl;
2237 if (dt->GetNNodes() == 1){
2238 Log() << kERROR <<
" YOUR tree has only 1 Node... kind of a funny *tree*. I cannot "
2239 <<
"boost such a thing... if after 1 step the error rate is == 0.5"
2241 <<
"please check why this happens, maybe too many events per node requested ?"
2245 Log() << kERROR <<
" The error rate in the BDT boosting is > 0.5. ("<< err
2246 <<
") That should not happen, but is possible for regression trees, and"
2247 <<
" should trigger a stop for the boosting. please check your code (i.e... the BDT code), I "
2248 <<
" stop boosting " << Endl;
2252 }
else if (err < 0) {
2253 Log() << kERROR <<
" The error rate in the BDT boosting is < 0. That can happen"
2254 <<
" due to improper treatment of negative weights in a Monte Carlo.. (if you have"
2255 <<
" an idea on how to do it in a better way, please let me know (Helge.Voss@cern.ch)"
2256 <<
" for the time being I set it to its absolute value.. just to continue.." << Endl;
2257 err = TMath::Abs(err);
2260 Double_t boostWeight = err / (1.-err);
2263 Results* results = Data()->GetResults(GetMethodName(), Types::kTraining, Types::kMaxAnalysisType);
2265 for (std::vector<const TMVA::Event*>::const_iterator e=eventSample.begin(); e!=eventSample.end();++e) {
2266 Double_t boostfactor = TMath::Power(boostWeight,(1.-TMath::Abs(dt->CheckEvent(*e,kFALSE) - (*e)->GetTarget(0) )/maxDev ) );
2267 results->GetHist(
"BoostWeights")->Fill(boostfactor);
2269 if ( (*e)->GetWeight() > 0 ){
2270 Float_t newBoostWeight = (*e)->GetBoostWeight() * boostfactor;
2271 Float_t newWeight = (*e)->GetWeight() * (*e)->GetBoostWeight() * boostfactor;
2272 if (newWeight == 0) {
2273 Log() << kINFO <<
"Weight= " << (*e)->GetWeight() << Endl;
2274 Log() << kINFO <<
"BoostWeight= " << (*e)->GetBoostWeight() << Endl;
2275 Log() << kINFO <<
"boostweight="<<boostWeight <<
" err= " <<err << Endl;
2276 Log() << kINFO <<
"NewBoostWeight= " << newBoostWeight << Endl;
2277 Log() << kINFO <<
"boostfactor= " << boostfactor << Endl;
2278 Log() << kINFO <<
"maxDev = " << maxDev << Endl;
2279 Log() << kINFO <<
"tmpDev = " << TMath::Abs(dt->CheckEvent(*e,kFALSE) - (*e)->GetTarget(0) ) << Endl;
2280 Log() << kINFO <<
"target = " << (*e)->GetTarget(0) << Endl;
2281 Log() << kINFO <<
"estimate = " << dt->CheckEvent(*e,kFALSE) << Endl;
2283 (*e)->SetBoostWeight( newBoostWeight );
2286 (*e)->SetBoostWeight( (*e)->GetBoostWeight() / boostfactor);
2288 newSumw+=(*e)->GetWeight();
2292 Double_t normWeight = sumw / newSumw;
2293 for (std::vector<const TMVA::Event*>::const_iterator e=eventSample.begin(); e!=eventSample.end();++e) {
2296 (*e)->SetBoostWeight( (*e)->GetBoostWeight() * normWeight );
2300 results->GetHist(
"BoostWeightsVsTree")->SetBinContent(fForest.size(),1./boostWeight);
2301 results->GetHist(
"ErrorFrac")->SetBinContent(fForest.size(),err);
2303 fBoostWeight = boostWeight;
2304 fErrorFraction = err;
2306 return TMath::Log(1./boostWeight);
2312 void TMVA::MethodBDT::AddWeightsXMLTo(
void* parent )
const
2314 void* wght = gTools().AddChild(parent,
"Weights");
2316 if (fDoPreselection){
2317 for (UInt_t ivar=0; ivar<GetNvar(); ivar++){
2318 gTools().AddAttr( wght, Form(
"PreselectionLowBkgVar%d",ivar), fIsLowBkgCut[ivar]);
2319 gTools().AddAttr( wght, Form(
"PreselectionLowBkgVar%dValue",ivar), fLowBkgCut[ivar]);
2320 gTools().AddAttr( wght, Form(
"PreselectionLowSigVar%d",ivar), fIsLowSigCut[ivar]);
2321 gTools().AddAttr( wght, Form(
"PreselectionLowSigVar%dValue",ivar), fLowSigCut[ivar]);
2322 gTools().AddAttr( wght, Form(
"PreselectionHighBkgVar%d",ivar), fIsHighBkgCut[ivar]);
2323 gTools().AddAttr( wght, Form(
"PreselectionHighBkgVar%dValue",ivar),fHighBkgCut[ivar]);
2324 gTools().AddAttr( wght, Form(
"PreselectionHighSigVar%d",ivar), fIsHighSigCut[ivar]);
2325 gTools().AddAttr( wght, Form(
"PreselectionHighSigVar%dValue",ivar),fHighSigCut[ivar]);
2330 gTools().AddAttr( wght,
"NTrees", fForest.size() );
2331 gTools().AddAttr( wght,
"AnalysisType", fForest.back()->GetAnalysisType() );
2333 for (UInt_t i=0; i< fForest.size(); i++) {
2334 void* trxml = fForest[i]->AddXMLTo(wght);
2335 gTools().AddAttr( trxml,
"boostWeight", fBoostWeights[i] );
2336 gTools().AddAttr( trxml,
"itree", i );
2343 void TMVA::MethodBDT::ReadWeightsFromXML(
void* parent) {
2345 for (i=0; i<fForest.size(); i++)
delete fForest[i];
2347 fBoostWeights.clear();
2350 UInt_t analysisType;
2351 Float_t boostWeight;
2354 if (gTools().HasAttr( parent, Form(
"PreselectionLowBkgVar%d",0))) {
2355 fIsLowBkgCut.resize(GetNvar());
2356 fLowBkgCut.resize(GetNvar());
2357 fIsLowSigCut.resize(GetNvar());
2358 fLowSigCut.resize(GetNvar());
2359 fIsHighBkgCut.resize(GetNvar());
2360 fHighBkgCut.resize(GetNvar());
2361 fIsHighSigCut.resize(GetNvar());
2362 fHighSigCut.resize(GetNvar());
2366 for (UInt_t ivar=0; ivar<GetNvar(); ivar++){
2367 gTools().ReadAttr( parent, Form(
"PreselectionLowBkgVar%d",ivar), tmpBool);
2368 fIsLowBkgCut[ivar]=tmpBool;
2369 gTools().ReadAttr( parent, Form(
"PreselectionLowBkgVar%dValue",ivar), tmpDouble);
2370 fLowBkgCut[ivar]=tmpDouble;
2371 gTools().ReadAttr( parent, Form(
"PreselectionLowSigVar%d",ivar), tmpBool);
2372 fIsLowSigCut[ivar]=tmpBool;
2373 gTools().ReadAttr( parent, Form(
"PreselectionLowSigVar%dValue",ivar), tmpDouble);
2374 fLowSigCut[ivar]=tmpDouble;
2375 gTools().ReadAttr( parent, Form(
"PreselectionHighBkgVar%d",ivar), tmpBool);
2376 fIsHighBkgCut[ivar]=tmpBool;
2377 gTools().ReadAttr( parent, Form(
"PreselectionHighBkgVar%dValue",ivar), tmpDouble);
2378 fHighBkgCut[ivar]=tmpDouble;
2379 gTools().ReadAttr( parent, Form(
"PreselectionHighSigVar%d",ivar),tmpBool);
2380 fIsHighSigCut[ivar]=tmpBool;
2381 gTools().ReadAttr( parent, Form(
"PreselectionHighSigVar%dValue",ivar), tmpDouble);
2382 fHighSigCut[ivar]=tmpDouble;
2387 gTools().ReadAttr( parent,
"NTrees", ntrees );
2389 if(gTools().HasAttr(parent,
"TreeType")) {
2390 gTools().ReadAttr( parent,
"TreeType", analysisType );
2392 gTools().ReadAttr( parent,
"AnalysisType", analysisType );
2395 void* ch = gTools().GetChild(parent);
2398 fForest.push_back( dynamic_cast<DecisionTree*>( DecisionTree::CreateFromXML(ch, GetTrainingTMVAVersionCode()) ) );
2399 fForest.back()->SetAnalysisType(Types::EAnalysisType(analysisType));
2400 fForest.back()->SetTreeID(i++);
2401 gTools().ReadAttr(ch,
"boostWeight",boostWeight);
2402 fBoostWeights.push_back(boostWeight);
2403 ch = gTools().GetNextChild(ch);
2410 void TMVA::MethodBDT::ReadWeightsFromStream( std::istream& istr )
2414 Int_t analysisType(0);
2417 istr >> dummy >> fNTrees;
2418 Log() << kINFO <<
"Read " << fNTrees <<
" Decision trees" << Endl;
2420 for (UInt_t i=0;i<fForest.size();i++)
delete fForest[i];
2422 fBoostWeights.clear();
2424 Double_t boostWeight;
2425 for (
int i=0;i<fNTrees;i++) {
2426 istr >> dummy >> iTree >> dummy >> boostWeight;
2428 fForest.back()->Print( std::cout );
2429 Log() << kFATAL <<
"Error while reading weight file; mismatch iTree="
2430 << iTree <<
" i=" << i
2431 <<
" dummy " << dummy
2432 <<
" boostweight " << boostWeight
2435 fForest.push_back(
new DecisionTree() );
2436 fForest.back()->SetAnalysisType(Types::EAnalysisType(analysisType));
2437 fForest.back()->SetTreeID(i);
2438 fForest.back()->Read(istr, GetTrainingTMVAVersionCode());
2439 fBoostWeights.push_back(boostWeight);
2445 Double_t TMVA::MethodBDT::GetMvaValue( Double_t* err, Double_t* errUpper ){
2446 return this->GetMvaValue( err, errUpper, 0 );
2454 Double_t TMVA::MethodBDT::GetMvaValue( Double_t* err, Double_t* errUpper, UInt_t useNTrees )
2456 const Event* ev = GetEvent();
2457 if (fDoPreselection) {
2458 Double_t val = ApplyPreselectionCuts(ev);
2459 if (TMath::Abs(val)>0.05)
return val;
2461 return PrivateGetMvaValue(ev, err, errUpper, useNTrees);
2470 Double_t TMVA::MethodBDT::PrivateGetMvaValue(
const TMVA::Event* ev, Double_t* err, Double_t* errUpper, UInt_t useNTrees )
2473 NoErrorCalc(err, errUpper);
2477 UInt_t nTrees = fForest.size();
2479 if (useNTrees > 0 ) nTrees = useNTrees;
2481 if (fBoostType==
"Grad")
return GetGradBoostMVA(ev,nTrees);
2485 for (UInt_t itree=0; itree<nTrees; itree++) {
2487 myMVA += fBoostWeights[itree] * fForest[itree]->CheckEvent(ev,fUseYesNoLeaf);
2488 norm += fBoostWeights[itree];
2490 return ( norm > std::numeric_limits<double>::epsilon() ) ? myMVA /= norm : 0 ;
2497 const std::vector<Float_t>& TMVA::MethodBDT::GetMulticlassValues()
2499 const TMVA::Event *e = GetEvent();
2500 if (fMulticlassReturnVal == NULL) fMulticlassReturnVal =
new std::vector<Float_t>();
2501 fMulticlassReturnVal->clear();
2503 UInt_t nClasses = DataInfo().GetNClasses();
2504 std::vector<Double_t> temp(nClasses);
2505 auto forestSize = fForest.size();
2508 std::vector<TMVA::DecisionTree *> forest = fForest;
2509 auto get_output = [&e, &forest, &temp, forestSize, nClasses](UInt_t iClass) {
2510 for (UInt_t itree = iClass; itree < forestSize; itree += nClasses) {
2511 temp[iClass] += forest[itree]->CheckEvent(e, kFALSE);
2515 TMVA::Config::Instance().GetThreadExecutor()
2516 .Foreach(get_output, ROOT::TSeqU(nClasses));
2520 UInt_t classOfTree = 0;
2521 for (UInt_t itree = 0; itree < forestSize; ++itree) {
2522 temp[classOfTree] += fForest[itree]->CheckEvent(e, kFALSE);
2523 if (++classOfTree == nClasses) classOfTree = 0;
2529 std::transform(temp.begin(), temp.end(), temp.begin(), [](Double_t d){
return exp(d);});
2531 Double_t exp_sum = std::accumulate(temp.begin(), temp.end(), 0.0);
2533 for (UInt_t i = 0; i < nClasses; i++) {
2534 Double_t p_cls = temp[i] / exp_sum;
2535 (*fMulticlassReturnVal).push_back(p_cls);
2538 return *fMulticlassReturnVal;
2544 const std::vector<Float_t> & TMVA::MethodBDT::GetRegressionValues()
2547 if (fRegressionReturnVal == NULL) fRegressionReturnVal =
new std::vector<Float_t>();
2548 fRegressionReturnVal->clear();
2550 const Event * ev = GetEvent();
2551 Event * evT =
new Event(*ev);
2555 if (fBoostType==
"AdaBoostR2") {
2566 vector< Double_t > response(fForest.size());
2567 vector< Double_t > weight(fForest.size());
2568 Double_t totalSumOfWeights = 0;
2570 for (UInt_t itree=0; itree<fForest.size(); itree++) {
2571 response[itree] = fForest[itree]->CheckEvent(ev,kFALSE);
2572 weight[itree] = fBoostWeights[itree];
2573 totalSumOfWeights += fBoostWeights[itree];
2576 std::vector< std::vector<Double_t> > vtemp;
2577 vtemp.push_back( response );
2578 vtemp.push_back( weight );
2579 gTools().UsefulSortAscending( vtemp );
2582 Double_t sumOfWeights = 0;
2583 while (sumOfWeights <= totalSumOfWeights/2.) {
2584 sumOfWeights += vtemp[1][t];
2590 for (UInt_t i= TMath::Max(UInt_t(0),UInt_t(t-(fForest.size()/6)-0.5));
2591 i< TMath::Min(UInt_t(fForest.size()),UInt_t(t+(fForest.size()/6)+0.5)); i++) {
2596 evT->SetTarget(0, rVal/Double_t(count) );
2598 else if(fBoostType==
"Grad"){
2599 for (UInt_t itree=0; itree<fForest.size(); itree++) {
2600 myMVA += fForest[itree]->CheckEvent(ev,kFALSE);
2603 evT->SetTarget(0, myMVA+fBoostWeights[0] );
2606 for (UInt_t itree=0; itree<fForest.size(); itree++) {
2608 myMVA += fBoostWeights[itree] * fForest[itree]->CheckEvent(ev,kFALSE);
2609 norm += fBoostWeights[itree];
2612 evT->SetTarget(0, ( norm > std::numeric_limits<double>::epsilon() ) ? myMVA /= norm : 0 );
2617 const Event* evT2 = GetTransformationHandler().InverseTransform( evT );
2618 fRegressionReturnVal->push_back( evT2->GetTarget(0) );
2623 return *fRegressionReturnVal;
2630 void TMVA::MethodBDT::WriteMonitoringHistosToFile(
void )
const
2632 Log() << kDEBUG <<
"\tWrite monitoring histograms to file: " << BaseDir()->GetPath() << Endl;
2636 fMonitorNtuple->Write();
2645 vector< Double_t > TMVA::MethodBDT::GetVariableImportance()
2647 fVariableImportance.resize(GetNvar());
2648 for (UInt_t ivar = 0; ivar < GetNvar(); ivar++) {
2649 fVariableImportance[ivar]=0;
2652 for (UInt_t itree = 0; itree < GetNTrees(); itree++) {
2653 std::vector<Double_t> relativeImportance(fForest[itree]->GetVariableImportance());
2654 for (UInt_t i=0; i< relativeImportance.size(); i++) {
2655 fVariableImportance[i] += fBoostWeights[itree] * relativeImportance[i];
2659 for (UInt_t ivar=0; ivar< fVariableImportance.size(); ivar++){
2660 fVariableImportance[ivar] = TMath::Sqrt(fVariableImportance[ivar]);
2661 sum += fVariableImportance[ivar];
2663 for (UInt_t ivar=0; ivar< fVariableImportance.size(); ivar++) fVariableImportance[ivar] /= sum;
2665 return fVariableImportance;
2673 Double_t TMVA::MethodBDT::GetVariableImportance( UInt_t ivar )
2675 std::vector<Double_t> relativeImportance = this->GetVariableImportance();
2676 if (ivar < (UInt_t)relativeImportance.size())
return relativeImportance[ivar];
2677 else Log() << kFATAL <<
"<GetVariableImportance> ivar = " << ivar <<
" is out of range " << Endl;
2685 const TMVA::Ranking* TMVA::MethodBDT::CreateRanking()
2688 fRanking =
new Ranking( GetName(),
"Variable Importance" );
2689 vector< Double_t> importance(this->GetVariableImportance());
2691 for (UInt_t ivar=0; ivar<GetNvar(); ivar++) {
2693 fRanking->AddRank( Rank( GetInputLabel(ivar), importance[ivar] ) );
2702 void TMVA::MethodBDT::GetHelpMessage()
const
2705 Log() << gTools().Color(
"bold") <<
"--- Short description:" << gTools().Color(
"reset") << Endl;
2707 Log() <<
"Boosted Decision Trees are a collection of individual decision" << Endl;
2708 Log() <<
"trees which form a multivariate classifier by (weighted) majority " << Endl;
2709 Log() <<
"vote of the individual trees. Consecutive decision trees are " << Endl;
2710 Log() <<
"trained using the original training data set with re-weighted " << Endl;
2711 Log() <<
"events. By default, the AdaBoost method is employed, which gives " << Endl;
2712 Log() <<
"events that were misclassified in the previous tree a larger " << Endl;
2713 Log() <<
"weight in the training of the following tree." << Endl;
2715 Log() <<
"Decision trees are a sequence of binary splits of the data sample" << Endl;
2716 Log() <<
"using a single discriminant variable at a time. A test event " << Endl;
2717 Log() <<
"ending up after the sequence of left-right splits in a final " << Endl;
2718 Log() <<
"(\"leaf\") node is classified as either signal or background" << Endl;
2719 Log() <<
"depending on the majority type of training events in that node." << Endl;
2721 Log() << gTools().Color(
"bold") <<
"--- Performance optimisation:" << gTools().Color(
"reset") << Endl;
2723 Log() <<
"By the nature of the binary splits performed on the individual" << Endl;
2724 Log() <<
"variables, decision trees do not deal well with linear correlations" << Endl;
2725 Log() <<
"between variables (they need to approximate the linear split in" << Endl;
2726 Log() <<
"the two dimensional space by a sequence of splits on the two " << Endl;
2727 Log() <<
"variables individually). Hence decorrelation could be useful " << Endl;
2728 Log() <<
"to optimise the BDT performance." << Endl;
2730 Log() << gTools().Color(
"bold") <<
"--- Performance tuning via configuration options:" << gTools().Color(
"reset") << Endl;
2732 Log() <<
"The two most important parameters in the configuration are the " << Endl;
2733 Log() <<
"minimal number of events requested by a leaf node as percentage of the " <<Endl;
2734 Log() <<
" number of training events (option \"MinNodeSize\" replacing the actual number " << Endl;
2735 Log() <<
" of events \"nEventsMin\" as given in earlier versions" << Endl;
2736 Log() <<
"If this number is too large, detailed features " << Endl;
2737 Log() <<
"in the parameter space are hard to be modelled. If it is too small, " << Endl;
2738 Log() <<
"the risk to overtrain rises and boosting seems to be less effective" << Endl;
2739 Log() <<
" typical values from our current experience for best performance " << Endl;
2740 Log() <<
" are between 0.5(%) and 10(%) " << Endl;
2742 Log() <<
"The default minimal number is currently set to " << Endl;
2743 Log() <<
" max(20, (N_training_events / N_variables^2 / 10)) " << Endl;
2744 Log() <<
"and can be changed by the user." << Endl;
2746 Log() <<
"The other crucial parameter, the pruning strength (\"PruneStrength\")," << Endl;
2747 Log() <<
"is also related to overtraining. It is a regularisation parameter " << Endl;
2748 Log() <<
"that is used when determining after the training which splits " << Endl;
2749 Log() <<
"are considered statistically insignificant and are removed. The" << Endl;
2750 Log() <<
"user is advised to carefully watch the BDT screen output for" << Endl;
2751 Log() <<
"the comparison between efficiencies obtained on the training and" << Endl;
2752 Log() <<
"the independent test sample. They should be equal within statistical" << Endl;
2753 Log() <<
"errors, in order to minimize statistical fluctuations in different samples." << Endl;
2759 void TMVA::MethodBDT::MakeClassSpecific( std::ostream& fout,
const TString& className )
const
2761 TString nodeName = className;
2762 nodeName.ReplaceAll(
"Read",
"");
2763 nodeName.Append(
"Node");
2765 fout <<
" std::vector<"<<nodeName<<
"*> fForest; // i.e. root nodes of decision trees" << std::endl;
2766 fout <<
" std::vector<double> fBoostWeights; // the weights applied in the individual boosts" << std::endl;
2767 fout <<
"};" << std::endl << std::endl;
2769 if(GetAnalysisType() == Types::kMulticlass) {
2770 fout <<
"std::vector<double> ReadBDTG::GetMulticlassValues__( const std::vector<double>& inputValues ) const" << std::endl;
2771 fout <<
"{" << std::endl;
2772 fout <<
" uint nClasses = " << DataInfo().GetNClasses() <<
";" << std::endl;
2773 fout <<
" std::vector<double> fMulticlassReturnVal;" << std::endl;
2774 fout <<
" fMulticlassReturnVal.reserve(nClasses);" << std::endl;
2776 fout <<
" std::vector<double> temp(nClasses);" << std::endl;
2777 fout <<
" auto forestSize = fForest.size();" << std::endl;
2778 fout <<
" // trees 0, nClasses, 2*nClasses, ... belong to class 0" << std::endl;
2779 fout <<
" // trees 1, nClasses+1, 2*nClasses+1, ... belong to class 1 and so forth" << std::endl;
2780 fout <<
" uint classOfTree = 0;" << std::endl;
2781 fout <<
" for (uint itree = 0; itree < forestSize; ++itree) {" << std::endl;
2782 fout <<
" BDTGNode *current = fForest[itree];" << std::endl;
2783 fout <<
" while (current->GetNodeType() == 0) { //intermediate node" << std::endl;
2784 fout <<
" if (current->GoesRight(inputValues)) current=(BDTGNode*)current->GetRight();" << std::endl;
2785 fout <<
" else current=(BDTGNode*)current->GetLeft();" << std::endl;
2786 fout <<
" }" << std::endl;
2787 fout <<
" temp[classOfTree] += current->GetResponse();" << std::endl;
2788 fout <<
" if (++classOfTree == nClasses) classOfTree = 0; // cheap modulo" << std::endl;
2789 fout <<
" }" << std::endl;
2791 fout <<
" // we want to calculate sum of exp(temp[j] - temp[i]) for all i,j (i!=j)" << std::endl;
2792 fout <<
" // first calculate exp(), then replace minus with division." << std::endl;
2793 fout <<
" std::transform(temp.begin(), temp.end(), temp.begin(), [](double d){return exp(d);});" << std::endl;
2795 fout <<
" for(uint iClass=0; iClass<nClasses; iClass++){" << std::endl;
2796 fout <<
" double norm = 0.0;" << std::endl;
2797 fout <<
" for(uint j=0;j<nClasses;j++){" << std::endl;
2798 fout <<
" if(iClass!=j)" << std::endl;
2799 fout <<
" norm += temp[j] / temp[iClass];" << std::endl;
2800 fout <<
" }" << std::endl;
2801 fout <<
" fMulticlassReturnVal.push_back(1.0/(1.0+norm));" << std::endl;
2802 fout <<
" }" << std::endl;
2804 fout <<
" return fMulticlassReturnVal;" << std::endl;
2805 fout <<
"}" << std::endl;
2807 fout <<
"double " << className <<
"::GetMvaValue__( const std::vector<double>& inputValues ) const" << std::endl;
2808 fout <<
"{" << std::endl;
2809 fout <<
" double myMVA = 0;" << std::endl;
2810 if (fDoPreselection){
2811 for (UInt_t ivar = 0; ivar< fIsLowBkgCut.size(); ivar++){
2812 if (fIsLowBkgCut[ivar]){
2813 fout <<
" if (inputValues["<<ivar<<
"] < " << fLowBkgCut[ivar] <<
") return -1; // is background preselection cut" << std::endl;
2815 if (fIsLowSigCut[ivar]){
2816 fout <<
" if (inputValues["<<ivar<<
"] < "<< fLowSigCut[ivar] <<
") return 1; // is signal preselection cut" << std::endl;
2818 if (fIsHighBkgCut[ivar]){
2819 fout <<
" if (inputValues["<<ivar<<
"] > "<<fHighBkgCut[ivar] <<
") return -1; // is background preselection cut" << std::endl;
2821 if (fIsHighSigCut[ivar]){
2822 fout <<
" if (inputValues["<<ivar<<
"] > "<<fHighSigCut[ivar]<<
") return 1; // is signal preselection cut" << std::endl;
2827 if (fBoostType!=
"Grad"){
2828 fout <<
" double norm = 0;" << std::endl;
2830 fout <<
" for (unsigned int itree=0; itree<fForest.size(); itree++){" << std::endl;
2831 fout <<
" "<<nodeName<<
" *current = fForest[itree];" << std::endl;
2832 fout <<
" while (current->GetNodeType() == 0) { //intermediate node" << std::endl;
2833 fout <<
" if (current->GoesRight(inputValues)) current=("<<nodeName<<
"*)current->GetRight();" << std::endl;
2834 fout <<
" else current=("<<nodeName<<
"*)current->GetLeft();" << std::endl;
2835 fout <<
" }" << std::endl;
2836 if (fBoostType==
"Grad"){
2837 fout <<
" myMVA += current->GetResponse();" << std::endl;
2839 if (fUseYesNoLeaf) fout <<
" myMVA += fBoostWeights[itree] * current->GetNodeType();" << std::endl;
2840 else fout <<
" myMVA += fBoostWeights[itree] * current->GetPurity();" << std::endl;
2841 fout <<
" norm += fBoostWeights[itree];" << std::endl;
2843 fout <<
" }" << std::endl;
2844 if (fBoostType==
"Grad"){
2845 fout <<
" return 2.0/(1.0+exp(-2.0*myMVA))-1.0;" << std::endl;
2847 else fout <<
" return myMVA /= norm;" << std::endl;
2848 fout <<
"}" << std::endl << std::endl;
2851 fout <<
"void " << className <<
"::Initialize()" << std::endl;
2852 fout <<
"{" << std::endl;
2853 fout <<
" double inf = std::numeric_limits<double>::infinity();" << std::endl;
2854 fout <<
" double nan = std::numeric_limits<double>::quiet_NaN();" << std::endl;
2856 for (UInt_t itree=0; itree<GetNTrees(); itree++) {
2857 fout <<
" // itree = " << itree << std::endl;
2858 fout <<
" fBoostWeights.push_back(" << fBoostWeights[itree] <<
");" << std::endl;
2859 fout <<
" fForest.push_back( " << std::endl;
2860 this->MakeClassInstantiateNode((DecisionTreeNode*)fForest[itree]->GetRoot(), fout, className);
2861 fout <<
" );" << std::endl;
2863 fout <<
" return;" << std::endl;
2864 fout <<
"};" << std::endl;
2866 fout <<
"// Clean up" << std::endl;
2867 fout <<
"inline void " << className <<
"::Clear() " << std::endl;
2868 fout <<
"{" << std::endl;
2869 fout <<
" for (unsigned int itree=0; itree<fForest.size(); itree++) { " << std::endl;
2870 fout <<
" delete fForest[itree]; " << std::endl;
2871 fout <<
" }" << std::endl;
2872 fout <<
"}" << std::endl;
2879 void TMVA::MethodBDT::MakeClassSpecificHeader( std::ostream& fout,
const TString& className)
const
2881 TString nodeName = className;
2882 nodeName.ReplaceAll(
"Read",
"");
2883 nodeName.Append(
"Node");
2884 fout <<
"#include <algorithm>" << std::endl;
2885 fout <<
"#include <limits>" << std::endl;
2888 fout <<
"#define NN new "<<nodeName << std::endl;
2891 fout <<
"#ifndef "<<nodeName<<
"__def" << std::endl;
2892 fout <<
"#define "<<nodeName<<
"__def" << std::endl;
2894 fout <<
"class "<<nodeName<<
" {" << std::endl;
2896 fout <<
"public:" << std::endl;
2898 fout <<
" // constructor of an essentially \"empty\" node floating in space" << std::endl;
2899 fout <<
" "<<nodeName<<
" ( "<<nodeName<<
"* left,"<<nodeName<<
"* right," << std::endl;
2900 if (fUseFisherCuts){
2901 fout <<
" int nFisherCoeff," << std::endl;
2902 for (UInt_t i=0;i<GetNVariables()+1;i++){
2903 fout <<
" double fisherCoeff"<<i<<
"," << std::endl;
2906 fout <<
" int selector, double cutValue, bool cutType, " << std::endl;
2907 fout <<
" int nodeType, double purity, double response ) :" << std::endl;
2908 fout <<
" fLeft ( left )," << std::endl;
2909 fout <<
" fRight ( right )," << std::endl;
2910 if (fUseFisherCuts) fout <<
" fNFisherCoeff ( nFisherCoeff )," << std::endl;
2911 fout <<
" fSelector ( selector )," << std::endl;
2912 fout <<
" fCutValue ( cutValue )," << std::endl;
2913 fout <<
" fCutType ( cutType )," << std::endl;
2914 fout <<
" fNodeType ( nodeType )," << std::endl;
2915 fout <<
" fPurity ( purity )," << std::endl;
2916 fout <<
" fResponse ( response ){" << std::endl;
2917 if (fUseFisherCuts){
2918 for (UInt_t i=0;i<GetNVariables()+1;i++){
2919 fout <<
" fFisherCoeff.push_back(fisherCoeff"<<i<<
");" << std::endl;
2922 fout <<
" }" << std::endl << std::endl;
2923 fout <<
" virtual ~"<<nodeName<<
"();" << std::endl << std::endl;
2924 fout <<
" // test event if it descends the tree at this node to the right" << std::endl;
2925 fout <<
" virtual bool GoesRight( const std::vector<double>& inputValues ) const;" << std::endl;
2926 fout <<
" "<<nodeName<<
"* GetRight( void ) {return fRight; };" << std::endl << std::endl;
2927 fout <<
" // test event if it descends the tree at this node to the left " << std::endl;
2928 fout <<
" virtual bool GoesLeft ( const std::vector<double>& inputValues ) const;" << std::endl;
2929 fout <<
" "<<nodeName<<
"* GetLeft( void ) { return fLeft; }; " << std::endl << std::endl;
2930 fout <<
" // return S/(S+B) (purity) at this node (from training)" << std::endl << std::endl;
2931 fout <<
" double GetPurity( void ) const { return fPurity; } " << std::endl;
2932 fout <<
" // return the node type" << std::endl;
2933 fout <<
" int GetNodeType( void ) const { return fNodeType; }" << std::endl;
2934 fout <<
" double GetResponse(void) const {return fResponse;}" << std::endl << std::endl;
2935 fout <<
"private:" << std::endl << std::endl;
2936 fout <<
" "<<nodeName<<
"* fLeft; // pointer to the left daughter node" << std::endl;
2937 fout <<
" "<<nodeName<<
"* fRight; // pointer to the right daughter node" << std::endl;
2938 if (fUseFisherCuts){
2939 fout <<
" int fNFisherCoeff; // =0 if this node doesn't use fisher, else =nvar+1 " << std::endl;
2940 fout <<
" std::vector<double> fFisherCoeff; // the fisher coeff (offset at the last element)" << std::endl;
2942 fout <<
" int fSelector; // index of variable used in node selection (decision tree) " << std::endl;
2943 fout <<
" double fCutValue; // cut value applied on this node to discriminate bkg against sig" << std::endl;
2944 fout <<
" bool fCutType; // true: if event variable > cutValue ==> signal , false otherwise" << std::endl;
2945 fout <<
" int fNodeType; // Type of node: -1 == Bkg-leaf, 1 == Signal-leaf, 0 = internal " << std::endl;
2946 fout <<
" double fPurity; // Purity of node from training"<< std::endl;
2947 fout <<
" double fResponse; // Regression response value of node" << std::endl;
2948 fout <<
"}; " << std::endl;
2950 fout <<
"//_______________________________________________________________________" << std::endl;
2951 fout <<
" "<<nodeName<<
"::~"<<nodeName<<
"()" << std::endl;
2952 fout <<
"{" << std::endl;
2953 fout <<
" if (fLeft != NULL) delete fLeft;" << std::endl;
2954 fout <<
" if (fRight != NULL) delete fRight;" << std::endl;
2955 fout <<
"}; " << std::endl;
2957 fout <<
"//_______________________________________________________________________" << std::endl;
2958 fout <<
"bool "<<nodeName<<
"::GoesRight( const std::vector<double>& inputValues ) const" << std::endl;
2959 fout <<
"{" << std::endl;
2960 fout <<
" // test event if it descends the tree at this node to the right" << std::endl;
2961 fout <<
" bool result;" << std::endl;
2962 if (fUseFisherCuts){
2963 fout <<
" if (fNFisherCoeff == 0){" << std::endl;
2964 fout <<
" result = (inputValues[fSelector] >= fCutValue );" << std::endl;
2965 fout <<
" }else{" << std::endl;
2966 fout <<
" double fisher = fFisherCoeff.at(fFisherCoeff.size()-1);" << std::endl;
2967 fout <<
" for (unsigned int ivar=0; ivar<fFisherCoeff.size()-1; ivar++)" << std::endl;
2968 fout <<
" fisher += fFisherCoeff.at(ivar)*inputValues.at(ivar);" << std::endl;
2969 fout <<
" result = fisher > fCutValue;" << std::endl;
2970 fout <<
" }" << std::endl;
2972 fout <<
" result = (inputValues[fSelector] >= fCutValue );" << std::endl;
2974 fout <<
" if (fCutType == true) return result; //the cuts are selecting Signal ;" << std::endl;
2975 fout <<
" else return !result;" << std::endl;
2976 fout <<
"}" << std::endl;
2978 fout <<
"//_______________________________________________________________________" << std::endl;
2979 fout <<
"bool "<<nodeName<<
"::GoesLeft( const std::vector<double>& inputValues ) const" << std::endl;
2980 fout <<
"{" << std::endl;
2981 fout <<
" // test event if it descends the tree at this node to the left" << std::endl;
2982 fout <<
" if (!this->GoesRight(inputValues)) return true;" << std::endl;
2983 fout <<
" else return false;" << std::endl;
2984 fout <<
"}" << std::endl;
2986 fout <<
"#endif" << std::endl;
2993 void TMVA::MethodBDT::MakeClassInstantiateNode( DecisionTreeNode *n, std::ostream& fout,
const TString& className )
const
2996 Log() << kFATAL <<
"MakeClassInstantiateNode: started with undefined node" <<Endl;
2999 fout <<
"NN("<<std::endl;
3000 if (n->GetLeft() != NULL){
3001 this->MakeClassInstantiateNode( (DecisionTreeNode*)n->GetLeft() , fout, className);
3006 fout <<
", " <<std::endl;
3007 if (n->GetRight() != NULL){
3008 this->MakeClassInstantiateNode( (DecisionTreeNode*)n->GetRight(), fout, className );
3013 fout <<
", " << std::endl
3014 << std::setprecision(6);
3015 if (fUseFisherCuts){
3016 fout << n->GetNFisherCoeff() <<
", ";
3017 for (UInt_t i=0; i< GetNVariables()+1; i++) {
3018 if (n->GetNFisherCoeff() == 0 ){
3021 fout << n->GetFisherCoeff(i) <<
", ";
3025 fout << n->GetSelector() <<
", "
3026 << n->GetCutValue() <<
", "
3027 << n->GetCutType() <<
", "
3028 << n->GetNodeType() <<
", "
3029 << n->GetPurity() <<
","
3030 << n->GetResponse() <<
") ";
3038 void TMVA::MethodBDT::DeterminePreselectionCuts(
const std::vector<const TMVA::Event*>& eventSample)
3040 Double_t nTotS = 0.0, nTotB = 0.0;
3041 Int_t nTotS_unWeighted = 0, nTotB_unWeighted = 0;
3043 std::vector<TMVA::BDTEventWrapper> bdtEventSample;
3045 fIsLowSigCut.assign(GetNvar(),kFALSE);
3046 fIsLowBkgCut.assign(GetNvar(),kFALSE);
3047 fIsHighSigCut.assign(GetNvar(),kFALSE);
3048 fIsHighBkgCut.assign(GetNvar(),kFALSE);
3050 fLowSigCut.assign(GetNvar(),0.);
3051 fLowBkgCut.assign(GetNvar(),0.);
3052 fHighSigCut.assign(GetNvar(),0.);
3053 fHighBkgCut.assign(GetNvar(),0.);
3058 for( std::vector<const TMVA::Event*>::const_iterator it = eventSample.begin(); it != eventSample.end(); ++it ) {
3059 if (DataInfo().IsSignal(*it)){
3060 nTotS += (*it)->GetWeight();
3064 nTotB += (*it)->GetWeight();
3067 bdtEventSample.push_back(TMVA::BDTEventWrapper(*it));
3070 for( UInt_t ivar = 0; ivar < GetNvar(); ivar++ ) {
3071 TMVA::BDTEventWrapper::SetVarIndex(ivar);
3072 std::sort( bdtEventSample.begin(),bdtEventSample.end() );
3074 Double_t bkgWeightCtr = 0.0, sigWeightCtr = 0.0;
3075 std::vector<TMVA::BDTEventWrapper>::iterator it = bdtEventSample.begin(), it_end = bdtEventSample.end();
3076 for( ; it != it_end; ++it ) {
3077 if (DataInfo().IsSignal(**it))
3078 sigWeightCtr += (**it)->GetWeight();
3080 bkgWeightCtr += (**it)->GetWeight();
3082 it->SetCumulativeWeight(
false,bkgWeightCtr);
3083 it->SetCumulativeWeight(
true,sigWeightCtr);
3088 Double_t dVal = (DataInfo().GetVariableInfo(ivar).GetMax() - DataInfo().GetVariableInfo(ivar).GetMin())/100. ;
3089 Double_t nSelS, nSelB, effS=0.05, effB=0.05, rejS=0.05, rejB=0.05;
3090 Double_t tmpEffS, tmpEffB, tmpRejS, tmpRejB;
3095 for(UInt_t iev = 1; iev < bdtEventSample.size(); iev++) {
3098 nSelS = bdtEventSample[iev].GetCumulativeWeight(
true);
3099 nSelB = bdtEventSample[iev].GetCumulativeWeight(
false);
3101 tmpEffS=nSelS/nTotS;
3102 tmpEffB=nSelB/nTotB;
3105 if (nSelS==0 && tmpEffB>effB) {effB=tmpEffB; fLowBkgCut[ivar] = bdtEventSample[iev].GetVal() - dVal; fIsLowBkgCut[ivar]=kTRUE;}
3106 else if (nSelB==0 && tmpEffS>effS) {effS=tmpEffS; fLowSigCut[ivar] = bdtEventSample[iev].GetVal() - dVal; fIsLowSigCut[ivar]=kTRUE;}
3107 else if (nSelB==nTotB && tmpRejS>rejS) {rejS=tmpRejS; fHighSigCut[ivar] = bdtEventSample[iev].GetVal() + dVal; fIsHighSigCut[ivar]=kTRUE;}
3108 else if (nSelS==nTotS && tmpRejB>rejB) {rejB=tmpRejB; fHighBkgCut[ivar] = bdtEventSample[iev].GetVal() + dVal; fIsHighBkgCut[ivar]=kTRUE;}
3113 Log() << kDEBUG <<
" \tfound and suggest the following possible pre-selection cuts " << Endl;
3114 if (fDoPreselection) Log() << kDEBUG <<
"\tthe training will be done after these cuts... and GetMVA value returns +1, (-1) for a signal (bkg) event that passes these cuts" << Endl;
3115 else Log() << kDEBUG <<
"\tas option DoPreselection was not used, these cuts however will not be performed, but the training will see the full sample"<<Endl;
3116 for (UInt_t ivar=0; ivar < GetNvar(); ivar++ ) {
3117 if (fIsLowBkgCut[ivar]){
3118 Log() << kDEBUG <<
" \tfound cut: Bkg if var " << ivar <<
" < " << fLowBkgCut[ivar] << Endl;
3120 if (fIsLowSigCut[ivar]){
3121 Log() << kDEBUG <<
" \tfound cut: Sig if var " << ivar <<
" < " << fLowSigCut[ivar] << Endl;
3123 if (fIsHighBkgCut[ivar]){
3124 Log() << kDEBUG <<
" \tfound cut: Bkg if var " << ivar <<
" > " << fHighBkgCut[ivar] << Endl;
3126 if (fIsHighSigCut[ivar]){
3127 Log() << kDEBUG <<
" \tfound cut: Sig if var " << ivar <<
" > " << fHighSigCut[ivar] << Endl;
3138 Double_t TMVA::MethodBDT::ApplyPreselectionCuts(
const Event* ev)
3142 for (UInt_t ivar=0; ivar < GetNvar(); ivar++ ) {
3143 if (fIsLowBkgCut[ivar]){
3144 if (ev->GetValue(ivar) < fLowBkgCut[ivar]) result = -1;
3146 if (fIsLowSigCut[ivar]){
3147 if (ev->GetValue(ivar) < fLowSigCut[ivar]) result = 1;
3149 if (fIsHighBkgCut[ivar]){
3150 if (ev->GetValue(ivar) > fHighBkgCut[ivar]) result = -1;
3152 if (fIsHighSigCut[ivar]){
3153 if (ev->GetValue(ivar) > fHighSigCut[ivar]) result = 1;