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