GestureRecognitionToolkit  Version: 1.0 Revision: 04-03-15
The Gesture Recognition Toolkit (GRT) is a cross-platform, open-source, c++ machine learning library for real-time gesture recognition.
DecisionStump.cpp
Go to the documentation of this file.
1 
28 #include "DecisionStump.h"
29 
30 using namespace GRT;
31 
32 //Register the DecisionStump module with the WeakClassifier base class
34 
35 DecisionStump::DecisionStump(const UINT numRandomSplits){
36  this->numRandomSplits = numRandomSplits;
37  trained = false;
40  decisionValue = 0;
41  direction = 0;
42  weakClassifierType = "DecisionStump";
43  trainingLog.setProceedingText("[TRAINING DecisionStump]");
44  warningLog.setProceedingText("[WARNING DecisionStump]");
45  errorLog.setProceedingText("[ERROR DecisionStump]");
46 }
47 
49 
50 }
51 
53  *this = rhs;
54 }
55 
57  if( this != &rhs ){
59  this->decisionValue = rhs.decisionValue;
60  this->direction = rhs.direction;
61  this->numRandomSplits = rhs.numRandomSplits;
62  this->copyBaseVariables( &rhs );
63  }
64  return *this;
65 }
66 
67 bool DecisionStump::deepCopyFrom(const WeakClassifier *weakClassifer){
68  if( weakClassifer == NULL ) return false;
69 
70  if( this->getWeakClassifierType() == weakClassifer->getWeakClassifierType() ){
71  *this = *(DecisionStump*)weakClassifer;
72  return true;
73  }
74  return false;
75 }
76 
77 bool DecisionStump::train(ClassificationData &trainingData, VectorDouble &weights){
78 
79  trained = false;
80  numInputDimensions = trainingData.getNumDimensions();
81 
82  //There should only be two classes in the dataset, the positive class (classLable==1) and the negative class (classLabel==2)
83  if( trainingData.getNumClasses() != 2 ){
84  errorLog << "train(ClassificationData &trainingData, VectorDouble &weights) - There should only be 2 classes in the training data, but there are : " << trainingData.getNumClasses() << endl;
85  return false;
86  }
87 
88  //There should be one weight for every training sample
89  if( trainingData.getNumSamples() != weights.size() ){
90  errorLog << "train(ClassificationData &trainingData, VectorDouble &weights) - There number of examples in the training data (" << trainingData.getNumSamples() << ") does not match the lenght of the weights vector (" << weights.size() << ")" << endl;
91  return false;
92  }
93 
94  //Pick the training sample to use as the stump feature
95  const UINT M = trainingData.getNumSamples();
96  UINT bestFeatureIndex = 0;
97  vector< MinMax > ranges = trainingData.getRanges();
98  double minError = numeric_limits<double>::max();
99  double minRange = 0;
100  double maxRange = 0;
101  double step = 0;
102  double threshold = 0;
103  double bestThreshold = 0;
104  Random random;
105 
106  for(UINT k=0; k<numRandomSplits; k++){
107 
108  //Randomly select a feature and a threshold
109  UINT n = random.getRandomNumberInt(0,numInputDimensions);
110  minRange = ranges[n].minValue;
111  maxRange = ranges[n].maxValue;
112  threshold = random.getRandomNumberUniform( minRange, maxRange );
113 
114  //Compute the error using the current threshold on the current input dimension
115  //We need to check both sides of the threshold
116  double rhsError = 0;
117  double lhsError = 0;
118  for(UINT i=0; i<M; i++){
119  bool positiveClass = trainingData[ i ].getClassLabel() == WEAK_CLASSIFIER_POSITIVE_CLASS_LABEL;
120  bool rhs = trainingData[ i ][ n ] >= threshold;
121  bool lhs = trainingData[ i ][ n ] <= threshold;
122  if( (rhs && !positiveClass) || (!rhs && positiveClass) ) rhsError += weights[ i ];
123  if( (lhs && !positiveClass) || (!lhs && positiveClass) ) lhsError += weights[ i ];
124  }
125 
126  //Check to see if either the rhsError or lhsError beats the minError, if so then store the results
127  if( rhsError < minError ){
128  minError = rhsError;
129  bestFeatureIndex = n;
130  bestThreshold = threshold;
131  direction = 1; //1 means rhs
132  }
133  if( lhsError < minError ){
134  minError = lhsError;
135  bestFeatureIndex = n;
136  bestThreshold = threshold;
137  direction = 0; //0 means lhs
138  }
139 
140  }
141 
142  decisionFeatureIndex = bestFeatureIndex;
143  decisionValue = bestThreshold;
144  trained = true;
145 
146  trainingLog << "Best Feature Index: " << decisionFeatureIndex << " Value: " << decisionValue << " Direction: " << direction << " Error: " << minError << endl;
147  return true;
148 }
149 
150 double DecisionStump::predict(const VectorDouble &x){
151  if( direction == 1){
152  if( x[ decisionFeatureIndex ] >= decisionValue ) return 1;
153  }else if( x[ decisionFeatureIndex ] <= decisionValue ) return 1;
154  return -1;
155 }
156 
157 bool DecisionStump::saveModelToFile(fstream &file) const{
158 
159  if(!file.is_open())
160  {
161  errorLog <<"saveModelToFile(fstream &file) - The file is not open!" << endl;
162  return false;
163  }
164 
165  //Write the WeakClassifierType data
166  file << "WeakClassifierType: " << weakClassifierType << endl;
167  file << "Trained: "<< trained << endl;
168  file << "NumInputDimensions: " << numInputDimensions << endl;
169 
170  //Write the DecisionStump data
171  file << "DecisionFeatureIndex: " << decisionFeatureIndex << endl;
172  file << "Direction: "<< direction << endl;
173  file << "NumRandomSplits: " << numRandomSplits << endl;
174  file << "DecisionValue: " << decisionValue << endl;
175 
176  //We don't need to close the file as the function that called this function should handle that
177  return true;
178 }
179 
181 
182  if(!file.is_open())
183  {
184  errorLog <<"loadModelFromFile(fstream &file) - The file is not open!" << endl;
185  return false;
186  }
187 
188  string word;
189 
190  file >> word;
191  if( word != "WeakClassifierType:" ){
192  errorLog <<"loadModelFromFile(fstream &file) - Failed to read WeakClassifierType header!" << endl;
193  return false;
194  }
195  file >> word;
196 
197  if( word != weakClassifierType ){
198  errorLog <<"loadModelFromFile(fstream &file) - The weakClassifierType:" << word << " does not match: " << weakClassifierType << endl;
199  return false;
200  }
201 
202  file >> word;
203  if( word != "Trained:" ){
204  errorLog <<"loadModelFromFile(fstream &file) - Failed to read Trained header!" << endl;
205  return false;
206  }
207  file >> trained;
208 
209  file >> word;
210  if( word != "NumInputDimensions:" ){
211  errorLog <<"loadModelFromFile(fstream &file) - Failed to read NumInputDimensions header!" << endl;
212  return false;
213  }
214  file >> numInputDimensions;
215 
216  file >> word;
217  if( word != "DecisionFeatureIndex:" ){
218  errorLog <<"loadModelFromFile(fstream &file) - Failed to read DecisionFeatureIndex header!" << endl;
219  return false;
220  }
221  file >> decisionFeatureIndex;
222 
223  file >> word;
224  if( word != "Direction:" ){
225  errorLog <<"loadModelFromFile(fstream &file) - Failed to read Direction header!" << endl;
226  return false;
227  }
228  file >> direction;
229 
230  file >> word;
231  if( word != "NumRandomSplits:" ){
232  errorLog <<"loadModelFromFile(fstream &file) - Failed to read NumRandomSplits header!" << endl;
233  return false;
234  }
235  file >> numRandomSplits;
236 
237  file >> word;
238  if( word != "DecisionValue:" ){
239  errorLog <<"loadModelFromFile(fstream &file) - Failed to read DecisionValue header!" << endl;
240  return false;
241  }
242  file >> decisionValue;
243 
244  //We don't need to close the file as the function that called this function should handle that
245  return true;
246 }
247 
248 void DecisionStump::print() const{
249  cout << "Trained: " << trained;
250  cout << "\tDecisionValue: " << decisionValue;
251  cout << "\tDecisionFeatureIndex: " << decisionFeatureIndex;
252  cout << "\tDirection: " << direction << endl;
253 }
254 
256  return decisionFeatureIndex;
257 }
258 
260  return direction;
261 }
262 
264  return numRandomSplits;
265 }
266 
268  return decisionValue;
269 }
270 
UINT numRandomSplits
The number of random splits used to search for the best decision spilt.
UINT getNumRandomSplits() const
DecisionStump & operator=(const DecisionStump &rhs)
UINT numInputDimensions
The number of input dimensions to the weak classifier.
virtual ~DecisionStump()
virtual void print() const
virtual bool saveModelToFile(fstream &file) const
Definition: AdaBoost.cpp:25
string weakClassifierType
A string that represents the weak classifier type, e.g. DecisionStump.
static RegisterWeakClassifierModule< DecisionStump > registerModule
This is used to register the DecisionStump with the WeakClassifier base class.
virtual double predict(const VectorDouble &x)
DecisionStump(const UINT numRandomSplits=100)
UINT decisionFeatureIndex
The dimension that the data will be spilt on.
string getWeakClassifierType() const
int getRandomNumberInt(int minRange, int maxRange)
Definition: Random.h:87
double getRandomNumberUniform(double minRange=0.0, double maxRange=1.0)
Definition: Random.h:197
virtual bool train(ClassificationData &trainingData, VectorDouble &weights)
UINT getDirection() const
bool trained
A flag to show if the weak classifier model has been trained.
UINT getDecisionFeatureIndex() const
vector< MinMax > getRanges() const
bool copyBaseVariables(const WeakClassifier *weakClassifer)
UINT direction
Indicates if the decision spilt threshold is greater than (1), or less than (0)
virtual bool loadModelFromFile(fstream &file)
virtual bool deepCopyFrom(const WeakClassifier *weakClassifer)
This class implements a DecisionStump, which is a single node of a DecisionTree.
double decisionValue
The decision spilt threshold.
double getDecisionValue() const