HMMLib.cpp

00001 #include <iostream>
00002 #include <cmath>
00003 #include "Learn/WiimoteGR/HMMLib.h"
00004 
00005 using namespace std;
00006 
00007 namespace WiimoteGR{
00008 
00009     HMM::HMM(const char* gestureName, const Quantizer& quantizer,
00010         const char* modelStyle, bool trained, size_t N,
00011         double *const A_in, double *const B_in, double *const pi_in)
00012         : gestureName(gestureName),
00013           quantizerName(quantizer.name),
00014           modelStyle(modelStyle),
00015           trained(trained),
00016           N(N),
00017           M(quantizer.M)
00018     {
00019         A.assign(A_in, A_in+N*N);
00020         B.assign(B_in, B_in+N*M);
00021         pi.assign(pi_in, pi_in+N);
00022     }
00023 
00024     void HMMLib::ShowHMM(HMM& hmm)
00025     {
00026         cout.width(15);
00027         cout << "----------------------------------------------------------------------" << endl;
00028         cout << "A " << (hmm.trained?"trained":"initial") << " HMM of " << hmm.gestureName << " gesture, " << endl;
00029         cout << "Trained with observation sequences quantized by " << hmm.quantizerName << "." << endl;
00030         cout << "with " << hmm.N << " states and " << hmm.M << " observable symbols." << endl << endl;
00031         cout << "pi:" << endl;
00032         for(size_t i=0; i<hmm.N-1; i++){
00033             cout.width(15);
00034             cout << hmm.pi[i];
00035         }
00036         cout.width(15);
00037         cout << hmm.pi[hmm.N-1] << endl;
00038         cout << endl;
00039         cout << "A:" << endl;
00040         for(size_t i=0; i<hmm.N; i++){
00041             for(size_t j=0; j<hmm.N-1; j++){
00042                 cout.width(15);
00043                 cout << hmm.TranProb(i,j);
00044             }
00045             cout.width(15);
00046             cout << hmm.TranProb(i,hmm.N-1) << endl;
00047         }
00048         cout << endl;
00049         cout << "B^T:" << endl;
00050         for(size_t k=0; k<hmm.M; k++){
00051             for(size_t j=0; j<hmm.N-1; j++){
00052                 cout.width(15);
00053                 cout << hmm.ObsProb(j,k);
00054             }
00055             cout.width(15);
00056             cout << hmm.ObsProb(hmm.N-1,k) << endl;
00057         }
00058         cout << endl;
00059     }
00060 
00061     const HMM& HMMLib::Recognize(const vector<HMM>& HMMVec, TimeSlot& seq)
00062     {
00063         vector<HMM>::const_iterator recognizedHMM = HMMVec.begin();
00064         for(vector<HMM>::const_iterator curHMM = HMMVec.begin()+1; curHMM < HMMVec.end(); curHMM++){
00065             if( SeqLogProb(*curHMM,seq,false) > SeqLogProb(*recognizedHMM,seq,false) )
00066                 recognizedHMM = curHMM;
00067         }
00068         return *recognizedHMM;
00069     }
00070 
00071     double HMMLib::SeqLogProb(const HMM& hmm, TimeSlot& seq, bool restart)
00072     {
00073         Forward(hmm, seq, restart);
00074         return seq.logProb[&hmm];
00075     }
00076 
00077     int HMMLib::EstimateModel(HMM& hmm, TimeSlot& seq, size_t maxIteration)
00078     {
00079         if(hmm.M != seq.M)
00080             return -1;
00081 
00082         vector<size_t>& o = seq.o;
00083         //vector< vector<double> >& alpha = seq.alpha[&hmm];
00084         //vector< vector<double> >& beta = seq.beta;
00085         vector< vector<double> >& gamma = seq.gamma;
00086         vector< vector< vector<double> > >& xi = seq.xi;
00087         
00088         //length of time
00089         size_t T = o.size();
00090 
00091         //indices
00092         size_t i, j, k;
00093         size_t t;
00094 
00095         size_t loopCount = 0;
00096 
00097         double numeratorA, denominatorA;
00098         double numeratorB, denominatorB;
00099 
00100         //probability of sequence to hmm at previous estimation
00101         double logProb_prev;
00102         //difference of prob between iteration
00103         double delta;
00104 
00105         /*
00106         Initialization
00107         */
00108 
00109         //allocate memory space, alpha and scale will be allocated in Forward()
00110         seq.beta.resize(T);
00111         seq.gamma.resize(T);
00112         seq.xi.resize(T);
00113         for(size_t t=0; t<T; t++){
00114             seq.beta[t].resize(hmm.N);
00115             seq.gamma[t].resize(hmm.N);
00116             seq.xi[t].resize(hmm.N);
00117             for(size_t i=0; i<hmm.N; i++)
00118                 seq.xi[t][i].resize(hmm.N);
00119         }
00120 
00121         //compute probs first time
00122         Forward(hmm,seq,true);
00123         Backward(hmm,seq);
00124         ComputeGamma(hmm,seq);
00125         ComputeXi(hmm,seq);
00126 
00127         logProb_prev = seq.logProb[&hmm];
00128 
00129         /*
00130         Iteration
00131         */
00132         do{
00133             // reestimate probility of state i in time t=0
00134             for(i = 0; i < hmm.N; i++)
00135                 hmm.pi[i] = 0.0001 + 0.9999*gamma[1][i];
00136 
00137             // reestimate transition matrix and prob of symbols to states
00138             for(i = 0; i < hmm.N; i++) {
00139                 denominatorA = 0.0;
00140                 for(t = 0; t < T - 1; t++)
00141                     denominatorA += gamma[t][i];
00142 
00143                 for(j = 0; j < hmm.N; j++) {
00144                     numeratorA = 0.0;
00145                     for(t = 0; t < T - 1; t++)
00146                         numeratorA += xi[t][i][j];
00147                     hmm.TranProb(i,j) = 0.0001 + 0.9999*numeratorA/denominatorA;
00148                 }
00149 
00150                 denominatorB = denominatorA + gamma[T-1][i];
00151                 for(k = 0; k < hmm.M; k++) {
00152                     numeratorB = 0.0;
00153                     for(t = 0; t < T; t++) {
00154                         if(o[t] == k)
00155                             numeratorB += gamma[t][i];
00156                     }
00157                     hmm.ObsProb(i,k) = 0.0001 + 0.9999*numeratorB/denominatorB;
00158                 }
00159             }
00160 
00161             // compute probs by new model
00162             Forward(hmm,seq,true);
00163             Backward(hmm,seq);
00164             ComputeGamma(hmm,seq);
00165             ComputeXi(hmm,seq);
00166 
00167             // delta prob between old and estimated model
00168             delta = seq.logProb[&hmm] - logProb_prev;
00169             logProb_prev = seq.logProb[&hmm];
00170             loopCount++;
00171         }while(loopCount < maxIteration);//loop utill log probability converged.
00172         return loopCount;
00173     }
00174 
00175 
00176     int HMMLib::EstimateModelBySeqs(HMM& hmm, vector<TimeSlot>& seqs, size_t maxIteration)
00177     {
00178         if( !CheckAndAllocateMemory(hmm,seqs) )
00179             return -1; //there is wrong sequence
00180 
00181         size_t i, j, k;
00182         size_t t;
00183         size_t T;
00184 
00185         size_t loopCount = 0;
00186 
00187         double numeratorA, numeratorA_partial, denominatorA, denominatorA_partial;
00188         double numeratorB, numeratorB_partial, denominatorB, denominatorB_partial;
00189 
00190         double logProbSum_prev = 0.0;
00191         double logProbSum = 0.0;
00192         double delta; //difference of prob between iteration
00193 
00194         /*
00195         Initialization
00196         */
00197         vector<TimeSlot>::iterator curSeq;
00198         for(curSeq = seqs.begin(); curSeq < seqs.end(); curSeq++){
00199             Forward(hmm, *curSeq, true);
00200             Backward(hmm, *curSeq);
00201             ComputeGamma(hmm, *curSeq);
00202             ComputeXi(hmm, *curSeq);
00203             logProbSum_prev += curSeq->logProb[&hmm];
00204         }
00205         /*
00206         Iteration
00207         */
00208         do{
00209             // reestimate probility of state i in time t=0
00210             //for(i = 0; i < hmm.N; i++)
00211             //    hmm.pi[i] = 0.001 + 0.999*curSeq->gamma[1][i];
00212 
00213             // reestimate transition matrix and prob of symbols to states
00214             for(i = 0; i < hmm.N; i++) {
00215                 denominatorA = 0.0;
00216                 denominatorB = 0.0;
00217                 for(curSeq = seqs.begin(); curSeq < seqs.end(); curSeq++){
00218                     double& logProb = curSeq->logProb[&hmm];
00219                     vector< vector<double> >& gamma = curSeq->gamma;
00220                     T = curSeq->o.size();
00221                     denominatorA_partial = 0.0;
00222                     for(t = 0; t < T - 1; t++)
00223                         denominatorA_partial += gamma[t][i];
00224                     denominatorB_partial = denominatorA_partial + gamma[T-1][i];
00225                     denominatorA += denominatorA_partial/exp(logProb);
00226                     denominatorB += denominatorB_partial/exp(logProb);
00227                 }
00228 
00229                 for(j = 0; j < hmm.N; j++) {
00230                     numeratorA = 0.0;
00231                     for(curSeq = seqs.begin(); curSeq < seqs.end(); curSeq++){
00232                         vector< vector< vector<double> > >& xi = curSeq->xi;
00233                         T = curSeq->o.size();
00234                         numeratorA_partial = 0.0;
00235                         for(t = 0; t < T - 1; t++)
00236                             numeratorA_partial += xi[t][i][j];
00237                         numeratorA += numeratorA_partial/exp(curSeq->logProb[&hmm]);
00238                     }
00239                     hmm.TranProb(i,j) = 0.0001 + 0.9999*numeratorA/denominatorA;
00240                 }
00241 
00242                 for(k = 0; k < hmm.M; k++) {
00243                     numeratorB = 0.0;
00244                     for(curSeq = seqs.begin(); curSeq < seqs.end(); curSeq++){
00245                         vector< vector<double> >& gamma = curSeq->gamma;
00246                         vector<size_t>& o = curSeq->o;
00247                         T = o.size();
00248                         numeratorB_partial = 0.0;
00249                         for(t = 0; t < T; t++) {
00250                             if(o[t] == k)
00251                                 numeratorB_partial += gamma[t][i];
00252                         }
00253                         numeratorB += numeratorB_partial/exp(curSeq->logProb[&hmm]);
00254                     }
00255                     hmm.ObsProb(i,k) = 0.0001 + 0.9999*numeratorB/denominatorB;
00256                 }
00257             }
00258 
00259             // compute probs by new model
00260             for(curSeq = seqs.begin(); curSeq < seqs.end(); curSeq++){
00261 
00262                 Forward(hmm, *curSeq, true);
00263                 Backward(hmm, *curSeq);
00264                 ComputeGamma(hmm, *curSeq);
00265                 ComputeXi(hmm, *curSeq);
00266                 logProbSum += curSeq->logProb[&hmm];
00267             }
00268 
00269             // delta prob between old and estimated model
00270             delta = logProbSum - logProbSum_prev;
00271             logProbSum_prev = logProbSum;
00272             loopCount++;
00273         }while(loopCount < maxIteration);//loop utill log probability converged.
00274         return loopCount;
00275     }
00276 
00277     bool HMMLib::CheckAndAllocateMemory(const HMM &hmm, vector<TimeSlot>& seqs)
00278     {
00279         /*
00280         check validity:
00281         invalid: one or more of the sequences's M is not match to hmm.M,
00282                  return false
00283         */
00284         vector<TimeSlot>::iterator curSeq;
00285         for(curSeq = seqs.begin(); curSeq < seqs.end(); curSeq++){
00286             if( curSeq->M != hmm.M)
00287                 return false;
00288         }
00289         size_t N = hmm.N;
00290         size_t T;
00291         for(curSeq = seqs.begin(); curSeq < seqs.end(); curSeq++){
00292             T = curSeq->o.size();
00293             /*
00294             allocation of curSeq->alpha and curSeq->scale will be done in Forward()
00295             for Forward()'s realtime computation style
00296             */
00297             curSeq->beta.resize(T);
00298             curSeq->gamma.resize(T);
00299             curSeq->xi.resize(T);
00300             for(size_t t=0; t<T; t++){
00301                 //set values to zero although not necessary
00302                 curSeq->beta[t].resize(N,0);
00303                 curSeq->gamma[t].resize(N,0);
00304                 curSeq->xi[t].resize(N);
00305                 for(size_t i=0; i<N; i++)
00306                     curSeq->xi[t][i].resize(N,0);
00307             }
00308         }
00309         return true;
00310     }
00311 
00312     /*
00313     Forward Algorithm
00314     compute P(O|HMM) by newly added symbols if restart==false
00315     else recompute alpha and scale and probability
00316     */
00317     void HMMLib::Forward(const HMM& hmm, TimeSlot& seq, bool restart)
00318     {
00319         //references for simplifying code
00320         const HMM* hmmPtr = &hmm;
00321         vector<size_t>& o = seq.o;
00322         vector< vector<double> >& alpha = seq.alpha[hmmPtr];
00323         vector<double>& scale = seq.scale[hmmPtr];
00324         double& logProb = seq.logProb[hmmPtr];
00325 
00326         size_t i, j; // state indices
00327         size_t t;    // time index
00328         double sum;  // partial sum
00329 
00330         //restart forward
00331         if(restart)
00332             alpha.clear();
00333 
00334         // number of observable symbols processed,
00335         // is also beginning of time iteration.
00336         size_t t_begin = alpha.size();
00337         size_t T = o.size(); // time length
00338 
00339         // space needed for this forward iteration
00340         alpha.resize(T);
00341         scale.resize(T);
00342 
00343         /*
00344         Only do iteration necessary for newly added
00345         symbols of input sequence.
00346         */
00347         for(t = t_begin; t < T; t++) {
00348             /*
00349             Do initialization if forward algorithm haven't been applied to
00350             the input sequence before.
00351             */
00352             if(t == 0){
00353                 logProb = 0.0;
00354                 scale[0] = 0.0;
00355                 alpha[0].resize(hmm.N);
00356                 for(i = 0; i < hmm.N; i++) {
00357                     alpha[0][i] = hmm.pi[i] * hmm.ObsProb(i,o[0]);
00358                     scale[0] += alpha[0][i];
00359                 }
00360                 for(i = 0; i < hmm.N; i++)
00361                     alpha[0][i] /= scale[0];
00362             }else{
00363                 scale[t] = 0.0;
00364                 alpha[t].resize(hmm.N);
00365                 for(j = 0; j < hmm.N; j++) {
00366                     sum = 0.0;
00367                     for(i = 0; i < hmm.N; i++)
00368                         sum += alpha[t-1][i] * hmm.TranProb(i,j);
00369 
00370                     alpha[t][j] = sum * hmm.ObsProb(j,o[t]);
00371                     scale[t] += alpha[t][j];
00372                 }
00373                 for(j = 0; j < hmm.N; j++)
00374                     alpha[t][j] /= scale[t];
00375             }
00376         }
00377 
00378         /*
00379         Compute sequence probability
00380         */
00381         for(t = t_begin; t < T; t++){
00382             logProb += log(scale[t]);
00383         }
00384     }
00385 
00386     /*
00387     Backward Algorithm:
00388     should not be executed before Forward()
00389     */
00390     void HMMLib::Backward(const HMM& hmm, TimeSlot& seq)
00391     {
00392         //references for simplifying code
00393         vector<size_t>& o = seq.o;
00394         vector< vector<double> >& beta = seq.beta;
00395         vector<double>& scale = seq.scale[&hmm];
00396 
00397         size_t i, j; // state indices
00398         size_t t;    // time index
00399         double sum;  // partial sum
00400 
00401         size_t T = seq.o.size(); // time length
00402 
00403         /*
00404         1. Initialization
00405         forward algorithm must finished before now
00406         */
00407         for(i = 0; i < hmm.N; i++)
00408             beta[T-1][i] = 1.0/scale[T-1];
00409 
00410         /*
00411         2. Induction
00412         */
00413         for(t = T - 2; /*t>=0 cannot be used for size_t*/; t--) {
00414             for(i = 0; i < hmm.N; i++) {
00415                 sum = 0.0;
00416                 for(j = 0; j < hmm.N; j++)
00417                     sum += hmm.TranProb(i,j)
00418                     * hmm.ObsProb(j,o[t+1])
00419                     * beta[t+1][j];
00420                 beta[t][i] = sum/scale[t];
00421             }
00422             if(t==0)
00423                 break;
00424         }
00425 
00426     }
00427 
00428     void HMMLib::ComputeGamma(const HMM& hmm, TimeSlot& seq)
00429     {
00430         //references for simplifying code
00431         //vector<size_t>& o = seq.o;
00432         vector< vector<double> >& alpha = seq.alpha[&hmm];
00433         vector< vector<double> >& beta = seq.beta;
00434         vector< vector<double> >& gamma = seq.gamma;
00435  
00436         size_t i, j; // state indices
00437         size_t t;    // time index
00438         double denominator;
00439 
00440         size_t T = seq.o.size(); // time length
00441 
00442         for(t = 0; t < T; t++) {
00443             denominator = 0.0;
00444             for(j = 0; j < hmm.N; j++) {
00445                 gamma[t][j] = alpha[t][j] * beta[t][j];
00446                 denominator += gamma[t][j];
00447             }
00448 
00449             for(i = 0; i < hmm.N; i++)
00450                 gamma[t][i] = gamma[t][i]/denominator;
00451         }
00452     }
00453     void HMMLib::ComputeXi(const HMM& hmm, TimeSlot& seq)
00454     {
00455         //references for simplifying code
00456         vector<size_t>& o = seq.o;
00457         vector< vector<double> >& alpha = seq.alpha[&hmm];
00458         vector< vector<double> >& beta = seq.beta;
00459         //vector< vector<double> >& gamma = seq.gamma;
00460         vector< vector< vector<double> > >& xi = seq.xi;
00461 
00462         size_t i, j; // state indices
00463         size_t t;    // time index
00464         double denominator;
00465 
00466         size_t T = seq.o.size(); // time length
00467 
00468         for(t = 0; t < T-1; t++) {
00469             denominator = 0.0;
00470             for(i = 0; i < hmm.N; i++)
00471                 for(j = 0; j < hmm.N; j++) {
00472                     xi[t][i][j] = alpha[t][i] * beta[t+1][j] * hmm.TranProb(i,j) * hmm.ObsProb(j,o[t+1]);
00473                     denominator += xi[t][i][j];
00474                 }
00475 
00476                 for(i = 0; i < hmm.N; i++)
00477                     for(j = 0; j < hmm.N; j++)
00478                         xi[t][i][j] /= denominator;
00479         }
00480     }
00481 }
Generated on Sun May 8 08:40:59 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3