Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
MethodKNN.cxx
Go to the documentation of this file.
1// @(#)root/tmva $Id$
2// Author: Rustem Ospanov
3
4/**********************************************************************************
5 * Project: TMVA - a Root-integrated toolkit for multivariate data analysis *
6 * Package: TMVA *
7 * Class : MethodKNN *
8 * *
9 * *
10 * Description: *
11 * Implementation *
12 * *
13 * Author: *
14 * Rustem Ospanov <rustem@fnal.gov> - U. of Texas at Austin, USA *
15 * *
16 * Copyright (c) 2007: *
17 * CERN, Switzerland *
18 * MPI-K Heidelberg, Germany *
19 * U. of Texas at Austin, USA *
20 * *
21 * Redistribution and use in source and binary forms, with or without *
22 * modification, are permitted according to the terms listed in LICENSE *
23 * (see tmva/doc/LICENSE) *
24 **********************************************************************************/
25
26/*! \class TMVA::MethodKNN
27\ingroup TMVA
28
29Analysis of k-nearest neighbor.
30
31*/
32
33#include "TMVA/MethodKNN.h"
34
36#include "TMVA/Configurable.h"
37#include "TMVA/DataSetInfo.h"
38#include "TMVA/Event.h"
39#include "TMVA/LDA.h"
40#include "TMVA/IMethod.h"
41#include "TMVA/MethodBase.h"
42#include "TMVA/MsgLogger.h"
43#include "TMVA/Ranking.h"
44#include "TMVA/Tools.h"
45#include "TMVA/Types.h"
46
47#include "TFile.h"
48#include "TMath.h"
49#include "TTree.h"
50
51#include <cmath>
52#include <string>
53#include <cstdlib>
54
56
57
58////////////////////////////////////////////////////////////////////////////////
59/// standard constructor
60
62 const TString& methodTitle,
65 : TMVA::MethodBase(jobName, Types::kKNN, methodTitle, theData, theOption)
66 , fSumOfWeightsS(0)
67 , fSumOfWeightsB(0)
68 , fModule(0)
69 , fnkNN(0)
70 , fBalanceDepth(0)
71 , fScaleFrac(0)
72 , fSigmaFact(0)
73 , fTrim(kFALSE)
74 , fUseKernel(kFALSE)
75 , fUseWeight(kFALSE)
76 , fUseLDA(kFALSE)
77 , fTreeOptDepth(0)
78{
79}
80
81////////////////////////////////////////////////////////////////////////////////
82/// constructor from weight file
83
87 , fSumOfWeightsS(0)
88 , fSumOfWeightsB(0)
89 , fModule(0)
90 , fnkNN(0)
91 , fBalanceDepth(0)
92 , fScaleFrac(0)
93 , fSigmaFact(0)
94 , fTrim(kFALSE)
95 , fUseKernel(kFALSE)
96 , fUseWeight(kFALSE)
97 , fUseLDA(kFALSE)
98 , fTreeOptDepth(0)
99{
100}
101
102////////////////////////////////////////////////////////////////////////////////
103/// destructor
104
106{
107 if (fModule) delete fModule;
108}
109
110////////////////////////////////////////////////////////////////////////////////
111/// MethodKNN options
112///
113/// - fnkNN = 20; // number of k-nearest neighbors
114/// - fBalanceDepth = 6; // number of binary tree levels used for tree balancing
115/// - fScaleFrac = 0.8; // fraction of events used to compute variable width
116/// - fSigmaFact = 1.0; // scale factor for Gaussian sigma
117/// - fKernel = use polynomial (1-x^3)^3 or Gaussian kernel
118/// - fTrim = false; // use equal number of signal and background events
119/// - fUseKernel = false; // use polynomial kernel weight function
120/// - fUseWeight = true; // count events using weights
121/// - fUseLDA = false
122
124{
125 DeclareOptionRef(fnkNN = 20, "nkNN", "Number of k-nearest neighbors");
126 DeclareOptionRef(fBalanceDepth = 6, "BalanceDepth", "Binary tree balance depth");
127 DeclareOptionRef(fScaleFrac = 0.80, "ScaleFrac", "Fraction of events used to compute variable width");
128 DeclareOptionRef(fSigmaFact = 1.0, "SigmaFact", "Scale factor for sigma in Gaussian kernel");
129 DeclareOptionRef(fKernel = "Gaus", "Kernel", "Use polynomial (=Poln) or Gaussian (=Gaus) kernel");
130 DeclareOptionRef(fTrim = kFALSE, "Trim", "Use equal number of signal and background events");
131 DeclareOptionRef(fUseKernel = kFALSE, "UseKernel", "Use polynomial kernel weight");
132 DeclareOptionRef(fUseWeight = kTRUE, "UseWeight", "Use weight to count kNN events");
133 DeclareOptionRef(fUseLDA = kFALSE, "UseLDA", "Use local linear discriminant - experimental feature");
134}
135
136////////////////////////////////////////////////////////////////////////////////
137/// options that are used ONLY for the READER to ensure backward compatibility
138
141 DeclareOptionRef(fTreeOptDepth = 6, "TreeOptDepth", "Binary tree optimisation depth");
142}
143
144////////////////////////////////////////////////////////////////////////////////
145/// process the options specified by the user
146
148{
149 if (!(fnkNN > 0)) {
150 fnkNN = 10;
151 Log() << kWARNING << "kNN must be a positive integer: set kNN = " << fnkNN << Endl;
152 }
153 if (fScaleFrac < 0.0) {
154 fScaleFrac = 0.0;
155 Log() << kWARNING << "ScaleFrac can not be negative: set ScaleFrac = " << fScaleFrac << Endl;
156 }
157 if (fScaleFrac > 1.0) {
158 fScaleFrac = 1.0;
159 }
160 if (!(fBalanceDepth > 0)) {
161 fBalanceDepth = 6;
162 Log() << kWARNING << "Optimize must be a positive integer: set Optimize = " << fBalanceDepth << Endl;
163 }
164
165 Log() << kVERBOSE
166 << "kNN options: \n"
167 << " kNN = \n" << fnkNN
168 << " UseKernel = \n" << fUseKernel
169 << " SigmaFact = \n" << fSigmaFact
170 << " ScaleFrac = \n" << fScaleFrac
171 << " Kernel = \n" << fKernel
172 << " Trim = \n" << fTrim
173 << " Optimize = " << fBalanceDepth << Endl;
174}
175
176////////////////////////////////////////////////////////////////////////////////
177/// FDA can handle classification with 2 classes and regression with one regression-target
178
185
186////////////////////////////////////////////////////////////////////////////////
187/// Initialization
188
190{
191 // fScaleFrac <= 0.0 then do not scale input variables
192 // fScaleFrac >= 1.0 then use all event coordinates to scale input variables
193
194 fModule = new kNN::ModulekNN();
195 fSumOfWeightsS = 0;
196 fSumOfWeightsB = 0;
197}
198
199////////////////////////////////////////////////////////////////////////////////
200/// create kNN
201
203{
204 if (!fModule) {
205 Log() << kFATAL << "ModulekNN is not created" << Endl;
206 }
207
208 fModule->Clear();
209
210 std::string option;
211 if (fScaleFrac > 0.0) {
212 option += "metric";
213 }
214 if (fTrim) {
215 option += "trim";
216 }
217
218 Log() << kINFO << "Creating kd-tree with " << fEvent.size() << " events" << Endl;
219
220 for (kNN::EventVec::const_iterator event = fEvent.begin(); event != fEvent.end(); ++event) {
221 fModule->Add(*event);
222 }
223
224 // create binary tree
225 fModule->Fill(static_cast<UInt_t>(fBalanceDepth),
226 static_cast<UInt_t>(100.0*fScaleFrac),
227 option);
228}
229
230////////////////////////////////////////////////////////////////////////////////
231/// kNN training
232
234{
235 Log() << kHEADER << "<Train> start..." << Endl;
236
237 if (IsNormalised()) {
238 Log() << kINFO << "Input events are normalized - setting ScaleFrac to 0" << Endl;
239 fScaleFrac = 0.0;
240 }
241
242 if (!fEvent.empty()) {
243 Log() << kINFO << "Erasing " << fEvent.size() << " previously stored events" << Endl;
244 fEvent.clear();
245 }
246 if (GetNVariables() < 1)
247 Log() << kFATAL << "MethodKNN::Train() - mismatched or wrong number of event variables" << Endl;
248
249
250 Log() << kINFO << "Reading " << GetNEvents() << " events" << Endl;
251
252 for (UInt_t ievt = 0; ievt < GetNEvents(); ++ievt) {
253 // read the training event
254 const Event* evt_ = GetEvent(ievt);
255 Double_t weight = evt_->GetWeight();
256
257 // in case event with neg weights are to be ignored
258 if (IgnoreEventsWithNegWeightsInTraining() && weight <= 0) continue;
259
260 kNN::VarVec vvec(GetNVariables(), 0.0);
261 for (UInt_t ivar = 0; ivar < evt_ -> GetNVariables(); ++ivar) vvec[ivar] = evt_->GetValue(ivar);
262
264
265 if (DataInfo().IsSignal(evt_)) { // signal type = 1
266 fSumOfWeightsS += weight;
267 event_type = 1;
268 }
269 else { // background type = 2
270 fSumOfWeightsB += weight;
271 event_type = 2;
272 }
273
274 //
275 // Create event and add classification variables, weight, type and regression variables
276 //
278 event_knn.SetTargets(evt_->GetTargets());
279 fEvent.push_back(event_knn);
280
281 }
282 Log() << kINFO
283 << "Number of signal events " << fSumOfWeightsS << Endl
284 << "Number of background events " << fSumOfWeightsB << Endl;
285
286 // create kd-tree (binary tree) structure
287 MakeKNN();
288
289}
290
291////////////////////////////////////////////////////////////////////////////////
292/// Compute classifier response
293
295{
296 // cannot determine error
297 NoErrorCalc(err, errUpper);
298
299 //
300 // Define local variables
301 //
302 const Event *ev = GetEvent();
303 const Int_t nvar = GetNVariables();
304 const Double_t weight = ev->GetWeight();
305 const UInt_t knn = static_cast<UInt_t>(fnkNN);
306
307 kNN::VarVec vvec(static_cast<UInt_t>(nvar), 0.0);
308
309 for (Int_t ivar = 0; ivar < nvar; ++ivar) {
310 vvec[ivar] = ev->GetValue(ivar);
311 }
312
313 // search for fnkNN+2 nearest neighbors, pad with two
314 // events to avoid Monte-Carlo events with zero distance
315 // most of CPU time is spent in this recursive function
316 const kNN::Event event_knn(vvec, weight, 3);
317 fModule->Find(event_knn, knn + 2);
318
319 const kNN::List &rlist = fModule->GetkNNList();
320 if (rlist.size() != knn + 2) {
321 Log() << kFATAL << "kNN result list is empty" << Endl;
322 return -100.0;
323 }
324
325 if (fUseLDA) return MethodKNN::getLDAValue(rlist, event_knn);
326
327 //
328 // Set flags for kernel option=Gaus, Poln
329 //
330 Bool_t use_gaus = false, use_poln = false;
331 if (fUseKernel) {
332 if (fKernel == "Gaus") use_gaus = true;
333 else if (fKernel == "Poln") use_poln = true;
334 }
335
336 //
337 // Compute radius for polynomial kernel
338 //
339 Double_t kradius = -1.0;
340 if (use_poln) {
342
343 if (!(kradius > 0.0)) {
344 Log() << kFATAL << "kNN radius is not positive" << Endl;
345 return -100.0;
346 }
347
349 }
350
351 //
352 // Compute RMS of variable differences for Gaussian sigma
353 //
354 std::vector<Double_t> rms_vec;
355 if (use_gaus) {
357
358 if (rms_vec.empty() || rms_vec.size() != event_knn.GetNVar()) {
359 Log() << kFATAL << "Failed to compute RMS vector" << Endl;
360 return -100.0;
361 }
362 }
363
364 UInt_t count_all = 0;
366
367 for (kNN::List::const_iterator lit = rlist.begin(); lit != rlist.end(); ++lit) {
368
369 // get reference to current node to make code more readable
370 const kNN::Node<kNN::Event> &node = *(lit->first);
371
372 // Warn about Monte-Carlo event with zero distance
373 // this happens when this query event is also in learning sample
374 if (lit->second < 0.0) {
375 Log() << kFATAL << "A neighbor has negative distance to query event" << Endl;
376 }
377 else if (!(lit->second > 0.0)) {
378 Log() << kVERBOSE << "A neighbor has zero distance to query event" << Endl;
379 }
380
381 // get event weight and scale weight by kernel function
382 Double_t evweight = node.GetWeight();
385
386 if (fUseWeight) weight_all += evweight;
387 else ++weight_all;
388
389 if (node.GetEvent().GetType() == 1) { // signal type = 1
390 if (fUseWeight) weight_sig += evweight;
391 else ++weight_sig;
392 }
393 else if (node.GetEvent().GetType() == 2) { // background type = 2
394 }
395 else {
396 Log() << kFATAL << "Unknown type for training event" << Endl;
397 }
398
399 // use only fnkNN events
400 ++count_all;
401
402 if (count_all >= knn) {
403 break;
404 }
405 }
406
407 // check that total number of events or total weight sum is positive
408 if (!(count_all > 0)) {
409 Log() << kFATAL << "Size kNN result list is not positive" << Endl;
410 return -100.0;
411 }
412
413 // check that number of events matches number of k in knn
414 if (count_all < knn) {
415 Log() << kDEBUG << "count_all and kNN have different size: " << count_all << " < " << knn << Endl;
416 }
417
418 // Check that total weight is positive
419 if (!(weight_all > 0.0)) {
420 Log() << kFATAL << "kNN result total weight is not positive" << Endl;
421 return -100.0;
422 }
423
424 return weight_sig/weight_all;
425}
426
427////////////////////////////////////////////////////////////////////////////////
428/// Return vector of averages for target values of k-nearest neighbors.
429/// Use own copy of the regression vector, I do not like using a pointer to vector.
430
431const std::vector< Float_t >& TMVA::MethodKNN::GetRegressionValues()
432{
433 if( fRegressionReturnVal == 0 )
434 fRegressionReturnVal = new std::vector<Float_t>;
435 else
436 fRegressionReturnVal->clear();
437
438 //
439 // Define local variables
440 //
441 const Event *evt = GetEvent();
442 const Int_t nvar = GetNVariables();
443 const UInt_t knn = static_cast<UInt_t>(fnkNN);
444 std::vector<float> reg_vec;
445
446 kNN::VarVec vvec(static_cast<UInt_t>(nvar), 0.0);
447
448 for (Int_t ivar = 0; ivar < nvar; ++ivar) {
449 vvec[ivar] = evt->GetValue(ivar);
450 }
451
452 // search for fnkNN+2 nearest neighbors, pad with two
453 // events to avoid Monte-Carlo events with zero distance
454 // most of CPU time is spent in this recursive function
455 const kNN::Event event_knn(vvec, evt->GetWeight(), 3);
456 fModule->Find(event_knn, knn + 2);
457
458 const kNN::List &rlist = fModule->GetkNNList();
459 if (rlist.size() != knn + 2) {
460 Log() << kFATAL << "kNN result list is empty" << Endl;
461 return *fRegressionReturnVal;
462 }
463
464 // compute regression values
466 UInt_t count_all = 0;
467
468 for (kNN::List::const_iterator lit = rlist.begin(); lit != rlist.end(); ++lit) {
469
470 // get reference to current node to make code more readable
471 const kNN::Node<kNN::Event> &node = *(lit->first);
472 const kNN::VarVec &tvec = node.GetEvent().GetTargets();
473 const Double_t weight = node.GetEvent().GetWeight();
474
475 if (reg_vec.empty()) {
476 reg_vec= kNN::VarVec(tvec.size(), 0.0);
477 }
478
479 for(UInt_t ivar = 0; ivar < tvec.size(); ++ivar) {
480 if (fUseWeight) reg_vec[ivar] += tvec[ivar]*weight;
481 else reg_vec[ivar] += tvec[ivar];
482 }
483
484 if (fUseWeight) weight_all += weight;
485 else ++weight_all;
486
487 // use only fnkNN events
488 ++count_all;
489
490 if (count_all == knn) {
491 break;
492 }
493 }
494
495 // check that number of events matches number of k in knn
496 if (!(weight_all > 0.0)) {
497 Log() << kFATAL << "Total weight sum is not positive: " << weight_all << Endl;
498 return *fRegressionReturnVal;
499 }
500
501 for (UInt_t ivar = 0; ivar < reg_vec.size(); ++ivar) {
503 }
504
505 // copy result
506 fRegressionReturnVal->insert(fRegressionReturnVal->begin(), reg_vec.begin(), reg_vec.end());
507
508 return *fRegressionReturnVal;
509}
510
511////////////////////////////////////////////////////////////////////////////////
512/// no ranking available
513
515{
516 return 0;
517}
518
519////////////////////////////////////////////////////////////////////////////////
520/// write weights to XML
521
522void TMVA::MethodKNN::AddWeightsXMLTo( void* parent ) const {
523 void* wght = gTools().AddChild(parent, "Weights");
524 gTools().AddAttr(wght,"NEvents",fEvent.size());
525 if (fEvent.size()>0) gTools().AddAttr(wght,"NVar",fEvent.begin()->GetNVar());
526 if (fEvent.size()>0) gTools().AddAttr(wght,"NTgt",fEvent.begin()->GetNTgt());
527
528 for (kNN::EventVec::const_iterator event = fEvent.begin(); event != fEvent.end(); ++event) {
529
530 std::stringstream s("");
531 s.precision( 16 );
532 for (UInt_t ivar = 0; ivar < event->GetNVar(); ++ivar) {
533 if (ivar>0) s << " ";
534 s << std::scientific << event->GetVar(ivar);
535 }
536
537 for (UInt_t itgt = 0; itgt < event->GetNTgt(); ++itgt) {
538 s << " " << std::scientific << event->GetTgt(itgt);
539 }
540
541 void* evt = gTools().AddChild(wght, "Event", s.str().c_str());
542 gTools().AddAttr(evt,"Type", event->GetType());
543 gTools().AddAttr(evt,"Weight", event->GetWeight());
544 }
545}
546
547////////////////////////////////////////////////////////////////////////////////
548
550 void* ch = gTools().GetChild(wghtnode); // first event
551 UInt_t nvar = 0, ntgt = 0;
552 gTools().ReadAttr( wghtnode, "NVar", nvar );
553 gTools().ReadAttr( wghtnode, "NTgt", ntgt );
554
555
556 Short_t evtType(0);
558
559 while (ch) {
560 // build event
561 kNN::VarVec vvec(nvar, 0);
563
564 gTools().ReadAttr( ch, "Type", evtType );
565 gTools().ReadAttr( ch, "Weight", evtWeight );
566 std::stringstream s( gTools().GetContent(ch) );
567
568 for(UInt_t ivar=0; ivar<nvar; ivar++)
569 s >> vvec[ivar];
570
571 for(UInt_t itgt=0; itgt<ntgt; itgt++)
572 s >> tvec[itgt];
573
574 ch = gTools().GetNextChild(ch);
575
577 fEvent.push_back(event_knn);
578 }
579
580 // create kd-tree (binary tree) structure
581 MakeKNN();
582}
583
584////////////////////////////////////////////////////////////////////////////////
585/// read the weights
586
588{
589 Log() << kINFO << "Starting ReadWeightsFromStream(std::istream& is) function..." << Endl;
590
591 if (!fEvent.empty()) {
592 Log() << kINFO << "Erasing " << fEvent.size() << " previously stored events" << Endl;
593 fEvent.clear();
594 }
595
596 UInt_t nvar = 0;
597
598 while (is) {
599 std::string line;
600 std::getline(is, line);
601
602 if (line.empty() || line.find("#") != std::string::npos) {
603 continue;
604 }
605
606 UInt_t count = 0;
607 std::string::size_type pos=0;
608 while( (pos=line.find(',',pos)) != std::string::npos ) { count++; pos++; }
609
610 if (nvar == 0) {
611 nvar = count - 2;
612 }
613 if (count < 3 || nvar != count - 2) {
614 Log() << kFATAL << "Missing comma delimeter(s)" << Endl;
615 }
616
617 // Int_t ievent = -1;
618 Int_t type = -1;
619 Double_t weight = -1.0;
620
621 kNN::VarVec vvec(nvar, 0.0);
622
623 UInt_t vcount = 0;
624 std::string::size_type prev = 0;
625
626 for (std::string::size_type ipos = 0; ipos < line.size(); ++ipos) {
627 if (line[ipos] != ',' && ipos + 1 != line.size()) {
628 continue;
629 }
630
631 if (!(ipos > prev)) {
632 Log() << kFATAL << "Wrong substring limits" << Endl;
633 }
634
635 std::string vstring = line.substr(prev, ipos - prev);
636 if (ipos + 1 == line.size()) {
637 vstring = line.substr(prev, ipos - prev + 1);
638 }
639
640 if (vstring.empty()) {
641 Log() << kFATAL << "Failed to parse string" << Endl;
642 }
643
644 if (vcount == 0) {
645 // ievent = std::atoi(vstring.c_str());
646 }
647 else if (vcount == 1) {
648 type = std::atoi(vstring.c_str());
649 }
650 else if (vcount == 2) {
651 weight = std::atof(vstring.c_str());
652 }
653 else if (vcount - 3 < vvec.size()) {
654 vvec[vcount - 3] = std::atof(vstring.c_str());
655 }
656 else {
657 Log() << kFATAL << "Wrong variable count" << Endl;
658 }
659
660 prev = ipos + 1;
661 ++vcount;
662 }
663
664 fEvent.push_back(kNN::Event(vvec, weight, type));
665 }
666
667 Log() << kINFO << "Read " << fEvent.size() << " events from text file" << Endl;
668
669 // create kd-tree (binary tree) structure
670 MakeKNN();
671}
672
673////////////////////////////////////////////////////////////////////////////////
674/// save weights to ROOT file
675
677{
678 Log() << kINFO << "Starting WriteWeightsToStream(TFile &rf) function..." << Endl;
679
680 if (fEvent.empty()) {
681 Log() << kWARNING << "MethodKNN contains no events " << Endl;
682 return;
683 }
684
685 kNN::Event *event = new kNN::Event();
686 TTree *tree = new TTree("knn", "event tree");
687 tree->SetDirectory(nullptr);
688 tree->Branch("event", "TMVA::kNN::Event", &event);
689
690 Double_t size = 0.0;
691 for (kNN::EventVec::const_iterator it = fEvent.begin(); it != fEvent.end(); ++it) {
692 (*event) = (*it);
693 size += tree->Fill();
694 }
695
696 // !!! hard coded tree name !!!
697 rf.WriteTObject(tree, "knn", "Overwrite");
698
699 // scale to MegaBytes
700 size /= 1048576.0;
701
702 Log() << kINFO << "Wrote " << size << "MB and " << fEvent.size()
703 << " events to ROOT file" << Endl;
704
705 delete tree;
706 delete event;
707}
708
709////////////////////////////////////////////////////////////////////////////////
710/// read weights from ROOT file
711
713{
714 Log() << kINFO << "Starting ReadWeightsFromStream(TFile &rf) function..." << Endl;
715
716 if (!fEvent.empty()) {
717 Log() << kINFO << "Erasing " << fEvent.size() << " previously stored events" << Endl;
718 fEvent.clear();
719 }
720
721 // !!! hard coded tree name !!!
722 TTree *tree = dynamic_cast<TTree *>(rf.Get("knn"));
723 if (!tree) {
724 Log() << kFATAL << "Failed to find knn tree" << Endl;
725 return;
726 }
727
728 kNN::Event *event = new kNN::Event();
729 tree->SetBranchAddress("event", &event);
730
731 const Int_t nevent = tree->GetEntries();
732
733 Double_t size = 0.0;
734 for (Int_t i = 0; i < nevent; ++i) {
735 size += tree->GetEntry(i);
736 fEvent.push_back(*event);
737 }
738
739 // scale to MegaBytes
740 size /= 1048576.0;
741
742 Log() << kINFO << "Read " << size << "MB and " << fEvent.size()
743 << " events from ROOT file" << Endl;
744
745 delete event;
746
747 // create kd-tree (binary tree) structure
748 MakeKNN();
749}
750
751////////////////////////////////////////////////////////////////////////////////
752/// write specific classifier response
753
754void TMVA::MethodKNN::MakeClassSpecific( std::ostream& fout, const TString& className ) const
755{
756 fout << " // not implemented for class: \"" << className << "\"" << std::endl;
757 fout << "};" << std::endl;
758}
759
760////////////////////////////////////////////////////////////////////////////////
761/// get help message text
762///
763/// typical length of text line:
764/// "|--------------------------------------------------------------|"
765
767{
768 Log() << Endl;
769 Log() << gTools().Color("bold") << "--- Short description:" << gTools().Color("reset") << Endl;
770 Log() << Endl;
771 Log() << "The k-nearest neighbor (k-NN) algorithm is a multi-dimensional classification" << Endl
772 << "and regression algorithm. Similarly to other TMVA algorithms, k-NN uses a set of" << Endl
773 << "training events for which a classification category/regression target is known. " << Endl
774 << "The k-NN method compares a test event to all training events using a distance " << Endl
775 << "function, which is an Euclidean distance in a space defined by the input variables. "<< Endl
776 << "The k-NN method, as implemented in TMVA, uses a kd-tree algorithm to perform a" << Endl
777 << "quick search for the k events with shortest distance to the test event. The method" << Endl
778 << "returns a fraction of signal events among the k neighbors. It is recommended" << Endl
779 << "that a histogram which stores the k-NN decision variable is binned with k+1 bins" << Endl
780 << "between 0 and 1." << Endl;
781
782 Log() << Endl;
783 Log() << gTools().Color("bold") << "--- Performance tuning via configuration options: "
784 << gTools().Color("reset") << Endl;
785 Log() << Endl;
786 Log() << "The k-NN method estimates a density of signal and background events in a "<< Endl
787 << "neighborhood around the test event. The method assumes that the density of the " << Endl
788 << "signal and background events is uniform and constant within the neighborhood. " << Endl
789 << "k is an adjustable parameter and it determines an average size of the " << Endl
790 << "neighborhood. Small k values (less than 10) are sensitive to statistical " << Endl
791 << "fluctuations and large (greater than 100) values might not sufficiently capture " << Endl
792 << "local differences between events in the training set. The speed of the k-NN" << Endl
793 << "method also increases with larger values of k. " << Endl;
794 Log() << Endl;
795 Log() << "The k-NN method assigns equal weight to all input variables. Different scales " << Endl
796 << "among the input variables is compensated using ScaleFrac parameter: the input " << Endl
797 << "variables are scaled so that the widths for central ScaleFrac*100% events are " << Endl
798 << "equal among all the input variables." << Endl;
799
800 Log() << Endl;
801 Log() << gTools().Color("bold") << "--- Additional configuration options: "
802 << gTools().Color("reset") << Endl;
803 Log() << Endl;
804 Log() << "The method inclues an option to use a Gaussian kernel to smooth out the k-NN" << Endl
805 << "response. The kernel re-weights events using a distance to the test event." << Endl;
806}
807
808////////////////////////////////////////////////////////////////////////////////
809/// polynomial kernel
810
812{
814
815 if (!(avalue < 1.0)) {
816 return 0.0;
817 }
818
819 const Double_t prod = 1.0 - avalue * avalue * avalue;
820
821 return (prod * prod * prod);
822}
823
824////////////////////////////////////////////////////////////////////////////////
825/// Gaussian kernel
826
828 const kNN::Event &event, const std::vector<Double_t> &svec) const
829{
830 if (event_knn.GetNVar() != event.GetNVar() || event_knn.GetNVar() != svec.size()) {
831 Log() << kFATAL << "Mismatched vectors in Gaussian kernel function" << Endl;
832 return 0.0;
833 }
834
835 //
836 // compute exponent
837 //
838 double sum_exp = 0.0;
839
840 for(unsigned int ivar = 0; ivar < event_knn.GetNVar(); ++ivar) {
841
842 const Double_t diff_ = event.GetVar(ivar) - event_knn.GetVar(ivar);
843 const Double_t sigm_ = svec[ivar];
844 if (!(sigm_ > 0.0)) {
845 Log() << kFATAL << "Bad sigma value = " << sigm_ << Endl;
846 return 0.0;
847 }
848
849 sum_exp += diff_*diff_/(2.0*sigm_*sigm_);
850 }
851
852 //
853 // Return unnormalized(!) Gaussian function, because normalization
854 // cancels for the ratio of weights.
855 //
856
857 return std::exp(-sum_exp);
858}
859
860////////////////////////////////////////////////////////////////////////////////
861///
862/// Get polynomial kernel radius
863///
864
866{
867 Double_t kradius = -1.0;
868 UInt_t kcount = 0;
869 const UInt_t knn = static_cast<UInt_t>(fnkNN);
870
871 for (kNN::List::const_iterator lit = rlist.begin(); lit != rlist.end(); ++lit)
872 {
873 if (!(lit->second > 0.0)) continue;
874
875 if (kradius < lit->second || kradius < 0.0) kradius = lit->second;
876
877 ++kcount;
878 if (kcount >= knn) break;
879 }
880
881 return kradius;
882}
883
884////////////////////////////////////////////////////////////////////////////////
885///
886/// Get polynomial kernel radius
887///
888
889const std::vector<Double_t> TMVA::MethodKNN::getRMS(const kNN::List &rlist, const kNN::Event &event_knn) const
890{
891 std::vector<Double_t> rvec;
892 UInt_t kcount = 0;
893 const UInt_t knn = static_cast<UInt_t>(fnkNN);
894
895 for (kNN::List::const_iterator lit = rlist.begin(); lit != rlist.end(); ++lit)
896 {
897 if (!(lit->second > 0.0)) continue;
898
899 const kNN::Node<kNN::Event> *node_ = lit -> first;
900 const kNN::Event &event_ = node_-> GetEvent();
901
902 if (rvec.empty()) {
903 rvec.insert(rvec.end(), event_.GetNVar(), 0.0);
904 }
905 else if (rvec.size() != event_.GetNVar()) {
906 Log() << kFATAL << "Wrong number of variables, should never happen!" << Endl;
907 rvec.clear();
908 return rvec;
909 }
910
911 for(unsigned int ivar = 0; ivar < event_.GetNVar(); ++ivar) {
912 const Double_t diff_ = event_.GetVar(ivar) - event_knn.GetVar(ivar);
913 rvec[ivar] += diff_*diff_;
914 }
915
916 ++kcount;
917 if (kcount >= knn) break;
918 }
919
920 if (kcount < 1) {
921 Log() << kFATAL << "Bad event kcount = " << kcount << Endl;
922 rvec.clear();
923 return rvec;
924 }
925
926 for(unsigned int ivar = 0; ivar < rvec.size(); ++ivar) {
927 if (!(rvec[ivar] > 0.0)) {
928 Log() << kFATAL << "Bad RMS value = " << rvec[ivar] << Endl;
929 rvec.clear();
930 return rvec;
931 }
932
933 rvec[ivar] = std::abs(fSigmaFact)*std::sqrt(rvec[ivar]/kcount);
934 }
935
936 return rvec;
937}
938
939////////////////////////////////////////////////////////////////////////////////
940
942{
944
945 for (kNN::List::const_iterator lit = rlist.begin(); lit != rlist.end(); ++lit) {
946
947 // get reference to current node to make code more readable
948 const kNN::Node<kNN::Event> &node = *(lit->first);
949 const kNN::VarVec &tvec = node.GetEvent().GetVars();
950
951 if (node.GetEvent().GetType() == 1) { // signal type = 1
952 sig_vec.push_back(tvec);
953 }
954 else if (node.GetEvent().GetType() == 2) { // background type = 2
955 bac_vec.push_back(tvec);
956 }
957 else {
958 Log() << kFATAL << "Unknown type for training event" << Endl;
959 }
960 }
961
962 fLDA.Initialize(sig_vec, bac_vec);
963
964 return fLDA.GetProb(event_knn.GetVars(), 1);
965}
#define REGISTER_METHOD(CLASS)
for example
std::vector< std::vector< Float_t > > LDAEvents
Definition LDA.h:38
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
short Short_t
Signed Short integer 2 bytes (short)
Definition RtypesCore.h:54
constexpr Bool_t kFALSE
Definition RtypesCore.h:109
constexpr Bool_t kTRUE
Definition RtypesCore.h:108
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
Option_t Option_t option
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t type
const_iterator begin() const
const_iterator end() const
A file, usually with extension .root, that stores data and code in the form of serialized objects in ...
Definition TFile.h:130
Class that contains all the data information.
Definition DataSetInfo.h:62
Virtual base Class for all MVA method.
Definition MethodBase.h:82
virtual void DeclareCompatibilityOptions()
options that are used ONLY for the READER to ensure backward compatibility they are hence without any...
Analysis of k-nearest neighbor.
Definition MethodKNN.h:54
void MakeKNN(void)
create kNN
virtual ~MethodKNN(void)
destructor
const std::vector< Double_t > getRMS(const kNN::List &rlist, const kNN::Event &event_knn) const
Get polynomial kernel radius.
const Ranking * CreateRanking() override
no ranking available
void DeclareOptions() override
MethodKNN options.
MethodKNN(const TString &jobName, const TString &methodTitle, DataSetInfo &theData, const TString &theOption="KNN")
standard constructor
Definition MethodKNN.cxx:61
Double_t getKernelRadius(const kNN::List &rlist) const
Get polynomial kernel radius.
void Train(void) override
kNN training
double getLDAValue(const kNN::List &rlist, const kNN::Event &event_knn)
void ProcessOptions() override
process the options specified by the user
Double_t PolnKernel(Double_t value) const
polynomial kernel
void DeclareCompatibilityOptions() override
options that are used ONLY for the READER to ensure backward compatibility
void ReadWeightsFromStream(std::istream &istr) override
read the weights
void GetHelpMessage() const override
get help message text
void MakeClassSpecific(std::ostream &, const TString &) const override
write specific classifier response
Double_t GetMvaValue(Double_t *err=nullptr, Double_t *errUpper=nullptr) override
Compute classifier response.
Bool_t HasAnalysisType(Types::EAnalysisType type, UInt_t numberClasses, UInt_t numberTargets) override
FDA can handle classification with 2 classes and regression with one regression-target.
void Init(void) override
Initialization.
void WriteWeightsToStream(TFile &rf) const
save weights to ROOT file
Double_t GausKernel(const kNN::Event &event_knn, const kNN::Event &event, const std::vector< Double_t > &svec) const
Gaussian kernel.
void ReadWeightsFromXML(void *wghtnode) override
const std::vector< Float_t > & GetRegressionValues() override
Return vector of averages for target values of k-nearest neighbors.
void AddWeightsXMLTo(void *parent) const override
write weights to XML
Ranking for variables in method (implementation)
Definition Ranking.h:48
const TString & Color(const TString &)
human readable color strings
Definition Tools.cxx:803
void ReadAttr(void *node, const char *, T &value)
read attribute from xml
Definition Tools.h:329
void * GetChild(void *parent, const char *childname=nullptr)
get child node
Definition Tools.cxx:1125
void AddAttr(void *node, const char *, const T &value, Int_t precision=16)
add attribute to xml
Definition Tools.h:347
void * AddChild(void *parent, const char *childname, const char *content=nullptr, bool isRootNode=false)
add child node
Definition Tools.cxx:1099
void * GetNextChild(void *prevchild, const char *childname=nullptr)
XML helpers.
Definition Tools.cxx:1137
Singleton class for Global types used by TMVA.
Definition Types.h:71
@ kClassification
Definition Types.h:127
@ kRegression
Definition Types.h:128
UInt_t GetNVar() const
Definition ModulekNN.h:188
This file contains binary tree and global function template that searches tree for k-nearest neigbors...
Definition NodekNN.h:68
Double_t GetWeight() const
Definition NodekNN.h:181
const T & GetEvent() const
Definition NodekNN.h:157
std::list< Elem > List
Definition ModulekNN.h:99
std::vector< VarType > VarVec
Definition ModulekNN.h:57
virtual void Clear(Option_t *="")
Definition TObject.h:127
Basic string class.
Definition TString.h:138
A TTree represents a columnar dataset.
Definition TTree.h:89
virtual Int_t Fill()
Fill all branches.
Definition TTree.cxx:4653
virtual Int_t SetBranchAddress(const char *bname, void *add, TBranch **ptr, TClass *realClass, EDataType datatype, bool isptr, bool suppressMissingBranchError)
Definition TTree.cxx:8675
virtual Int_t GetEntry(Long64_t entry, Int_t getall=0)
Read all branches of entry and return total number of bytes read.
Definition TTree.cxx:5718
virtual void SetDirectory(TDirectory *dir)
Change the tree's directory.
Definition TTree.cxx:9220
virtual Long64_t GetEntries() const
Definition TTree.h:510
TBranch * Branch(const char *name, T *obj, Int_t bufsize=32000, Int_t splitlevel=99)
Add a new branch, and infer the data type from the type of obj being passed.
Definition TTree.h:397
TLine * line
create variable transformations
Tools & gTools()
MsgLogger & Endl(MsgLogger &ml)
Definition MsgLogger.h:148
Double_t Sqrt(Double_t x)
Returns the square root of x.
Definition TMath.h:675
Short_t Abs(Short_t d)
Returns the absolute value of parameter Short_t d.
Definition TMathBase.h:122