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