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 }