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