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.
DiscreteHiddenMarkovModel.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
23 
24 GRT_BEGIN_NAMESPACE
25 
26 //Default constructor
27 DiscreteHiddenMarkovModel::DiscreteHiddenMarkovModel() : MLBase( "DiscreteHiddenMarkovModel" )
28 {
29  numStates = 0;
30  numSymbols = 0;
31  delta = 1;
32  numRandomTrainingIterations = 5;
33  maxNumEpochs = 100;
34  cThreshold = -1000;
35  modelType = HMM_LEFTRIGHT;
36  logLikelihood = 0.0;
37  minChange = 1.0e-5;
38 }
39 
40 //Init the model with a set number of states and symbols
41 DiscreteHiddenMarkovModel::DiscreteHiddenMarkovModel(const UINT numStates,const UINT numSymbols,const UINT modelType,const UINT delta) : MLBase( "DiscreteHiddenMarkovModel" )
42 {
43  this->numStates = numStates;
44  this->numSymbols = numSymbols;
45  this->modelType = modelType;
46  this->delta = delta;
47  logLikelihood = 0.0;
48  numRandomTrainingIterations = 5;
49  cThreshold = -1000;
50  logLikelihood = 0.0;
51 
52  randomizeMatrices(numStates,numSymbols);
53 }
54 
55 //Init the model with a pre-trained a, b, and pi matrices
56 DiscreteHiddenMarkovModel::DiscreteHiddenMarkovModel(const MatrixFloat &a,const MatrixFloat &b,const VectorFloat &pi,const UINT modelType,const UINT delta) : MLBase( "DiscreteHiddenMarkovModel" )
57 {
58 
59  numStates = 0;
60  numSymbols = 0;
61  numRandomTrainingIterations = 5;
62  maxNumEpochs = 100;
63  cThreshold = -1000;
64  logLikelihood = 0.0;
65  minChange = 1.0e-5;
66 
67  if( a.getNumRows() == a.getNumRows() && a.getNumRows() == b.getNumRows() && a.getNumRows() == pi.size() ){
68  this->a = a;
69  this->b = b;
70  this->pi = pi;
71  this->modelType = modelType;
72  this->delta = delta;
73  numStates = b.getNumRows();
74  numSymbols = b.getNumCols();
75  trained = true;
76  }else{
77  errorLog << "DiscreteHiddenMarkovModel(...) - The a,b,pi sizes are invalid!" << std::endl;
78  }
79 }
80 
81 DiscreteHiddenMarkovModel::DiscreteHiddenMarkovModel(const DiscreteHiddenMarkovModel &rhs) : MLBase( "DiscreteHiddenMarkovModel" )
82 {
83  this->numStates = rhs.numStates;
84  this->numSymbols = rhs.numSymbols;
85  this->delta = rhs.delta;
86  this->numRandomTrainingIterations = rhs.numRandomTrainingIterations;
87  this->cThreshold = rhs.cThreshold;
88  this->modelType = rhs.modelType;
89  this->logLikelihood = rhs.logLikelihood;
90  this->a = rhs.a;
91  this->b = rhs.b;
92  this->pi = rhs.pi;
93  this->trainingLog = rhs.trainingLog;
94 }
95 
96 //Default destructor
97 DiscreteHiddenMarkovModel::~DiscreteHiddenMarkovModel(){
98 
99 }
100 
101 //This can be called at any time to reset the entire model
102 bool DiscreteHiddenMarkovModel::resetModel(const UINT numStates,const UINT numSymbols,const UINT modelType,const UINT delta){
103  this->numStates = numStates;
104  this->numSymbols = numSymbols;
105  this->modelType = modelType;
106  this->delta = delta;
107  return randomizeMatrices(numStates,numSymbols);
108 }
109 
110 bool DiscreteHiddenMarkovModel::randomizeMatrices(const UINT numStates,const UINT numSymbols){
111 
112  //Set the model as untrained as everything will now be reset
113  trained = false;
114  logLikelihood = 0.0;
115 
116  //Set the new state and symbol size
117  this->numStates = numStates;
118  this->numSymbols = numSymbols;
119  a.resize(numStates,numStates);
120  b.resize(numStates,numSymbols);
121  pi.resize(numStates);
122 
123  //Fill Transition and Symbol Matrices randomly
124  //It's best to choose values in the range [0.9 1.1] rather than [0 1]
125  //That way, no single value will get too large or too small a weight when the values are normalized
126  Random random;
127  for(UINT i=0; i<a.getNumRows(); i++)
128  for(UINT j=0; j<a.getNumCols(); j++)
129  a[i][j] = random.getRandomNumberUniform(0.9,1);
130 
131  for(UINT i=0; i<b.getNumRows(); i++)
132  for(UINT j=0; j<b.getNumCols(); j++)
133  b[i][j] = random.getRandomNumberUniform(0.9,1);
134 
135  //Randomise pi
136  for(UINT i=0; i<numStates; i++)
137  pi[i] = random.getRandomNumberUniform(0.9,1);
138 
139  //Set any constraints on the model
140  switch( modelType ){
141  case(HMM_ERGODIC):
142  //Don't need todo anything
143  break;
144  case(HMM_LEFTRIGHT):
145  //Set the state transitions constraints
146  for(UINT i=0; i<numStates; i++)
147  for(UINT j=0; j<numStates; j++)
148  if((j<i) || (j>i+delta)) a[i][j] = 0.0;
149 
150  //Set pi to start in state 0
151  for(UINT i=0; i<numStates; i++){
152  pi[i] = i==0 ? 1 : 0;
153  }
154  break;
155  default:
156  throw("HMM_ERROR: Unkown model type!");
157  return false;
158  break;
159  }
160 
161  //Normalize the matrices
162  Float sum=0.0;
163  for (UINT i=0; i<numStates; i++) {
164  sum = 0.;
165  for (UINT j=0; j<numStates; j++) sum += a[i][j];
166  for (UINT j=0; j<numStates; j++) a[i][j] /= sum;
167  }
168  for (UINT i=0; i<numStates; i++) {
169  sum = 0.;
170  for (UINT k=0; k<numSymbols; k++) sum += b[i][k];
171  for (UINT k=0; k<numSymbols; k++) b[i][k] /= sum;
172  }
173 
174  //Normalise pi
175  sum = 0.0;
176  for (UINT i=0; i<numStates; i++) sum += pi[i];
177  for (UINT i=0; i<numStates; i++) pi[i] /= sum;
178 
179  return true;
180 }
181 
182 Float DiscreteHiddenMarkovModel::predict(const UINT newSample){
183 
184  if( !trained ){
185  return 0;
186  }
187 
188  observationSequence.push_back( newSample );
189 
190  Vector< UINT > obs = observationSequence.getData();
191 
192  return predict(obs);
193 }
194 
195 /*Float predictLogLikelihood(Vector<UINT> &obs)
196  - This method computes P(O|A,B,Pi) using the forward algorithm
197  */
198 Float DiscreteHiddenMarkovModel::predict(const Vector<UINT> &obs){
199 
200  const int N = (int)numStates;
201  const int T = (int)obs.size();
202  int t,i,j = 0;
203  MatrixFloat alpha(T,numStates);
204  VectorFloat c(T);
205 
207  //Step 1: Init at t=0
208  t = 0;
209  c[t] = 0.0;
210  for(i=0; i<N; i++){
211  alpha[t][i] = pi[i]*b[i][ obs[t] ];
212  c[t] += alpha[t][i];
213  }
214 
215  //Set the inital scaling coeff
216  c[t] = 1.0/c[t];
217 
218  //Scale alpha
219  for(i=0; i<N; i++) alpha[t][i] *= c[t];
220 
221  //Step 2: Induction
222  for(t=1; t<T; t++){
223  c[t] = 0.0;
224  for(j=0; j<N; j++){
225  alpha[t][j] = 0.0;
226  for(i=0; i<N; i++){
227  alpha[t][j] += alpha[t-1][i] * a[i][j];
228  }
229  alpha[t][j] *= b[j][obs[t]];
230  c[t] += alpha[t][j];
231  }
232 
233  //Set the scaling coeff
234  c[t] = 1.0/c[t];
235 
236  //Scale Alpha
237  for(j=0; j<N; j++) alpha[t][j] *= c[t];
238  }
239 
240  if( int(estimatedStates.size()) != T ) estimatedStates.resize(T);
241  for(t=0; t<T; t++){
242  Float maxValue = 0;
243  for(i=0; i<N; i++){
244  if( alpha[t][i] > maxValue ){
245  maxValue = alpha[t][i];
246  estimatedStates[t] = i;
247  }
248  }
249  }
250 
251  //Termination
252  Float loglikelihood = 0.0;
253  for(t=0; t<T; t++) loglikelihood += log( c[t] );
254  return -loglikelihood; //Return the negative log likelihood
255 }
256 
257 /*Float predictLogLikelihood(Vector<UINT> &obs)
258 - This method computes P(O|A,B,Pi) using the forward algorithm
259 */
260 Float DiscreteHiddenMarkovModel::predictLogLikelihood(const Vector<UINT> &obs){
261 
262  const UINT T = (unsigned int)obs.size();
263  UINT t,i,j,minState = 0;
264  MatrixFloat alpha(T,numStates);
265  Float minWeight = 0;
266  Float weight = 0;
267 
268  // Base
269  t = 0;
270  for(i=0; i<numStates; i++){
271  alpha[t][i] = (-1.0 * log(pi[i])) - log(b[i][ obs[t] ]);
272  }
273 
274  // Induction
275  for (t=1; t<T; t++){
276  for (j=0; j<numStates; j++){
277  minState = 0;
278  minWeight = alpha[t-1][j] - log(a[0][j]);
279 
280  for (i=1; i<numStates; i++){
281  weight = alpha[t-1][i] - log(a[i][j]);
282 
283  if (weight < minWeight)
284  {
285  minState = i;
286  minWeight = weight;
287  }
288  }
289 
290  alpha[t][j] = minWeight - log(b[j][ obs[t] ]);
291  }
292  }
293 
294  // Find minimum value for time T-1
295  minState = 1;
296  minWeight = alpha[T - 1][0];
297 
298  for(i=1; i<numStates; i++)
299  {
300  if (alpha[T-1][i] < minWeight)
301  {
302  minState = i;
303  minWeight = alpha[T-1][i];
304  }
305  }
306 
307  // Returns the sequence probability
308  return exp(-minWeight);
309 }
310 
311 /*Float forwardBackward(Vector<UINT> &obs)
312 - This method runs one pass of the forward backward algorithm, the hmm training object needs to be resized BEFORE calling this function!
313 */
314 bool DiscreteHiddenMarkovModel::forwardBackward(HMMTrainingObject &hmm,const Vector<UINT> &obs){
315 
316  const int N = (int)numStates;
317  const int T = (int)obs.size();
318  int t,i,j = 0;
319 
321  //Step 1: Init at t=0
322  t = 0;
323  hmm.c[t] = 0.0;
324  for(i=0; i<N; i++){
325  hmm.alpha[t][i] = pi[i]*b[i][ obs[t] ];
326  hmm.c[t] += hmm.alpha[t][i];
327  }
328 
329  //Set the inital scaling coeff
330  hmm.c[t] = 1.0/hmm.c[t];
331 
332  //Scale alpha
333  for(i=0; i<N; i++) hmm.alpha[t][i] *= hmm.c[t];
334 
335  //Step 2: Induction
336  for(t=1; t<T; t++){
337  hmm.c[t] = 0.0;
338  for(j=0; j<N; j++){
339  hmm.alpha[t][j] = 0.0;
340  for(i=0; i<N; i++){
341  hmm.alpha[t][j] += hmm.alpha[t-1][i] * a[i][j];
342  }
343  hmm.alpha[t][j] *= b[j][obs[t]];
344  hmm.c[t] += hmm.alpha[t][j];
345  }
346 
347  //Set the scaling coeff
348  hmm.c[t] = 1.0/hmm.c[t];
349 
350  //Scale Alpha
351  for(j=0; j<N; j++) hmm.alpha[t][j] *= hmm.c[t];
352  }
353 
354  //Termination
355  hmm.pk = 0.0;
356  for(t=0; t<T; t++) hmm.pk += log( hmm.c[t] );
357  //hmm.pk = - hmm.pk; //We don't really need to minus here
358 
359  if( grt_isinf(hmm.pk) ){
360  return false;
361  }
362 
364  //Step 1: Init at time t=T (T-1 as everything is zero based)
365  t = T-1;
366  for(i=0; i<N; i++) hmm.beta[t][i] = 1.0;
367 
368  //Scale beta, using the same coeff as A
369  for(i=0; i<N; i++) hmm.beta[t][i] *= hmm.c[t];
370 
371  //Step 2: Induction, from T-1 until 1 (T-2 until 0 as everything is zero based)
372  for(t=T-2; t>=0; t--){
373  for(i=0; i<N; i++){
374  //Calculate the backward step for t, using the scaled beta
375  hmm.beta[t][i]=0.0;
376  for(j=0; j<N; j++)
377  hmm.beta[t][i] += a[i][j] * b[j][ obs[t+1] ] * hmm.beta[t+1][j];
378 
379  //Scale B using the same coeff as A
380  hmm.beta[t][i] *= hmm.c[t];
381  }
382  }
383 
384  return true;
385 }
386 
387 /*bool batchTrain(Vector<UINT> &obs)
388 - This method
389 */
390 bool DiscreteHiddenMarkovModel::train(const Vector< Vector<UINT> > &trainingData){
391 
392  //Clear any previous models
393  trained = false;
394  observationSequence.clear();
395  estimatedStates.clear();
396  trainingIterationLog.clear();
397 
398  UINT n,currentIter, bestIndex = 0;
399  Float newLoglikelihood, bestLogValue = 0;
400 
401  if( numRandomTrainingIterations > 1 ){
402 
403  //A buffer to keep track each AB matrix
404  Vector< MatrixFloat > aTracker( numRandomTrainingIterations );
405  Vector< MatrixFloat > bTracker( numRandomTrainingIterations );
406  Vector< Float > loglikelihoodTracker( numRandomTrainingIterations );
407 
408  UINT maxNumTestIter = maxNumEpochs > 10 ? 10 : maxNumEpochs;
409 
410  //Try and find the best starting point
411  for(n=0; n<numRandomTrainingIterations; n++){
412  //Reset the model to a new random starting values
413  randomizeMatrices(numStates,numSymbols);
414 
415  if( !train_(trainingData,maxNumTestIter,currentIter,newLoglikelihood) ){
416  return false;
417  }
418  aTracker[n] = a;
419  bTracker[n] = b;
420  loglikelihoodTracker[n] = newLoglikelihood;
421  }
422 
423  //Get the best result and set it as the a and b starting values
424  bestIndex = 0;
425  bestLogValue = loglikelihoodTracker[0];
426  for(n=1; n<numRandomTrainingIterations; n++){
427  if(bestLogValue < loglikelihoodTracker[n]){
428  bestLogValue = loglikelihoodTracker[n];
429  bestIndex = n;
430  }
431  }
432 
433  //Set a and b
434  a = aTracker[bestIndex];
435  b = bTracker[bestIndex];
436 
437  }else{
438  randomizeMatrices(numStates,numSymbols);
439  }
440 
441  //Perform the actual training
442  if( !train_(trainingData,maxNumEpochs,currentIter,newLoglikelihood) ){
443  return false;
444  }
445 
446  //Calculate the observationSequence buffer length
447  const UINT numObs = (unsigned int)trainingData.size();
448  UINT k = 0;
449  UINT averageObsLength = 0;
450  for(k=0; k<numObs; k++){
451  const UINT T = (unsigned int)trainingData[k].size();
452  averageObsLength += T;
453  }
454 
455  averageObsLength = (UINT)floor( averageObsLength/Float(numObs) );
456  observationSequence.resize( averageObsLength );
457  estimatedStates.resize( averageObsLength );
458 
459  //Finally, flag that the model was trained
460  trained = true;
461 
462  return true;
463 }
464 
465 bool DiscreteHiddenMarkovModel::train_(const Vector< Vector<UINT> > &obs,const UINT maxIter, UINT &currentIter,Float &newLoglikelihood){
466 
467  const UINT numObs = (unsigned int)obs.size();
468  UINT i,j,k,t = 0;
469  Float num,denom,oldLoglikelihood = 0;
470  bool keepTraining = true;
471  trainingIterationLog.clear();
472 
473  //Create the array to hold the data for each training instance
474  Vector< HMMTrainingObject > hmms( numObs );
475 
476  //Create epislon and gamma to hold the re-estimation variables
477  Vector< Vector< MatrixFloat > > epsilon( numObs );
478  Vector< MatrixFloat > gamma( numObs );
479 
480  //Resize the hmms, epsilon and gamma matrices so they are ready to be filled
481  for(k=0; k<numObs; k++){
482  const UINT T = (UINT)obs[k].size();
483  gamma[k].resize(T,numStates);
484  epsilon[k].resize(T);
485  for(t=0; t<T; t++) epsilon[k][t].resize(numStates,numStates);
486 
487  //Resize alpha, beta and phi
488  hmms[k].alpha.resize(T,numStates);
489  hmms[k].beta.resize(T,numStates);
490  hmms[k].c.resize(T);
491  }
492 
493  //For each training seq, run one pass of the forward backward
494  //algorithm then reestimate a and b using the Baum-Welch
495  oldLoglikelihood = 0;
496  newLoglikelihood = 0;
497  currentIter = 0;
498 
499  do{
500  newLoglikelihood = 0.0;
501 
502  //Run the forwardbackward algorithm for each training example
503  for(k=0; k<numObs; k++){
504  if( !forwardBackward(hmms[k],obs[k]) ){
505  return false;
506  }
507  newLoglikelihood += hmms[k].pk;
508  }
509 
510  //Set the new log likelihood as the average of the observations
511  newLoglikelihood /= numObs;
512 
513  trainingIterationLog.push_back( newLoglikelihood );
514 
515  if( ++currentIter >= maxIter ){ keepTraining = false; trainingLog << "Max Iter Reached! Stopping Training" << std::endl; }
516  if( fabs(newLoglikelihood-oldLoglikelihood) < minChange && currentIter > 1 ){ keepTraining = false; trainingLog << "Min Improvement Reached! Stopping Training" << std::endl; }
517  //if( newLoglikelihood < oldLoglikelihood ){ cout<<"Warning: Inverted Training!\n";}
518 
519  trainingLog << "Iter: " << currentIter << " logLikelihood: " << newLoglikelihood << " change: " << oldLoglikelihood - newLoglikelihood << std::endl;
520 
521  print();
522  //PAUSE;
523 
524  oldLoglikelihood = newLoglikelihood;
525 
526  //Only update A, B, and Pi if needed
527  if( keepTraining ){
528 
529  //Re-estimate A
530  for(i=0; i<numStates; i++){
531 
532  //Compute the denominator of A (which is independent of j)
533  denom = 0;
534  for(k=0; k<numObs; k++){
535  for(t=0; t<obs[k].size()-1; t++){
536  denom += hmms[k].alpha[t][i] * hmms[k].beta[t][i] / hmms[k].c[t];
537  }
538  }
539 
540  //Compute the numerator and also update a[i][j]
541  if( denom > 0 ){
542  for(j=0; j<numStates; j++){
543  num = 0;
544  for(k=0; k<numObs; k++){
545  for(t=0; t<obs[k].size()-1; t++){
546  num += hmms[k].alpha[t][i] * a[i][j] * b[j][ obs[k][t+1] ] * hmms[k].beta[t+1][j];
547  }
548  }
549 
550  //Update a[i][j]
551  a[i][j] = num/denom;
552  }
553  }else{
554  errorLog << "Denom is zero for A!" << std::endl;
555  return false;
556  }
557  }
558 
559  //Re-estimate B
560  bool renormB = false;
561  for(i=0; i<numStates; i++){
562  for(j=0; j<numSymbols; j++){
563  num=0.0;
564  denom = 0.0;
565  for(k=0; k<numObs; k++){
566  const UINT T = (unsigned int)obs[k].size();
567  for(t=0; t<T; t++){
568  if( obs[k][t] == j ){
569  num += hmms[k].alpha[t][i] * hmms[k].beta[t][i] / hmms[k].c[t];
570  }
571  denom += hmms[k].alpha[t][i] * hmms[k].beta[t][i] / hmms[k].c[t];
572  }
573  }
574 
575  if( denom == 0 ){
576  errorLog << "Denominator is zero for B!" << std::endl;
577  return false;
578  }
579  //Update b[i][j]
580  //If there are no observations at all for a state then the probabilities will be zero which is bad
581  //So instead we flag that B needs to be renormalized later
582  if( num > 0 ) b[i][j] = denom > 0 ? num/denom : 1.0e-5;
583  else{ b[i][j] = 0; renormB = true; }
584  }
585  }
586 
587  if( renormB ){
588  Float sum;
589  for (UINT i=0; i<numStates; i++) {
590  sum = 0.;
591  for (UINT k=0; k<numSymbols; k++){
592  b[i][k] += 1.0/numSymbols; //Add a small value to B to make sure the value will not be zero
593  sum += b[i][k];
594  }
595  for (UINT k=0; k<numSymbols; k++) b[i][k] /= sum;
596  }
597  }
598 
599  //Re-estimate Pi - only if the model type is HMM_ERGODIC, otherwise Pi[0] == 1 and everything else is 0
600  if (modelType==HMM_ERGODIC ){
601  for(k=0; k<numObs; k++){
602  const UINT T = (unsigned int)obs[k].size();
603  //Compute epsilon
604  for(t=0; t<T-1; t++){
605  denom = 0.0;
606  for(i=0; i<numStates; i++){
607  for(j=0; j<numStates; j++){
608  epsilon[k][t][i][j] = hmms[k].alpha[t][i] * a[i][j] * b[j][ obs[k][t+1] ] * hmms[k].beta[t+1][j];
609  denom += epsilon[k][t][i][j];
610  }
611  }
612  //Normalize Epsilon
613  for(i=0; i<numStates; i++){
614  for(j=0; j<numStates; j++){
615  if(denom!=0) epsilon[k][t][i][j] /= denom;
616  else{ epsilon[k][t][i][j] = 0; }
617  }
618  }
619  }
620 
621  //Compute gamma
622  for(t=0; t<T-1; t++){
623  for(i=0; i<numStates; i++){
624  gamma[k][t][i]= 0.0;
625  for(j=0; j<numStates; j++)
626  gamma[k][t][i] += epsilon[k][t][i][j];
627  }
628  }
629  }
630 
631  Float sum = 0;
632  for(i=0; i<numStates; i++){
633  sum=0.0;
634  for(k=0; k<numObs; k++){
635  sum += gamma[k][0][i];
636  }
637  pi[i] = sum / numObs;
638  }
639  }
640  }
641 
642  }while(keepTraining);
643 
644  return true;
645 
646 }
647 
649 
650  for(UINT i=0; i<observationSequence.getSize(); i++){
651  observationSequence.push_back( 0 );
652  }
653 
654  return true;
655 }
656 
657 bool DiscreteHiddenMarkovModel::save( std::fstream &file ) const{
658 
659  if(!file.is_open())
660  {
661  errorLog << "save( fstream &file ) - File is not open!" << std::endl;
662  return false;
663  }
664 
665  //Write the header info
666  file << "DISCRETE_HMM_MODEL_FILE_V1.0\n";
667 
668  //Write the base settings to the file
669  if( !MLBase::saveBaseSettingsToFile(file) ){
670  errorLog <<"save(fstream &file) - Failed to save classifier base settings to file!" << std::endl;
671  return false;
672  }
673 
674  file << "NumStates: " << numStates << std::endl;
675  file << "NumSymbols: " << numSymbols << std::endl;
676  file << "ModelType: " << modelType << std::endl;
677  file << "Delta: " << delta << std::endl;
678  file << "Threshold: " << cThreshold << std::endl;
679  file << "NumRandomTrainingIterations: " << numRandomTrainingIterations << std::endl;
680 
681  file << "A:\n";
682  for(UINT i=0; i<numStates; i++){
683  for(UINT j=0; j<numStates; j++){
684  file << a[i][j];
685  if( j+1 < numStates ) file << "\t";
686  }file << std::endl;
687  }
688 
689  file << "B:\n";
690  for(UINT i=0; i<numStates; i++){
691  for(UINT j=0; j<numSymbols; j++){
692  file << b[i][j];
693  if( j+1 < numSymbols ) file << "\t";
694  }file << std::endl;
695  }
696 
697  file<<"Pi:\n";
698  for(UINT i=0; i<numStates; i++){
699  file << pi[i];
700  if( i+1 < numStates ) file << "\t";
701  }
702  file << std::endl;
703 
704  return true;
705 }
706 
707 bool DiscreteHiddenMarkovModel::load( std::fstream &file ){
708 
709  clear();
710 
711  if(!file.is_open())
712  {
713  errorLog << "load( fstream &file ) - File is not open!" << std::endl;
714  return false;
715  }
716 
717  std::string word;
718 
719  file >> word;
720 
721  //Find the file type header
722  if(word != "DISCRETE_HMM_MODEL_FILE_V1.0"){
723  errorLog << "load( fstream &file ) - Could not find Model File Header!" << std::endl;
724  return false;
725  }
726 
727  //Load the base settings from the file
729  errorLog << "load(string filename) - Failed to load base settings from file!" << std::endl;
730  return false;
731  }
732 
733  file >> word;
734  if(word != "NumStates:"){
735  errorLog << "load( fstream &file ) - Could not find the NumStates header." << std::endl;
736  return false;
737  }
738  file >> numStates;
739 
740  file >> word;
741  if(word != "NumSymbols:"){
742  errorLog << "load( fstream &file ) - Could not find the NumSymbols header." << std::endl;
743  return false;
744  }
745  file >> numSymbols;
746 
747  file >> word;
748  if(word != "ModelType:"){
749  errorLog << "load( fstream &file ) - Could not find the modelType for the header." << std::endl;
750  return false;
751  }
752  file >> modelType;
753 
754  file >> word;
755  if(word != "Delta:"){
756  errorLog << "load( fstream &file ) - Could not find the Delta for the header." << std::endl;
757  return false;
758  }
759  file >> delta;
760 
761  file >> word;
762  if(word != "Threshold:"){
763  errorLog << "load( fstream &file ) - Could not find the Threshold for the header." << std::endl;
764  return false;
765  }
766  file >> cThreshold;
767 
768  file >> word;
769  if(word != "NumRandomTrainingIterations:"){
770  errorLog << "load( fstream &file ) - Could not find the numRandomTrainingIterations header." << std::endl;
771  return false;
772  }
773  file >> numRandomTrainingIterations;
774 
775  a.resize(numStates,numStates);
776  b.resize(numStates,numSymbols);
777  pi.resize(numStates);
778 
779  //Load the A, B and Pi matrices
780  file >> word;
781  if(word != "A:"){
782  errorLog << "load( fstream &file ) - Could not find the A matrix header." << std::endl;
783  return false;
784  }
785 
786  //Load A
787  for(UINT i=0; i<numStates; i++){
788  for(UINT j=0; j<numStates; j++){
789  file >> a[i][j];
790  }
791  }
792 
793  file >> word;
794  if(word != "B:"){
795  errorLog << "load( fstream &file ) - Could not find the B matrix header." << std::endl;
796  return false;
797  }
798 
799  //Load B
800  for(UINT i=0; i<numStates; i++){
801  for(UINT j=0; j<numSymbols; j++){
802  file >> b[i][j];
803  }
804  }
805 
806  file >> word;
807  if(word != "Pi:"){
808  errorLog << "load( fstream &file ) - Could not find the Pi matrix header." << std::endl;
809  return false;
810  }
811 
812  //Load Pi
813  for(UINT i=0; i<numStates; i++){
814  file >> pi[i];
815  }
816 
817  return true;
818 }
819 
821 
822  trainingLog << "A: " << std::endl;
823  for(UINT i=0; i<a.getNumRows(); i++){
824  for(UINT j=0; j<a.getNumCols(); j++){
825  trainingLog << a[i][j] << "\t";
826  }
827  trainingLog << std::endl;
828  }
829 
830  trainingLog << "B: " << std::endl;
831  for(UINT i=0; i<b.getNumRows(); i++){
832  for(UINT j=0; j<b.getNumCols(); j++){
833  trainingLog << b[i][j] << "\t";
834  }
835  trainingLog << std::endl;
836  }
837 
838  trainingLog << "Pi: ";
839  for(UINT i=0; i<pi.size(); i++){
840  trainingLog << pi[i] << "\t";
841  }
842  trainingLog << std::endl;
843 
844  //Check the weights all sum to 1
845  if( true ){
846  Float sum=0.0;
847  for(UINT i=0; i<a.getNumRows(); i++){
848  sum=0.0;
849  for(UINT j=0; j<a.getNumCols(); j++) sum += a[i][j];
850  if( sum <= 0.99 || sum >= 1.01 ) warningLog << "WARNING: A Row " << i <<" Sum: "<< sum << std::endl;
851  }
852 
853  for(UINT i=0; i<b.getNumRows(); i++){
854  sum=0.0;
855  for(UINT j=0; j<b.getNumCols(); j++) sum += b[i][j];
856  if( sum <= 0.99 || sum >= 1.01 ) warningLog << "WARNING: B Row " << i << " Sum: " << sum << std::endl;
857  }
858  }
859 
860  return true;
861 }
862 
863 VectorFloat DiscreteHiddenMarkovModel::getTrainingIterationLog() const{
864  return trainingIterationLog;
865 }
866 
867 GRT_END_NAMESPACE
868 
bool saveBaseSettingsToFile(std::fstream &file) const
Definition: MLBase.cpp:435
virtual bool save(std::fstream &file) const
This file contains the Random class, a useful wrapper for generating cross platform random functions...
Definition: Random.h:46
virtual bool resize(const unsigned int size)
Definition: Vector.h:133
bool clear()
clears all elements in the dictionary
Definition: Dict.h:175
unsigned int getNumRows() const
Definition: Matrix.h:574
unsigned int getNumCols() const
Definition: Matrix.h:581
bool loadBaseSettingsFromFile(std::fstream &file)
Definition: MLBase.cpp:458
This class implements a discrete Hidden Markov Model.
Float getRandomNumberUniform(Float minRange=0.0, Float maxRange=1.0)
Definition: Random.cpp:129
T * getData()
Definition: Vector.h:208
virtual bool resize(const unsigned int r, const unsigned int c)
Definition: Matrix.h:245
virtual bool load(std::fstream &file)
This is the main base class that all GRT machine learning algorithms should inherit from...
Definition: MLBase.h:72