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.
LinearRegression.cpp
1 /*
2 GRT MIT License
3 Copyright (c) <2012> <Nicholas Gillian, Media Lab, MIT>
4 
5 Permission is hereby granted, free of charge, to any person obtaining a copy of this software
6 and associated documentation files (the "Software"), to deal in the Software without restriction,
7 including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
9 subject to the following conditions:
10 
11 The above copyright notice and this permission notice shall be included in all copies or substantial
12 portions of the Software.
13 
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
15 LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
17 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
18 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 */
20 
21 #define GRT_DLL_EXPORTS
22 #include "LinearRegression.h"
23 
24 GRT_BEGIN_NAMESPACE
25 
26 //Register the LinearRegression module with the Classifier base class
27 RegisterRegressifierModule< LinearRegression > LinearRegression::registerModule("LinearRegression");
28 
30 {
31  this->useScaling = useScaling;
32  minChange = 1.0e-5;
33  maxNumEpochs = 500;
34  learningRate = 0.01;
35  classType = "LinearRegression";
36  regressifierType = classType;
37  debugLog.setProceedingText("[DEBUG LinearRegression]");
38  errorLog.setProceedingText("[ERROR LinearRegression]");
39  trainingLog.setProceedingText("[TRAINING LinearRegression]");
40  warningLog.setProceedingText("[WARNING LinearRegression]");
41 }
42 
44 {
45 }
46 
48  if( this != &rhs ){
49  this->w0 = rhs.w0;
50  this->w = rhs.w;
51 
52  //Copy the base variables
54  }
55  return *this;
56 }
57 
58 bool LinearRegression::deepCopyFrom(const Regressifier *regressifier){
59 
60  if( regressifier == NULL ) return false;
61 
62  if( this->getRegressifierType() == regressifier->getRegressifierType() ){
63 
64  const LinearRegression *ptr = dynamic_cast<const LinearRegression*>(regressifier);
65 
66  this->w0 = ptr->w0;
67  this->w = ptr->w;
68 
69  //Copy the base variables
70  return copyBaseVariables( regressifier );
71  }
72  return false;
73 }
74 
76 
77  const unsigned int M = trainingData.getNumSamples();
78  const unsigned int N = trainingData.getNumInputDimensions();
79  const unsigned int K = trainingData.getNumTargetDimensions();
80  trained = false;
81  trainingResults.clear();
82 
83  if( M == 0 ){
84  errorLog << "train_(RegressionData &trainingData) - Training data has zero samples!" << std::endl;
85  return false;
86  }
87 
88  if( K == 0 ){
89  errorLog << "train_(RegressionData &trainingData) - The number of target dimensions is not 1!" << std::endl;
90  return false;
91  }
92 
93  numInputDimensions = N;
94  numOutputDimensions = 1; //Logistic Regression will have 1 output
95  inputVectorRanges.clear();
96  targetVectorRanges.clear();
97 
98  //Scale the training and validation data, if needed
99  if( useScaling ){
100  //Find the ranges for the input data
101  inputVectorRanges = trainingData.getInputRanges();
102 
103  //Find the ranges for the target data
104  targetVectorRanges = trainingData.getTargetRanges();
105 
106  //Scale the training data
107  trainingData.scale(inputVectorRanges,targetVectorRanges,0.0,1.0);
108  }
109 
110  //Reset the weights
111  Random rand;
112  w0 = rand.getRandomNumberUniform(-0.1,0.1);
113  w.resize(N);
114  for(UINT j=0; j<N; j++){
115  w[j] = rand.getRandomNumberUniform(-0.1,0.1);
116  }
117 
118  Float error = 0;
119  Float lastError = 0;
120  Float delta = 0;
121  UINT iter = 0;
122  bool keepTraining = true;
123  Vector< UINT > randomTrainingOrder(M);
124  TrainingResult result;
125  trainingResults.reserve(M);
126 
127  //In most cases, the training data is grouped into classes (100 samples for class 1, followed by 100 samples for class 2, etc.)
128  //This can cause a problem for stochastic gradient descent algorithm. To avoid this issue, we randomly shuffle the order of the
129  //training samples. This random order is then used at each epoch.
130  for(UINT i=0; i<M; i++){
131  randomTrainingOrder[i] = i;
132  }
133  std::random_shuffle(randomTrainingOrder.begin(), randomTrainingOrder.end());
134 
135  //Run the main stochastic gradient descent training algorithm
136  while( keepTraining ){
137 
138  //Run one epoch of training using stochastic gradient descent
139  totalSquaredTrainingError = 0;
140  for(UINT m=0; m<M; m++){
141 
142  //Select the random sample
143  UINT i = randomTrainingOrder[m];
144 
145  //Compute the error, given the current weights
146  VectorFloat x = trainingData[i].getInputVector();
147  VectorFloat y = trainingData[i].getTargetVector();
148  Float h = w0;
149  for(UINT j=0; j<N; j++){
150  h += x[j] * w[j];
151  }
152  error = y[0] - h;
153  totalSquaredTrainingError += SQR( error );
154 
155  //Update the weights
156  for(UINT j=0; j<N; j++){
157  w[j] += learningRate * error * x[j];
158  }
159  w0 += learningRate * error;
160  }
161 
162  //Compute the error
163  delta = fabs( totalSquaredTrainingError-lastError );
164  lastError = totalSquaredTrainingError;
165 
166  //Check to see if we should stop
167  if( delta <= minChange ){
168  keepTraining = false;
169  }
170 
171  if( grt_isinf( totalSquaredTrainingError ) || grt_isnan( totalSquaredTrainingError ) ){
172  errorLog << "train_(RegressionData &trainingData) - Training failed! Total squared training error is NAN. If scaling is not enabled then you should try to scale your data and see if this solves the issue." << std::endl;
173  return false;
174  }
175 
176  if( ++iter >= maxNumEpochs ){
177  keepTraining = false;
178  }
179 
180  //Store the training results
181  rootMeanSquaredTrainingError = sqrt( totalSquaredTrainingError / Float(M) );
182  result.setRegressionResult(iter,totalSquaredTrainingError,rootMeanSquaredTrainingError,this);
183  trainingResults.push_back( result );
184 
185  //Notify any observers of the new result
186  trainingResultsObserverManager.notifyObservers( result );
187 
188  trainingLog << "Epoch: " << iter << " SSE: " << totalSquaredTrainingError << " Delta: " << delta << std::endl;
189  }
190 
191  //Flag that the algorithm has been trained
192  regressionData.resize(1,0);
193  trained = true;
194  return trained;
195 }
196 
198 
199  if( !trained ){
200  errorLog << "predict_(VectorFloat &inputVector) - Model Not Trained!" << std::endl;
201  return false;
202  }
203 
204  if( !trained ) return false;
205 
206  if( inputVector.size() != numInputDimensions ){
207  errorLog << "predict_(VectorFloat &inputVector) - The size of the input Vector (" << int( inputVector.size() ) << ") does not match the num features in the model (" << numInputDimensions << std::endl;
208  return false;
209  }
210 
211  if( useScaling ){
212  for(UINT n=0; n<numInputDimensions; n++){
213  inputVector[n] = scale(inputVector[n], inputVectorRanges[n].minValue, inputVectorRanges[n].maxValue, 0, 1);
214  }
215  }
216 
217  regressionData[0] = w0;
218  for(UINT j=0; j<numInputDimensions; j++){
219  regressionData[0] += inputVector[j] * w[j];
220  }
221 
222  if( useScaling ){
223  for(UINT n=0; n<numOutputDimensions; n++){
224  regressionData[n] = scale(regressionData[n], 0, 1, targetVectorRanges[n].minValue, targetVectorRanges[n].maxValue);
225  }
226  }
227 
228  return true;
229 }
230 
231 bool LinearRegression::save( std::fstream &file ) const{
232 
233  if(!file.is_open())
234  {
235  errorLog << "save(fstream &file) - The file is not open!" << std::endl;
236  return false;
237  }
238 
239  //Write the header info
240  file<<"GRT_LINEAR_REGRESSION_MODEL_FILE_V2.0\n";
241 
242  //Write the regressifier settings to the file
244  errorLog <<"save(fstream &file) - Failed to save Regressifier base settings to file!" << std::endl;
245  return false;
246  }
247 
248  if( trained ){
249  file << "Weights: ";
250  file << w0;
251  for(UINT j=0; j<numInputDimensions; j++){
252  file << " " << w[j];
253  }
254  file << std::endl;
255  }
256 
257  return true;
258 }
259 
260 bool LinearRegression::load( std::fstream &file ){
261 
262  clear();
263 
264  if(!file.is_open())
265  {
266  errorLog << "load( fstream &file ) - Could not open file to load model" << std::endl;
267  return false;
268  }
269 
270  std::string word;
271 
272  //Find the file type header
273  file >> word;
274 
275  //Check to see if we should load a legacy file
276  if( word == "GRT_LINEAR_REGRESSION_MODEL_FILE_V1.0" ){
277  return loadLegacyModelFromFile( file );
278  }
279 
280  if( word != "GRT_LINEAR_REGRESSION_MODEL_FILE_V2.0" ){
281  errorLog << "load( fstream &file ) - Could not find Model File Header" << std::endl;
282  return false;
283  }
284 
285  //Load the regressifier settings from the file
287  errorLog <<"load( fstream &file ) - Failed to save Regressifier base settings to file!" << std::endl;
288  return false;
289  }
290 
291  if( trained ){
292 
293  //Resize the weights
294  w.resize(numInputDimensions);
295 
296  //Load the weights
297  file >> word;
298  if(word != "Weights:"){
299  errorLog << "load( fstream &file ) - Could not find the Weights!" << std::endl;
300  return false;
301  }
302 
303  file >> w0;
304  for(UINT j=0; j<numInputDimensions; j++){
305  file >> w[j];
306 
307  }
308  }
309 
310  return true;
311 }
312 
313 bool LinearRegression::setMaxNumIterations(const UINT maxNumIterations){
314  return setMaxNumEpochs( maxNumIterations );
315 }
316 
318  return getMaxNumEpochs();
319 }
320 
321 bool LinearRegression::loadLegacyModelFromFile( std::fstream &file ){
322 
323  std::string word;
324 
325  file >> word;
326  if(word != "NumFeatures:"){
327  errorLog << "loadLegacyModelFromFile( fstream &file ) - Could not find NumFeatures!" << std::endl;
328  return false;
329  }
330  file >> numInputDimensions;
331 
332  file >> word;
333  if(word != "NumOutputDimensions:"){
334  errorLog << "loadLegacyModelFromFile( fstream &file ) - Could not find NumOutputDimensions!" << std::endl;
335  return false;
336  }
337  file >> numOutputDimensions;
338 
339  file >> word;
340  if(word != "UseScaling:"){
341  errorLog << "loadLegacyModelFromFile( fstream &file ) - Could not find UseScaling!" << std::endl;
342  return false;
343  }
344  file >> useScaling;
345 
347  if( useScaling ){
348  //Resize the ranges buffer
349  inputVectorRanges.resize(numInputDimensions);
350  targetVectorRanges.resize(numOutputDimensions);
351 
352  //Load the ranges
353  file >> word;
354  if(word != "InputVectorRanges:"){
355  file.close();
356  errorLog << "loadLegacyModelFromFile( fstream &file ) - Failed to find InputVectorRanges!" << std::endl;
357  return false;
358  }
359  for(UINT j=0; j<inputVectorRanges.size(); j++){
360  file >> inputVectorRanges[j].minValue;
361  file >> inputVectorRanges[j].maxValue;
362  }
363 
364  file >> word;
365  if(word != "OutputVectorRanges:"){
366  file.close();
367  errorLog << "loadLegacyModelFromFile( fstream &file ) - Failed to find OutputVectorRanges!" << std::endl;
368  return false;
369  }
370  for(UINT j=0; j<targetVectorRanges.size(); j++){
371  file >> targetVectorRanges[j].minValue;
372  file >> targetVectorRanges[j].maxValue;
373  }
374  }
375 
376  //Resize the weights
377  w.resize(numInputDimensions);
378 
379  //Load the weights
380  file >> word;
381  if(word != "Weights:"){
382  errorLog << "loadLegacyModelFromFile( fstream &file ) - Could not find the Weights!" << std::endl;
383  return false;
384  }
385 
386  file >> w0;
387  for(UINT j=0; j<numInputDimensions; j++){
388  file >> w[j];
389 
390  }
391 
392  //Resize the regression data Vector
393  regressionData.resize(1,0);
394 
395  //Flag that the model has been trained
396  trained = true;
397 
398  return true;
399 }
400 
401 GRT_END_NAMESPACE
This class implements the Linear Regression algorithm. Linear Regression is a simple but effective re...
Float scale(const Float &x, const Float &minSource, const Float &maxSource, const Float &minTarget, const Float &maxTarget, const bool constrain=false)
Definition: MLBase.h:353
virtual bool predict_(VectorFloat &inputVector)
LinearRegression(bool useScaling=false)
Vector< MinMax > getInputRanges() const
Definition: Random.h:40
virtual bool resize(const unsigned int size)
Definition: Vector.h:133
virtual ~LinearRegression(void)
bool copyBaseVariables(const Regressifier *regressifier)
UINT getNumInputDimensions() const
Vector< MinMax > getTargetRanges() const
bool saveBaseSettingsToFile(std::fstream &file) const
bool scale(const Float minTarget, const Float maxTarget)
UINT getMaxNumEpochs() const
Definition: MLBase.cpp:227
UINT getNumTargetDimensions() const
std::string getRegressifierType() const
virtual bool save(std::fstream &file) const
bool loadBaseSettingsFromFile(std::fstream &file)
LinearRegression & operator=(const LinearRegression &rhs)
bool setMaxNumIterations(const UINT maxNumIterations)
virtual bool load(std::fstream &file)
virtual bool train_(RegressionData &trainingData)
Float getRandomNumberUniform(Float minRange=0.0, Float maxRange=1.0)
Definition: Random.h:198
virtual bool deepCopyFrom(const Regressifier *regressifier)
bool loadLegacyModelFromFile(std::fstream &file)
bool setMaxNumEpochs(const UINT maxNumEpochs)
Definition: MLBase.cpp:273
UINT getMaxNumIterations() const
UINT getNumSamples() const
virtual bool clear()