GestureRecognitionToolkit  Version: 0.2.5
The Gesture Recognition Toolkit (GRT) is a cross-platform, open-source, c++ machine learning library for real-time gesture recognition.
HMM.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 "HMM.h"
23 
24 GRT_BEGIN_NAMESPACE
25 
26 //Define the string that will be used to identify the object
27 const std::string HMM::id = "HMM";
28 std::string HMM::getId() { return HMM::id; }
29 
30 //Register the HMM with the classifier base type
31 RegisterClassifierModule< HMM > HMM::registerModule( HMM::getId() );
32 
33 HMM::HMM(const UINT hmmType,const UINT modelType,const UINT delta,const bool useScaling,const bool useNullRejection) : Classifier( HMM::getId() )
34 {
35  this->hmmType = hmmType;
36  this->modelType = modelType;
37  this->delta = delta;
38  this->useScaling = useScaling;
39  this->useNullRejection = useNullRejection;
40 
41  //Default discrete setup
42  numStates = 10;
43  numSymbols = 20;
44  maxNumEpochs = 1000;
45  minChange = 1.0e-5;
46 
47  //Default continuous setup
48  downsampleFactor = 5;
49  committeeSize = 5;
50  sigma = 10.0;
51  autoEstimateSigma = true;
52 
53  supportsNullRejection = false; //TODO - need to add better null rejection support
54  classifierMode = TIMESERIES_CLASSIFIER_MODE;
55 }
56 
57 HMM::HMM(const HMM &rhs) : Classifier( HMM::getId() )
58 {
59  classifierMode = TIMESERIES_CLASSIFIER_MODE;
60  *this = rhs;
61 }
62 
63 HMM::~HMM(void)
64 {
65 }
66 
67 HMM& HMM::operator=(const HMM &rhs){
68  if( this != &rhs ){
69  this->hmmType = rhs.hmmType;
70  this->modelType = rhs.modelType;
71  this->delta = rhs.delta;
72  this->numStates = rhs.numStates;
73  this->numSymbols = rhs.numSymbols;
74  this->downsampleFactor = rhs.downsampleFactor;
75  this->committeeSize = rhs.committeeSize;
76  this->sigma = rhs.sigma;
77  this->autoEstimateSigma = rhs.autoEstimateSigma;
78  this->discreteModels = rhs.discreteModels;
79  this->continuousModels = rhs.continuousModels;
80 
81  copyBaseVariables( (Classifier*)&rhs );
82  }
83  return *this;
84 
85 }
86 
87 bool HMM::deepCopyFrom(const Classifier *classifier){
88 
89  if( classifier == NULL ) return false;
90 
91  if( this->getId() == classifier->getId() ){
92 
93  //Cast the classifier pointer to an HMM pointer
94  const HMM *ptr = dynamic_cast<const HMM*>(classifier);
95 
96  //Copy the HMM variables
97  this->hmmType = ptr->hmmType;
98  this->modelType = ptr->modelType;
99  this->delta = ptr->delta;
100  this->numStates = ptr->numStates;
101  this->numSymbols = ptr->numSymbols;
102  this->downsampleFactor = ptr->downsampleFactor;
103  this->committeeSize = ptr->committeeSize;
104  this->sigma = ptr->sigma;
105  this->autoEstimateSigma = ptr->autoEstimateSigma;
106  this->discreteModels = ptr->discreteModels;
107  this->continuousModels = ptr->continuousModels;
108 
109  //Copy the base variables
110  return copyBaseVariables( classifier );
111  }
112  return false;
113 }
114 
115 bool HMM::train(ClassificationData trainingData){
116  errorLog << __GRT_LOG__ << " The HMM classifier should be trained using the train(TimeSeriesClassificationData &trainingData) method" << std::endl;
117  return false;
118 }
119 
120 
122 
123  switch( hmmType ){
124  case HMM_DISCRETE:
125  return train_discrete( trainingData );
126  break;
127  case HMM_CONTINUOUS:
128  return train_continuous( trainingData );
129  break;
130  }
131 
132  errorLog << __GRT_LOG__ << " Failed to train model, unknown HMM type!" << std::endl;
133 
134  return false;
135 }
136 
137 bool HMM::train_discrete(TimeSeriesClassificationData &trainingData){
138 
139  clear();
140 
141  if( trainingData.getNumSamples() == 0 ){
142  errorLog << __GRT_LOG__ << " There are no training samples to train the HMM classifer!" << std::endl;
143  return false;
144  }
145 
146  if( trainingData.getNumDimensions() != 1 ){
147  errorLog << __GRT_LOG__ << " The number of dimensions in the training data must be 1. If your training data is not 1 dimensional then you must quantize the training data using one of the GRT quantization algorithms" << std::endl;
148  return false;
149  }
150 
151  //Reset the HMM
152  numInputDimensions = trainingData.getNumDimensions();
153  numClasses = trainingData.getNumClasses();
154  discreteModels.resize( numClasses );
155  classLabels.resize( numClasses );
156 
157  //Init the models
158  for(UINT k=0; k<numClasses; k++){
159  discreteModels[k].resetModel(numStates,numSymbols,modelType,delta);
160  discreteModels[k].setMaxNumEpochs( maxNumEpochs );
161  discreteModels[k].setMinChange( minChange );
162  }
163 
164  //Train each of the models
165  for(UINT k=0; k<numClasses; k++){
166  //Get the class ID of this gesture
167  UINT classID = trainingData.getClassTracker()[k].classLabel;
168  classLabels[k] = classID;
169 
170  //Convert this classes training data into a list of observation sequences
171  TimeSeriesClassificationData classData = trainingData.getClassData( classID );
172  Vector< Vector< UINT > > observationSequences;
173  if( !convertDataToObservationSequence( classData, observationSequences ) ){
174  return false;
175  }
176 
177  //Train the model
178  if( !discreteModels[k].train( observationSequences ) ){
179  errorLog << __GRT_LOG__ << " Failed to train HMM for class " << classID << std::endl;
180  return false;
181  }
182  }
183 
184  //Compute the rejection thresholds
185  nullRejectionThresholds.resize(numClasses);
186 
187  for(UINT k=0; k<numClasses; k++){
188  //Get the class ID of this gesture
189  UINT classID = trainingData.getClassTracker()[k].classLabel;
190  classLabels[k] = classID;
191 
192  //Convert this classes training data into a list of observation sequences
193  TimeSeriesClassificationData classData = trainingData.getClassData( classID );
194  Vector< Vector< UINT > > observationSequences;
195  if( !convertDataToObservationSequence( classData, observationSequences ) ){
196  return false;
197  }
198 
199  //Test the model
200  Float loglikelihood = 0;
201  Float avgLoglikelihood = 0;
202  for(UINT i=0; i<observationSequences.size(); i++){
203  loglikelihood = discreteModels[k].predict( observationSequences[i] );
204  avgLoglikelihood += fabs( loglikelihood );
205  }
206  nullRejectionThresholds[k] = -( avgLoglikelihood / Float( observationSequences.size() ) );
207  }
208 
209  //Flag that the model has been trained
210  trained = true;
211  converged = true;
212 
213  return true;
214 
215 }
216 
217 bool HMM::train_continuous(TimeSeriesClassificationData &trainingData){
218 
219  clear();
220 
221  if( trainingData.getNumSamples() == 0 ){
222  errorLog << __GRT_LOG__ << " There are no training samples to train the CHMM classifer!" << std::endl;
223  return false;
224  }
225 
226  //Reset the CHMM
227  numInputDimensions = trainingData.getNumDimensions();
228  numClasses = trainingData.getNumClasses();
229  classLabels.resize( numClasses );
230  for(UINT k=0; k<numClasses; k++){
231  classLabels[k] = trainingData.getClassTracker()[k].classLabel;
232  }
233 
234  //Scale the training data if needed
235  ranges = trainingData.getRanges();
236  if( useScaling ){
237  trainingData.scale(0, 1);
238  }
239 
240  //Setup the models, there will be 1 model for each training sample
241  const UINT numTrainingSamples = trainingData.getNumSamples();
242  continuousModels.resize( numTrainingSamples );
243 
244  //Train each of the models
245  for(UINT k=0; k<numTrainingSamples; k++){
246 
247  //Init the model
248  continuousModels[k].setDownsampleFactor( downsampleFactor );
249  continuousModels[k].setModelType( modelType );
250  continuousModels[k].setDelta( delta );
251  continuousModels[k].setSigma( sigma );
252  continuousModels[k].setAutoEstimateSigma( autoEstimateSigma );
253  continuousModels[k].enableScaling( false ); //Scaling should always off for the models as we do any scaling in the CHMM
254 
255  //Train the model
256  if( !continuousModels[k].train_( trainingData[k] ) ){
257  errorLog << __GRT_LOG__ << " Failed to train CHMM for sample " << k << std::endl;
258  return false;
259  }
260  }
261 
262  if( committeeSize > trainingData.getNumSamples() ){
263  committeeSize = trainingData.getNumSamples();
264  warningLog << __GRT_LOG__ << " The committeeSize is larger than the number of training sample. Setting committeeSize to number of training samples: " << trainingData.getNumSamples() << std::endl;
265  }
266 
267  //Flag that the model has been trained
268  trained = true;
269 
270  //Compute any null rejection thresholds if needed
271  if( useNullRejection ){
272  //Compute the rejection thresholds
273  nullRejectionThresholds.resize(numClasses);
274  }
275 
276  return true;
277 }
278 
279 bool HMM::predict_(VectorFloat &inputVector){
280 
281  switch( hmmType ){
282  case HMM_DISCRETE:
283  return predict_discrete( inputVector );
284  break;
285  case HMM_CONTINUOUS:
286  return predict_continuous( inputVector );
287  break;
288  }
289 
290  errorLog << __GRT_LOG__ << " Failed to predict, unknown HMM type!" << std::endl;
291 
292  return false;
293 }
294 
295 bool HMM::predict_discrete( VectorFloat &inputVector ){
296 
297  predictedClassLabel = 0;
298  maxLikelihood = -10000;
299 
300  if( !trained ){
301  errorLog << __GRT_LOG__ << " The HMM classifier has not been trained!" << std::endl;
302  return false;
303  }
304 
305  if( inputVector.size() != numInputDimensions ){
306  errorLog << __GRT_LOG__ << " The size of the input vector (" << inputVector.size() << ") does not match the num features in the model (" << numInputDimensions << std::endl;
307  return false;
308  }
309 
310  if( classLikelihoods.size() != numClasses ) classLikelihoods.resize(numClasses,0);
311  if( classDistances.size() != numClasses ) classDistances.resize(numClasses,0);
312 
313  Float sum = 0;
314  bestDistance = -99e+99;
315  UINT bestIndex = 0;
316  UINT newObservation = (UINT)inputVector[0];
317 
318  if( newObservation >= numSymbols ){
319  errorLog << __GRT_LOG__ << " The new observation is not a valid symbol! It should be in the range [0 numSymbols-1]" << std::endl;
320  return false;
321  }
322 
323  for(UINT k=0; k<numClasses; k++){
324  classDistances[k] = discreteModels[k].predict( newObservation );
325 
326  //Set the class likelihood as the antilog of the class distances
327  classLikelihoods[k] = grt_antilog( classDistances[k] );
328 
329  //The loglikelihood values are negative so we want the values closest to 0
330  if( classDistances[k] > bestDistance ){
331  bestDistance = classDistances[k];
332  bestIndex = k;
333  }
334 
335  sum += classLikelihoods[k];
336  }
337 
338  //Turn the class distances into proper likelihoods
339  for(UINT k=0; k<numClasses; k++){
340  classLikelihoods[k] /= sum;
341  }
342 
343  maxLikelihood = classLikelihoods[ bestIndex ];
344  predictedClassLabel = classLabels[ bestIndex ];
345 
346  if( useNullRejection ){
347  if( maxLikelihood > nullRejectionThresholds[ bestIndex ] ){
348  predictedClassLabel = classLabels[ bestIndex ];
349  }else predictedClassLabel = GRT_DEFAULT_NULL_CLASS_LABEL;
350  }
351 
352  return true;
353 }
354 
355 bool HMM::predict_continuous( VectorFloat &inputVector ){
356 
357  if( !trained ){
358  errorLog << __GRT_LOG__ << " The HMM classifier has not been trained!" << std::endl;
359  return false;
360  }
361 
362  if( inputVector.getSize() != numInputDimensions ){
363  errorLog << __GRT_LOG__ << " The size of the input vector (" << inputVector.getSize() << ") does not match the num features in the model (" << numInputDimensions << std::endl;
364  return false;
365  }
366 
367  //Scale the input vector if needed
368  if( useScaling ){
369  for(UINT i=0; i<numInputDimensions; i++){
370  inputVector[i] = scale(inputVector[i], ranges[i].minValue, ranges[i].maxValue, 0, 1);
371  }
372  }
373 
374  if( classLikelihoods.size() != numClasses ) classLikelihoods.resize(numClasses,0);
375  if( classDistances.size() != numClasses ) classDistances.resize(numClasses,0);
376 
377  std::fill(classLikelihoods.begin(),classLikelihoods.end(),0);
378  std::fill(classDistances.begin(),classDistances.end(),0);
379 
380  bestDistance = -1000;
381  UINT bestIndex = 0;
382  Float minValue = -1000;
383 
384  const UINT numModels = (UINT)continuousModels.size();
385  Vector< IndexedDouble > results(numModels);
386  for(UINT i=0; i<numModels; i++){
387 
388  //Run the prediction for this model
389  if( continuousModels[i].predict_( inputVector ) ){
390  results[i].value = continuousModels[i].getLoglikelihood();
391  results[i].index = continuousModels[i].getClassLabel();
392  }else{
393  errorLog << __GRT_LOG__ << " Prediction failed for model: " << i << std::endl;
394  return false;
395  }
396 
397  if( results[i].value < minValue ){
398  if( !grt_isnan(results[i].value) ){
399  minValue = results[i].value;
400  }
401  }
402 
403  if( results[i].value > bestDistance ){
404  if( !grt_isnan(results[i].value) ){
405  bestDistance = results[i].value;
406  bestIndex = i;
407  }
408  }
409 
410  //cout << "value: " << results[i].value << " label: " << results[i].index << std::endl;
411  }
412 
413  //Store the phase from the best model
414  phase = continuousModels[ bestIndex ].getPhase();
415 
416  //Sort the results
417  std::sort(results.begin(),results.end(),IndexedDouble::sortIndexedDoubleByValueDescending);
418 
419  //Run the majority vote
420  const Float committeeWeight = 1.0 / committeeSize;
421  for(UINT i=0; i<committeeSize; i++){
422  classDistances[ getClassLabelIndexValue( results[i].index ) ] += Util::scale(results[i].value, -1000, 0, 0, committeeWeight, true);
423  }
424 
425  //Turn the class distances into likelihoods
426  Float sum = Util::sum(classDistances);
427  if( sum > 0 ){
428  for(UINT k=0; k<numClasses; k++){
429  classLikelihoods[k] = classDistances[k] / sum;
430  }
431 
432  //Find the maximum label
433  for(UINT k=0; k<numClasses; k++){
434  if( classDistances[k] > bestDistance ){
435  bestDistance = classDistances[k];
436  bestIndex = k;
437  }
438  }
439 
440  maxLikelihood = classLikelihoods[ bestIndex ];
441  predictedClassLabel = classLabels[ bestIndex ];
442  }else{
443  //If the sum is not greater than 1, then no class is close to any model
444  maxLikelihood = 0;
445  predictedClassLabel = 0;
446  }
447 
448  return true;
449 }
450 
451 bool HMM::predict_(MatrixFloat &timeseries){
452 
453  switch( hmmType ){
454  case HMM_DISCRETE:
455  return predict_discrete( timeseries );
456  break;
457  case HMM_CONTINUOUS:
458  return predict_continuous( timeseries );
459  break;
460  }
461 
462  errorLog << __GRT_LOG__ << " Failed to predict, unknown HMM type!" << std::endl;
463 
464  return false;
465 }
466 
467 bool HMM::predict_discrete(MatrixFloat &timeseries){
468 
469  if( !trained ){
470  errorLog << __GRT_LOG__ << " The HMM classifier has not been trained!" << std::endl;
471  return false;
472  }
473 
474  if( timeseries.getNumCols() != 1 ){
475  errorLog << __GRT_LOG__ << " The number of columns in the input matrix must be 1. It is: " << timeseries.getNumCols() << std::endl;
476  return false;
477  }
478 
479  //Covert the matrix Float to observations
480  const UINT M = timeseries.getNumRows();
481  Vector<UINT> observationSequence( M );
482 
483  for(UINT i=0; i<M; i++){
484  observationSequence[i] = (UINT)timeseries[i][0];
485 
486  if( observationSequence[i] >= numSymbols ){
487  errorLog << __GRT_LOG__ << " The new observation is not a valid symbol! It should be in the range [0 numSymbols-1]" << std::endl;
488  return false;
489  }
490  }
491 
492  if( classLikelihoods.size() != numClasses ) classLikelihoods.resize(numClasses,0);
493  if( classDistances.size() != numClasses ) classDistances.resize(numClasses,0);
494 
495  bestDistance = -99e+99;
496  UINT bestIndex = 0;
497  Float sum = 0;
498  for(UINT k=0; k<numClasses; k++){
499  classDistances[k] = discreteModels[k].predict( observationSequence );
500 
501  //Set the class likelihood as the antilog of the class distances
502  classLikelihoods[k] = grt_antilog( classDistances[k] );
503 
504  //The loglikelihood values are negative so we want the values closest to 0
505  if( classDistances[k] > bestDistance ){
506  bestDistance = classDistances[k];
507  bestIndex = k;
508  }
509 
510  sum += classLikelihoods[k];
511  }
512 
513  //Turn the class distances into proper likelihoods
514  for(UINT k=0; k<numClasses; k++){
515  classLikelihoods[k] /= sum;
516  }
517 
518  maxLikelihood = classLikelihoods[ bestIndex ];
519  predictedClassLabel = classLabels[ bestIndex ];
520 
521  if( useNullRejection ){
522  if( maxLikelihood > nullRejectionThresholds[ bestIndex ] ){
523  predictedClassLabel = classLabels[ bestIndex ];
524  }else predictedClassLabel = GRT_DEFAULT_NULL_CLASS_LABEL;
525  }
526 
527  return true;
528 }
529 
530 
531 bool HMM::predict_continuous(MatrixFloat &timeseries){
532 
533  if( !trained ){
534  errorLog << __GRT_LOG__ << " The HMM classifier has not been trained!" << std::endl;
535  return false;
536  }
537 
538  if( timeseries.getNumCols() != numInputDimensions ){
539  errorLog << __GRT_LOG__ << " The number of columns in the input matrix (" << timeseries.getNumCols() << ") does not match the num features in the model (" << numInputDimensions << std::endl;
540  return false;
541  }
542 
543  //Scale the input vector if needed
544  if( useScaling ){
545  const UINT timeseriesLength = timeseries.getNumRows();
546  for(UINT j=0; j<numInputDimensions; j++){
547  for(UINT i=0; i<timeseriesLength; i++){
548  timeseries[i][j] = scale(timeseries[i][j], ranges[j].minValue, ranges[j].maxValue, 0, 1);
549  }
550  }
551  }
552 
553  if( classLikelihoods.getSize() != numClasses ) classLikelihoods.resize(numClasses,0);
554  if( classDistances.getSize() != numClasses ) classDistances.resize(numClasses,0);
555 
556  std::fill(classLikelihoods.begin(),classLikelihoods.end(),0);
557  std::fill(classDistances.begin(),classDistances.end(),0);
558 
559  bestDistance = -1000;
560  UINT bestIndex = 0;
561  Float minValue = -1000;
562 
563  const UINT numModels = (UINT)continuousModels.size();
564  Vector< IndexedDouble > results(numModels);
565  for(UINT i=0; i<numModels; i++){
566 
567  //Run the prediction for this model
568  if( continuousModels[i].predict_( timeseries ) ){
569  results[i].value = continuousModels[i].getLoglikelihood();
570  results[i].index = continuousModels[i].getClassLabel();
571  }else{
572  errorLog << __GRT_LOG__ << " Prediction failed for model: " << i << std::endl;
573  return false;
574  }
575 
576  if( results[i].value < minValue ){
577  minValue = results[i].value;
578  }
579 
580  if( results[i].value > bestDistance ){
581  bestDistance = results[i].value;
582  bestIndex = i;
583  }
584  }
585 
586  //Store the phase from the best model
587  phase = continuousModels[ bestIndex ].getPhase();
588 
589  //Sort the results
590  std::sort(results.begin(),results.end(),IndexedDouble::sortIndexedDoubleByValueDescending);
591 
592  //Run the majority vote
593  const Float committeeWeight = 1.0 / committeeSize;
594  for(UINT i=0; i<committeeSize; i++){
595  classDistances[ getClassLabelIndexValue( results[i].index ) ] += Util::scale(results[i].value, -1000, 0, 0, committeeWeight, true);
596  }
597 
598  //Turn the class distances into likelihoods
599  Float sum = Util::sum(classDistances);
600  if( sum > 0 ){
601  for(UINT k=0; k<numClasses; k++){
602  classLikelihoods[k] = classDistances[k] / sum;
603  }
604 
605  //Find the maximum label
606  for(UINT k=0; k<numClasses; k++){
607  if( classDistances[k] > bestDistance ){
608  bestDistance = classDistances[k];
609  bestIndex = k;
610  }
611  }
612 
613  maxLikelihood = classLikelihoods[ bestIndex ];
614  predictedClassLabel = classLabels[ bestIndex ];
615  }else{
616  //If the sum is not greater than 1, then no class is close to any model
617  maxLikelihood = 0;
618  predictedClassLabel = 0;
619  }
620  return true;
621 }
622 
623 bool HMM::reset(){
624 
625  //Reset the base class
627 
628  switch( hmmType ){
629  case HMM_DISCRETE:
630  for(size_t i=0; i<discreteModels.size(); i++){
631  discreteModels[i].reset();
632  }
633  break;
634  case HMM_CONTINUOUS:
635  for(size_t i=0; i<continuousModels.size(); i++){
636  continuousModels[i].reset();
637  }
638  break;
639  }
640 
641  return true;
642 }
643 
644 bool HMM::clear(){
645 
646  //Clear the base class
648 
649  discreteModels.clear();
650  continuousModels.clear();
651 
652  return true;
653 }
654 
655 bool HMM::print() const{
656 
657  std::cout << "HMM Model\n";
658 
659  //Write the generic hmm data
660  std::cout << "HmmType: " << hmmType << std::endl;
661  std::cout << "ModelType: " << modelType << std::endl;
662  std::cout << "Delta: " << delta << std::endl;
663 
664  //Write the model specific data
665  switch( hmmType ){
666  case HMM_DISCRETE:
667  std::cout << "NumStates: " << numStates << std::endl;
668  std::cout << "NumSymbols: " << numSymbols << std::endl;
669  std::cout << "NumRandomTrainingIterations: " << numRandomTrainingIterations << std::endl;
670  std::cout << "NumDiscreteModels: " << discreteModels.getSize() << std::endl;
671  std::cout << "DiscreteModels: " << std::endl;
672  for(size_t i=0; i<discreteModels.getSize(); i++){
673  if( !discreteModels[i].print() ){
674  errorLog << __GRT_LOG__ << " Failed to print discrete model " << i << " to file!" << std::endl;
675  return false;
676  }
677  }
678  break;
679  case HMM_CONTINUOUS:
680  std::cout << "DownsampleFactor: " << downsampleFactor << std::endl;
681  std::cout << "CommitteeSize: " << committeeSize << std::endl;
682  std::cout << "Sigma: " << sigma << std::endl;
683  std::cout << "AutoEstimateSigma: " << autoEstimateSigma << std::endl;
684  std::cout << "NumContinuousModels: " << continuousModels.getSize() << std::endl;
685  std::cout << "ContinuousModels: " << std::endl;
686  for(size_t i=0; i<continuousModels.getSize(); i++){
687  if( !continuousModels[i].print() ){
688  errorLog << __GRT_LOG__ << " Failed to print continuous model " << i << " to file!" << std::endl;
689  return false;
690  }
691  }
692  break;
693  }
694 
695  return true;
696 }
697 
698 bool HMM::save( std::fstream &file ) const{
699 
700  if(!file.is_open())
701  {
702  errorLog << __GRT_LOG__ << " File is not open!" << std::endl;
703  return false;
704  }
705 
706  //Write the header info
707  file << "HMM_MODEL_FILE_V2.0\n";
708 
709  //Write the classifier settings to the file
711  errorLog << __GRT_LOG__ << " Failed to save classifier base settings to file!" << std::endl;
712  return false;
713  }
714 
715  //Write the generic hmm data
716  file << "HmmType: " << hmmType << std::endl;
717  file << "ModelType: " << modelType << std::endl;
718  file << "Delta: " << delta << std::endl;
719 
720  //Write the model specific data
721  switch( hmmType ){
722  case HMM_DISCRETE:
723  file << "NumStates: " << numStates << std::endl;
724  file << "NumSymbols: " << numSymbols << std::endl;
725  file << "NumRandomTrainingIterations: " << numRandomTrainingIterations << std::endl;
726  file << "NumDiscreteModels: " << discreteModels.getSize() << std::endl;
727  file << "DiscreteModels: " << std::endl;
728  for(size_t i=0; i<discreteModels.getSize(); i++){
729  if( !discreteModels[i].save( file ) ){
730  errorLog << __GRT_LOG__ << " Failed to save discrete model " << i << " to file!" << std::endl;
731  return false;
732  }
733  }
734  break;
735  case HMM_CONTINUOUS:
736  file << "DownsampleFactor: " << downsampleFactor << std::endl;
737  file << "CommitteeSize: " << committeeSize << std::endl;
738  file << "Sigma: " << sigma << std::endl;
739  file << "NumContinuousModels: " << continuousModels.getSize() << std::endl;
740  file << "ContinuousModels: " << std::endl;
741  for(UINT i=0; i<continuousModels.getSize(); i++){
742  if( !continuousModels[i].save( file ) ){
743  errorLog << __GRT_LOG__ << " Failed to save continuous model " << i << " to file!" << std::endl;
744  return false;
745  }
746  }
747  break;
748  }
749 
750  return true;
751 }
752 
753 bool HMM::load( std::fstream &file ){
754 
755  clear();
756 
757  if(!file.is_open())
758  {
759  errorLog << __GRT_LOG__ << " File is not open!" << std::endl;
760  return false;
761  }
762 
763  std::string word;
764  UINT numModels = 0;
765 
766  file >> word;
767 
768  //Find the file type header
769  if(word != "HMM_MODEL_FILE_V2.0"){
770  errorLog << __GRT_LOG__ << " Could not find Model File Header!" << std::endl;
771  return false;
772  }
773 
774  //Load the base settings from the file
776  errorLog << __GRT_LOG__ << " Failed to load base settings from file!" << std::endl;
777  return false;
778  }
779 
780  //Load the generic hmm data
781  file >> word;
782  if(word != "HmmType:"){
783  errorLog << __GRT_LOG__ << " Could not find HmmType." << std::endl;
784  return false;
785  }
786  file >> hmmType;
787 
788  file >> word;
789  if(word != "ModelType:"){
790  errorLog << __GRT_LOG__ << " Could not find ModelType." << std::endl;
791  return false;
792  }
793  file >> modelType;
794 
795  file >> word;
796  if(word != "Delta:"){
797  errorLog << __GRT_LOG__ << " Could not find Delta." << std::endl;
798  return false;
799  }
800  file >> delta;
801 
802  //Load the model specific data
803  switch( hmmType ){
804  case HMM_DISCRETE:
805 
806  file >> word;
807  if(word != "NumStates:"){
808  errorLog << __GRT_LOG__ << " Could not find NumStates." << std::endl;
809  return false;
810  }
811  file >> numStates;
812 
813  file >> word;
814  if(word != "NumSymbols:"){
815  errorLog << __GRT_LOG__ << " Could not find NumSymbols." << std::endl;
816  return false;
817  }
818  file >> numSymbols;
819 
820  file >> word;
821  if(word != "NumRandomTrainingIterations:"){
822  errorLog << __GRT_LOG__ << " Could not find NumRandomTrainingIterations." << std::endl;
823  return false;
824  }
825  file >> numRandomTrainingIterations;
826 
827  file >> word;
828  if(word != "NumDiscreteModels:"){
829  errorLog << __GRT_LOG__ << " Could not find NumDiscreteModels." << std::endl;
830  return false;
831  }
832  file >> numModels;
833 
834  file >> word;
835  if(word != "DiscreteModels:"){
836  errorLog << __GRT_LOG__ << " Could not find DiscreteModels." << std::endl;
837  return false;
838  }
839 
840  if( numModels > 0 ){
841  discreteModels.resize(numModels);
842  for(size_t i=0; i<discreteModels.getSize(); i++){
843  if( !discreteModels[i].load( file ) ){
844  errorLog << __GRT_LOG__ << " Failed to load discrete model " << i << " from file!" << std::endl;
845  return false;
846  }
847  }
848  }
849  break;
850  case HMM_CONTINUOUS:
851 
852  file >> word;
853  if(word != "DownsampleFactor:"){
854  errorLog << __GRT_LOG__ << " Could not find DownsampleFactor." << std::endl;
855  return false;
856  }
857  file >> downsampleFactor;
858 
859  file >> word;
860  if(word != "CommitteeSize:"){
861  errorLog << __GRT_LOG__ << " Could not find CommitteeSize." << std::endl;
862  return false;
863  }
864  file >> committeeSize;
865 
866  file >> word;
867  if(word != "Sigma:"){
868  errorLog << __GRT_LOG__ << " Could not find Sigma." << std::endl;
869  return false;
870  }
871  file >> sigma;
872 
873  file >> word;
874  if(word != "NumContinuousModels:"){
875  errorLog << __GRT_LOG__ << " Could not find NumContinuousModels." << std::endl;
876  return false;
877  }
878  file >> numModels;
879 
880  file >> word;
881  if(word != "ContinuousModels:"){
882  errorLog << __GRT_LOG__ << " Could not find ContinuousModels." << std::endl;
883  return false;
884  }
885 
886  if( numModels > 0 ){
887  continuousModels.resize(numModels);
888  for(size_t i=0; i<continuousModels.getSize(); i++){
889  if( !continuousModels[i].load( file ) ){
890  errorLog << __GRT_LOG__ << " Failed to load continuous model " << i << " from file!" << std::endl;
891  return false;
892  }
893  }
894  }
895  break;
896  }
897 
898  return true;
899 
900 }
901 
902 bool HMM::convertDataToObservationSequence( TimeSeriesClassificationData &classData, Vector< Vector< UINT > > &observationSequences ){
903 
904  observationSequences.resize( classData.getNumSamples() );
905 
906  for(UINT i=0; i<classData.getNumSamples(); i++){
907  MatrixFloat &timeseries = classData[i].getData();
908  observationSequences[i].resize( timeseries.getNumRows() );
909  for(UINT j=0; j<timeseries.getNumRows(); j++){
910  if( timeseries[j][0] >= numSymbols ){
911  errorLog << __GRT_LOG__ << " Found an observation sequence with a value outside of the symbol range! Value: " << timeseries[j][0] << std::endl;
912  return false;
913  }
914  observationSequences[i][j] = (UINT)timeseries[j][0];
915  }
916  }
917 
918  return true;
919 }
920 
921 UINT HMM::getHMMType() const{
922  return hmmType;
923 }
924 
925 UINT HMM::getModelType() const{
926  return modelType;
927 }
928 
929 UINT HMM::getDelta() const{
930  return delta;
931 }
932 
933 UINT HMM::getNumStates() const{
934  return numStates;
935 }
936 
937 UINT HMM::getNumSymbols() const{
938  return numSymbols;
939 }
940 
942  return numRandomTrainingIterations;
943 }
944 
946  return discreteModels;
947 }
948 
950  return continuousModels;
951 }
952 
953 bool HMM::setHMMType(const UINT hmmType){
954 
955  clear();
956 
957  if( hmmType == HMM_DISCRETE || hmmType == HMM_CONTINUOUS ){
958  this->hmmType = hmmType;
959  return true;
960  }
961 
962  warningLog << __GRT_LOG__ << " Unknown HMM type!" << std::endl;
963  return false;
964 }
965 
966 bool HMM::setModelType(const UINT modelType){
967 
968  clear();
969 
970  if( modelType == HMM_ERGODIC || modelType == HMM_LEFTRIGHT ){
971  this->modelType = modelType;
972  return true;
973  }
974 
975  warningLog << __GRT_LOG__ << " Unknown model type!" << std::endl;
976  return false;
977 }
978 
979 bool HMM::setDelta(const UINT delta){
980 
981  clear();
982 
983  if( delta > 0 ){
984  this->delta = delta;
985  return true;
986  }
987 
988  warningLog << __GRT_LOG__ << " Delta must be greater than zero!" << std::endl;
989  return false;
990 }
991 
992 bool HMM::setDownsampleFactor(const UINT downsampleFactor){
993 
994  clear();
995  if( downsampleFactor > 0 ){
996  this->downsampleFactor = downsampleFactor;
997  return true;
998  }
999  return false;
1000 }
1001 
1002 bool HMM::setCommitteeSize(const UINT committeeSize){
1003 
1004  if( committeeSize > 0 ){
1005  this->committeeSize = committeeSize;
1006  return true;
1007  }
1008 
1009  return false;
1010 }
1011 
1012 bool HMM::setNumStates(const UINT numStates){
1013 
1014  clear();
1015 
1016  if( numStates > 0 ){
1017  this->numStates = numStates;
1018  return true;
1019  }
1020 
1021  warningLog << __GRT_LOG__ << " Num states must be greater than zero!" << std::endl;
1022  return false;
1023 }
1024 
1025 bool HMM::setNumSymbols(const UINT numSymbols){
1026 
1027  clear();
1028 
1029  if( numSymbols > 0 ){
1030  this->numSymbols = numSymbols;
1031  return true;
1032  }
1033 
1034  warningLog << __GRT_LOG__ << " Num symbols must be greater than zero!" << std::endl;
1035  return false;
1036 }
1037 
1038 bool HMM::setNumRandomTrainingIterations(const UINT numRandomTrainingIterations){
1039 
1040  clear();
1041 
1042  if( numRandomTrainingIterations > 0 ){
1043  this->numRandomTrainingIterations = numRandomTrainingIterations;
1044  return true;
1045  }
1046 
1047  warningLog << __GRT_LOG__ << " The number of random training iterations must be greater than zero!" << std::endl;
1048  return false;
1049 }
1050 
1051 bool HMM::setSigma(const Float sigma){
1052  if( sigma > 0 ){
1053  this->sigma = sigma;
1054  for(UINT i=0; i<continuousModels.getSize(); i++){
1055  continuousModels[i].setSigma( sigma );
1056  }
1057  return true;
1058  }
1059  return false;
1060 }
1061 
1062 bool HMM::setAutoEstimateSigma(const bool autoEstimateSigma){
1063 
1064  clear();
1065 
1066  this->autoEstimateSigma = autoEstimateSigma;
1067 
1068  return true;
1069 }
1070 
1071 GRT_END_NAMESPACE
bool saveBaseSettingsToFile(std::fstream &file) const
Definition: Classifier.cpp:274
std::string getId() const
Definition: GRTBase.cpp:85
static std::string getId()
Definition: HMM.cpp:28
bool setHMMType(const UINT hmmType)
Definition: HMM.cpp:953
bool setDownsampleFactor(const UINT downsampleFactor)
Definition: HMM.cpp:992
static Float scale(const Float &x, const Float &minSource, const Float &maxSource, const Float &minTarget, const Float &maxTarget, const bool constrain=false)
Definition: Util.cpp:55
virtual bool predict_(VectorFloat &inputVector)
Definition: HMM.cpp:279
virtual bool resize(const unsigned int size)
Definition: Vector.h:133
virtual bool deepCopyFrom(const Classifier *classifier)
Definition: HMM.cpp:87
bool setNumRandomTrainingIterations(const UINT numRandomTrainingIterations)
Definition: HMM.cpp:1038
virtual bool reset()
Definition: HMM.cpp:623
virtual bool load(std::fstream &file)
Definition: HMM.cpp:753
UINT getSize() const
Definition: Vector.h:201
bool setNumSymbols(const UINT numStates)
Definition: HMM.cpp:1025
virtual ~HMM(void)
Definition: HMM.cpp:63
virtual bool train(ClassificationData trainingData)
Definition: HMM.cpp:115
Vector< DiscreteHiddenMarkovModel > getDiscreteModels() const
Definition: HMM.cpp:945
virtual bool clear()
Definition: HMM.cpp:644
bool setModelType(const UINT modelType)
Definition: HMM.cpp:966
UINT getModelType() const
Definition: HMM.cpp:925
virtual bool print() const
Definition: HMM.cpp:655
Vector< ClassTracker > getClassTracker() const
HMM & operator=(const HMM &rhs)
Definition: HMM.cpp:67
UINT hmmType
Controls if this is a HMM_DISCRETE or a HMM_CONTINUOUS.
Definition: HMM.h:356
UINT getNumStates() const
Definition: HMM.cpp:933
bool setSigma(const Float sigma)
Definition: HMM.cpp:1051
virtual bool train_(TimeSeriesClassificationData &trainingData)
Definition: HMM.cpp:121
HMM(const UINT hmmType=HMM_CONTINUOUS, const UINT modelType=HMM_LEFTRIGHT, const UINT delta=1, const bool useScaling=false, const bool useNullRejection=false)
Definition: HMM.cpp:33
UINT getDelta() const
Definition: HMM.cpp:929
UINT getHMMType() const
Definition: HMM.cpp:921
bool copyBaseVariables(const Classifier *classifier)
Definition: Classifier.cpp:101
bool loadBaseSettingsFromFile(std::fstream &file)
Definition: Classifier.cpp:321
UINT getNumSymbols() const
Definition: HMM.cpp:937
unsigned int getNumRows() const
Definition: Matrix.h:574
unsigned int getNumCols() const
Definition: Matrix.h:581
UINT getClassLabelIndexValue(const UINT classLabel) const
Definition: Classifier.cpp:213
bool setDelta(const UINT delta)
Definition: HMM.cpp:979
bool setCommitteeSize(const UINT committeeSize)
Definition: HMM.cpp:1002
virtual bool save(std::fstream &file) const
Definition: HMM.cpp:698
virtual bool reset()
Definition: Classifier.cpp:132
This class acts as the main interface for using a Hidden Markov Model.
Definition: HMM.h:41
bool scale(const Float minTarget, const Float maxTarget)
TimeSeriesClassificationData getClassData(const UINT classLabel) const
Definition: Vector.h:41
UINT getNumRandomTrainingIterations() const
Definition: HMM.cpp:941
bool setNumStates(const UINT numStates)
Definition: HMM.cpp:1012
virtual bool clear()
Definition: Classifier.cpp:151
This is the main base class that all GRT Classification algorithms should inherit from...
Definition: Classifier.h:41
static Float sum(const VectorFloat &x)
Definition: Util.cpp:178
Float scale(const Float &x, const Float &minSource, const Float &maxSource, const Float &minTarget, const Float &maxTarget, const bool constrain=false)
Definition: GRTBase.h:184
Vector< ContinuousHiddenMarkovModel > getContinuousModels() const
Definition: HMM.cpp:949