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