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