00001 /*!@file Learn/HMM.C Hidden Markov Models */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005 // 00005 // by the University of Southern California (USC) and the iLab at USC. // 00006 // See http://iLab.usc.edu for information about this project. // 00007 // //////////////////////////////////////////////////////////////////// // 00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00010 // in Visual Environments, and Applications'' by Christof Koch and // 00011 // Laurent Itti, California Institute of Technology, 2001 (patent // 00012 // pending; application number 09/912,225 filed July 23, 2001; see // 00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00014 // //////////////////////////////////////////////////////////////////// // 00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00016 // // 00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00018 // redistribute it and/or modify it under the terms of the GNU General // 00019 // Public License as published by the Free Software Foundation; either // 00020 // version 2 of the License, or (at your option) any later version. // 00021 // // 00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00025 // PURPOSE. See the GNU General Public License for more details. // 00026 // // 00027 // You should have received a copy of the GNU General Public License // 00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00030 // Boston, MA 02111-1307 USA. // 00031 // //////////////////////////////////////////////////////////////////// // 00032 // 00033 // Primary maintainer for this file: Lior Elazary <elazary@usc.edu> 00034 // $HeadURL: $ 00035 // $Id: $ 00036 // 00037 00038 #include "Learn/HMM.H" 00039 #include "Util/Assert.H" 00040 #include "Util/log.H" 00041 #include <math.h> 00042 #include <fcntl.h> 00043 #include <limits> 00044 #include <string> 00045 00046 #include <cstdio> 00047 00048 // ###################################################################### 00049 template <class T> 00050 HMM<T>::HMM(const std::vector<T>& states, const std::vector<T>& observations, const std::string& name) : 00051 itsName(name), 00052 itsStates(states), 00053 itsObservations(observations) 00054 { 00055 itsStateTransitions.resize(states.size(), states.size()); 00056 itsStateTransitions.clear(0); 00057 00058 itsStateEmissions.resize(states.size(), observations.size()); 00059 itsStateEmissions.clear(1.0F/(float)observations.size()); //default to 1/numObservations 00060 00061 for(uint i=0; i<itsStates.size(); i++) 00062 itsStatesMap[itsStates[i]] = i; 00063 00064 for(uint i=0; i<itsObservations.size(); i++) 00065 itsObservationsMap[itsObservations[i]] = i; 00066 00067 itsCurrentPath.resize(states.size()); 00068 00069 00070 } 00071 00072 // ###################################################################### 00073 template <class T> 00074 HMM<T>::~HMM() 00075 { 00076 } 00077 00078 00079 // ###################################################################### 00080 template <class T> 00081 void HMM<T>::setStateTransition(const T fromState, const T toState, double prob) 00082 { 00083 size_t fromStateIdx = itsStatesMap[fromState]; 00084 size_t toStateIdx = itsStatesMap[toState]; 00085 00086 itsStateTransitions.setVal(fromStateIdx, toStateIdx, prob); 00087 00088 } 00089 00090 // ###################################################################### 00091 template <class T> 00092 void HMM<T>::setStateEmission(const T state, const T emission, double prob) 00093 { 00094 size_t stateIdx = itsStatesMap[state]; 00095 size_t observationIdx = itsObservationsMap[emission]; 00096 00097 itsStateEmissions.setVal(stateIdx, observationIdx, prob); 00098 00099 } 00100 00101 // ###################################################################### 00102 template <class T> 00103 void HMM<T>::setCurrentState(const T state, double prob) 00104 { 00105 size_t stateIdx = itsStatesMap[state]; 00106 00107 itsCurrentPath[stateIdx].prob = prob; 00108 itsCurrentPath[stateIdx].path.clear(); 00109 itsCurrentPath[stateIdx].path.push_back(stateIdx); 00110 } 00111 00112 // ###################################################################### 00113 template <class T> 00114 void HMM<T>::show() 00115 { 00116 printf("HMM: %s", itsName.c_str()); 00117 00118 printf("Current State\n"); 00119 for(size_t i=0; i<itsCurrentPath.size(); i++) 00120 printf("%zu\t\t", i); 00121 printf("\n"); 00122 00123 for(size_t i=0; i<itsCurrentPath.size(); i++) 00124 printf("%f\t", itsCurrentPath[i].prob); 00125 printf("\n\n"); 00126 00127 printf("State Transitions\n"); 00128 printf("\t"); 00129 for(int i=0; i<itsStateTransitions.getWidth(); i++) 00130 printf("%i\t\t", i); 00131 printf("\n"); 00132 00133 for(int i=0; i<itsStateTransitions.getWidth(); i++) 00134 { 00135 printf("%i\t", i); 00136 for(int j=0; j<itsStateTransitions.getHeight(); j++) 00137 printf("%f\t", itsStateTransitions.getVal(i,j)); 00138 printf("\n"); 00139 } 00140 printf("\n\n"); 00141 00142 printf("State Emissions\n"); 00143 printf("\t"); 00144 for(int i=0; i<itsStateEmissions.getWidth(); i++) 00145 printf("%i\t\t", i); 00146 printf("\n"); 00147 00148 for(int j=0; j<itsStateEmissions.getHeight(); j++) 00149 { 00150 printf("%i\t", j); 00151 for(int i=0; i<itsStateEmissions.getWidth(); i++) 00152 printf("%f\t", itsStateEmissions.getVal(i,j)); 00153 printf("\n"); 00154 } 00155 00156 00157 } 00158 00159 00160 // ###################################################################### 00161 template <class T> 00162 std::vector<T> HMM<T>::getLikelyStates(const std::vector<T> observations, 00163 double& maxPathProb) 00164 { 00165 if (observations.size() < 1) 00166 return std::vector<T>(); 00167 00168 for(uint i=0; i<observations.size(); i++) 00169 iteratePath(observations[i]); 00170 00171 00172 return getMaxPath(maxPathProb); 00173 } 00174 00175 00176 // ###################################################################### 00177 template <class T> 00178 std::vector<T> HMM<T>::getMaxPath(double& maxPathProb) 00179 { 00180 //Final sum/max 00181 double maxProb = 0; 00182 std::vector<size_t> maxPath; 00183 for(uint i=0; i<itsStates.size(); i++) 00184 if (itsCurrentPath[i].prob > maxProb) 00185 { 00186 maxProb = itsCurrentPath[i].prob; 00187 maxPath = itsCurrentPath[i].path; 00188 } 00189 00190 std::vector<T> maxPathType; //the path in the vector type 00191 for(uint i=0; i<maxPath.size(); i++) 00192 maxPathType.push_back(itsStates[maxPath[i]]); 00193 00194 maxPathProb = maxProb; 00195 00196 return maxPathType; 00197 } 00198 00199 // ###################################################################### 00200 template <class T> 00201 void HMM<T>::iteratePath(const T observation) 00202 { 00203 std::vector<Path> U(itsStates.size()); 00204 00205 for(uint nextState=0; nextState<itsStates.size(); nextState++) 00206 { 00207 Path nextPath; 00208 nextPath.prob = 0; 00209 00210 //Find the maxProb for each state given the observation 00211 double maxProb = 0; 00212 for(uint sourceState=0; sourceState<itsStates.size(); sourceState++) 00213 { 00214 size_t observationIdx = itsObservationsMap[observation]; 00215 00216 double p = itsStateEmissions.getVal(sourceState,observationIdx) * 00217 itsStateTransitions.getVal(sourceState,nextState); 00218 00219 double prob = itsCurrentPath[sourceState].prob * p; 00220 00221 if (prob > maxProb) 00222 { 00223 nextPath.path = itsCurrentPath[sourceState].path; 00224 nextPath.path.push_back(nextState); 00225 nextPath.prob = prob; 00226 maxProb = prob; 00227 } 00228 } 00229 U[nextState] = nextPath; 00230 } 00231 itsCurrentPath = U; //Assign the max path so far and forget the reset 00232 00233 } 00234 00235 // ###################################################################### 00236 template <class T> 00237 double HMM<T>::forward(const std::vector<T> observations) 00238 { 00239 if (observations.size() < 1) 00240 return 0; 00241 00242 double logProb = 0; 00243 00244 double sum; // partial sum 00245 00246 //restart forward 00247 itsSeqInfo.alpha.clear(); 00248 00249 // space needed for this forward iteration 00250 itsSeqInfo.alpha.resize(observations.size()); 00251 itsSeqInfo.scale.resize(observations.size()); 00252 00253 for(size_t t = 0; t<observations.size(); t++) 00254 { 00255 size_t observationIdx = itsObservationsMap[observations[t] ]; 00256 00257 //Init 00258 if(t == 0){ 00259 00260 logProb = 0.0; 00261 itsSeqInfo.scale[0] = 0.0; 00262 itsSeqInfo.alpha[0].resize(itsStates.size()); 00263 00264 00265 for(size_t state = 0; state < itsStates.size(); state++) { 00266 itsSeqInfo.alpha[0][state] = itsCurrentPath[state].prob * itsStateEmissions.getVal(state, observationIdx); 00267 itsSeqInfo.scale[0] += itsSeqInfo.alpha[0][state]; 00268 } 00269 for(size_t state = 0; state < itsStates.size(); state++) 00270 itsSeqInfo.alpha[0][state] /= itsSeqInfo.scale[0]; 00271 00272 }else{ 00273 itsSeqInfo.scale[t] = 0.0; 00274 itsSeqInfo.alpha[t].resize(itsStates.size()); 00275 for(size_t j = 0; j < itsStates.size(); j++) { 00276 sum = 0.0; 00277 for(size_t i = 0; i < itsStates.size(); i++) 00278 sum += itsSeqInfo.alpha[t-1][i] *itsStateTransitions.getVal(i,j); 00279 00280 itsSeqInfo.alpha[t][j] = sum * itsStateEmissions.getVal(j, observationIdx); 00281 itsSeqInfo.scale[t] += itsSeqInfo.alpha[t][j]; 00282 } 00283 for(size_t j = 0; j < itsStates.size(); j++) 00284 itsSeqInfo.alpha[t][j] /= itsSeqInfo.scale[t]; 00285 } 00286 } 00287 00288 /* 00289 Compute sequence probability 00290 */ 00291 for(size_t t = 0; t<observations.size(); t++) 00292 logProb += log(itsSeqInfo.scale[t]); 00293 00294 00295 return logProb; 00296 } 00297 00298 template <class T> 00299 double HMM<T>::backward(const std::vector<T> observations) 00300 { 00301 if (observations.size() < 1) 00302 return 0; 00303 00304 /* 00305 1. Initialization 00306 forward algorithm must finished before now 00307 */ 00308 for(size_t i = 0; i < itsStates.size(); i++) 00309 itsSeqInfo.beta[observations.size()-1][i] = 1.0/itsSeqInfo.scale[observations.size()-1]; 00310 00311 /* 00312 2. Induction 00313 */ 00314 for(size_t t = observations.size() - 2; /*t>=0 cannot be used for size_t*/; t--) 00315 { 00316 for(size_t i = 0; i < itsStates.size(); i++) { 00317 double sum = 0.0; 00318 for(size_t j = 0; j < itsStates.size(); j++) 00319 { 00320 size_t observationIdx = itsObservationsMap[observations[t+1] ]; 00321 sum += itsStateTransitions.getVal(i,j) 00322 * itsStateEmissions.getVal(j,observationIdx) 00323 * itsSeqInfo.beta[t+1][j]; 00324 } 00325 itsSeqInfo.beta[t][i] = sum/itsSeqInfo.scale[t]; 00326 } 00327 if(t==0) 00328 break; 00329 } 00330 00331 return 0; 00332 } 00333 00334 // ###################################################################### 00335 template <class T> 00336 void HMM<T>::train(const std::vector<T> observations, size_t numIterations) 00337 { 00338 if (observations.size() < 1) 00339 return; 00340 00341 00342 size_t loopCount = 0; 00343 00344 double numeratorA, denominatorA; 00345 double numeratorB, denominatorB; 00346 00347 //probability of sequence to hmm at previous estimation 00348 double logProb_prev; 00349 //difference of prob between iteration 00350 double delta; 00351 00352 //allocate memory space, alpha and scale will be allocated in Forward() 00353 itsSeqInfo.beta.resize(observations.size()); 00354 itsSeqInfo.gamma.resize(observations.size()); 00355 itsSeqInfo.xi.resize(observations.size()); 00356 00357 for(size_t t=0; t<observations.size(); t++){ 00358 itsSeqInfo.beta[t].resize(itsStates.size()); 00359 itsSeqInfo.gamma[t].resize(itsStates.size()); 00360 itsSeqInfo.xi[t].resize(itsStates.size()); 00361 for(size_t i=0; i<itsStates.size(); i++) 00362 itsSeqInfo.xi[t][i].resize(itsStates.size()); 00363 } 00364 00365 00366 //compute probs first time 00367 double logProb = forward(observations); 00368 backward(observations); 00369 computeGamma(observations); 00370 computeXi(observations); 00371 00372 logProb_prev = logProb; 00373 00374 do{ 00375 // reestimate probility of state i in time t=0 00376 //for(size_t i = 0; i < itsStates.size(); i++) 00377 // itsCurrentPath[i].prob = 0.0001 + 0.9999*itsSeqInfo.gamma[1][i]; 00378 00379 // reestimate transition matrix and prob of symbols to states 00380 for(size_t i = 0; i < itsStates.size(); i++) { 00381 denominatorA = 0.0; 00382 for(size_t t = 0; t < observations.size() - 1; t++) 00383 denominatorA += itsSeqInfo.gamma[t][i]; 00384 00385 for(size_t j = 0; j < itsStates.size(); j++) { 00386 numeratorA = 0.0; 00387 for(size_t t = 0; t < observations.size() - 1; t++) 00388 numeratorA += itsSeqInfo.xi[t][i][j]; 00389 itsStateTransitions.setVal(i, j, 0.0001 + 0.9999*numeratorA/denominatorA); 00390 } 00391 00392 denominatorB = denominatorA + itsSeqInfo.gamma[observations.size()-1][i]; 00393 for(size_t k = 0; k < itsObservations.size(); k++) { 00394 numeratorB = 0.0; 00395 for(size_t t = 0; t < observations.size(); t++) { 00396 size_t observationIdx = itsObservationsMap[observations[t] ]; 00397 if(observationIdx == k) 00398 numeratorB += itsSeqInfo.gamma[t][i]; 00399 } 00400 itsStateEmissions.setVal(i,k, 0.0001 + 0.9999*numeratorB/denominatorB); 00401 } 00402 } 00403 00404 // compute probs by new model 00405 logProb = forward(observations); 00406 backward(observations); 00407 computeGamma(observations); 00408 computeXi(observations); 00409 00410 // delta prob between old and estimated model 00411 delta = logProb - logProb_prev; 00412 logProb_prev = logProb; 00413 loopCount++; 00414 } while(loopCount < numIterations);//loop utill log probability converged. 00415 00416 //return loopCount; 00417 } 00418 00419 // ###################################################################### 00420 template <class T> 00421 void HMM<T>::train(const std::vector< std::vector<T> > observations, size_t numIterations) 00422 { 00423 00424 if (observations.size() < 1) 00425 return; 00426 00427 //Run the single ibservation trainer 00428 if (observations.size() == 1) 00429 { 00430 train(observations[0], numIterations); 00431 return; 00432 } 00433 00434 size_t loopCount = 0; 00435 00436 //FIXME (observations[0].size() may not equale observations[1].size() 00437 itsSeqInfo.beta.resize(observations[0].size()); 00438 itsSeqInfo.gamma.resize(observations[0].size()); 00439 itsSeqInfo.xi.resize(observations[0].size()); 00440 00441 for(size_t t=0; t<observations[0].size(); t++){ 00442 itsSeqInfo.beta[t].resize(itsStates.size()); 00443 itsSeqInfo.gamma[t].resize(itsStates.size()); 00444 itsSeqInfo.xi[t].resize(itsStates.size()); 00445 for(size_t i=0; i<itsStates.size(); i++) 00446 itsSeqInfo.xi[t][i].resize(itsStates.size()); 00447 } 00448 00449 double numeratorA, numeratorA_partial, denominatorA, denominatorA_partial; 00450 double numeratorB, numeratorB_partial, denominatorB, denominatorB_partial; 00451 00452 double logProbSum_prev = 0.0; 00453 double logProbSum = 0.0; 00454 double delta; //difference of prob between iteration 00455 00456 /* 00457 Initialization 00458 */ 00459 std::vector<SeqInfo> seqsInfo(observations.size()); 00460 00461 for(size_t seq = 0; seq < observations.size(); seq++) 00462 { 00463 double logProb = forward(observations[seq]); 00464 backward(observations[seq]); 00465 computeGamma(observations[seq]); 00466 computeXi(observations[seq]); 00467 logProbSum_prev += logProb; 00468 00469 seqsInfo[seq].prob = logProb; 00470 seqsInfo[seq].gamma = itsSeqInfo.gamma; 00471 seqsInfo[seq].xi = itsSeqInfo.xi; 00472 00473 } 00474 00475 //Iteration 00476 do{ 00477 // reestimate probility of state i in time t=0 00478 //for(i = 0; i < hmm.N; i++) 00479 // hmm.pi[i] = 0.001 + 0.999*curSeq->gamma[1][i]; 00480 00481 // reestimate transition matrix and prob of symbols to states 00482 for(size_t i = 0; i < itsStates.size(); i++) { 00483 denominatorA = 0.0; 00484 denominatorB = 0.0; 00485 00486 for(size_t seq = 0; seq < observations.size(); seq++) 00487 { 00488 double logProb = seqsInfo[seq].prob; 00489 std::vector< std::vector<double> >& gamma = seqsInfo[seq].gamma; 00490 denominatorA_partial = 0.0; 00491 for(size_t t = 0; t < observations[seq].size() - 1; t++) 00492 denominatorA_partial += gamma[t][i]; 00493 denominatorB_partial = denominatorA_partial + gamma[observations[seq].size()-1][i]; 00494 denominatorA += denominatorA_partial/exp(logProb); 00495 denominatorB += denominatorB_partial/exp(logProb); 00496 } 00497 00498 for(size_t j = 0; j < itsStates.size(); j++) { 00499 numeratorA = 0.0; 00500 for(size_t seq = 0; seq < observations.size(); seq++) 00501 { 00502 std::vector< std::vector< std::vector<double> > >& xi = seqsInfo[seq].xi; 00503 numeratorA_partial = 0.0; 00504 for(size_t t = 0; t < observations[seq].size() - 1; t++) 00505 numeratorA_partial += xi[t][i][j]; 00506 numeratorA += numeratorA_partial/exp(seqsInfo[seq].prob); 00507 } 00508 itsStateTransitions.setVal(i, j, 0.0001 + 0.9999*numeratorA/denominatorA); 00509 } 00510 00511 for(size_t k = 0; k < itsObservations.size(); k++) { 00512 numeratorB = 0.0; 00513 for(size_t seq = 0; seq < observations.size(); seq++) 00514 { 00515 std::vector< std::vector<double> >& gamma = seqsInfo[seq].gamma; 00516 numeratorB_partial = 0.0; 00517 for(size_t t = 0; t < observations[seq].size(); t++) 00518 { 00519 size_t observationIdx = itsObservationsMap[observations[seq][t] ]; 00520 if(observationIdx == k) 00521 numeratorB_partial += gamma[t][i]; 00522 } 00523 numeratorB += numeratorB_partial/exp(seqsInfo[seq].prob); 00524 } 00525 itsStateEmissions.setVal(i,k, 0.0001 + 0.9999*numeratorB/denominatorB); 00526 } 00527 } 00528 00529 // compute probs by new model 00530 for(size_t seq = 0; seq < observations.size(); seq++) 00531 { 00532 double logProb = forward(observations[seq]); 00533 backward(observations[seq]); 00534 computeGamma(observations[seq]); 00535 computeXi(observations[seq]); 00536 logProbSum += logProb; 00537 00538 seqsInfo[seq].prob = logProb; 00539 seqsInfo[seq].gamma = itsSeqInfo.gamma; 00540 seqsInfo[seq].xi = itsSeqInfo.xi; 00541 } 00542 00543 // delta prob between old and estimated model 00544 delta = logProbSum - logProbSum_prev; 00545 logProbSum_prev = logProbSum; 00546 loopCount++; 00547 }while(loopCount < numIterations);//loop utill log probability converged. 00548 00549 } 00550 00551 // ###################################################################### 00552 template <class T> 00553 void HMM<T>::computeGamma(const std::vector<T> observations) 00554 { 00555 00556 for(size_t t = 0; t < observations.size(); t++) { 00557 double denominator = 0.0; 00558 for(size_t j = 0; j < itsStates.size(); j++) { 00559 itsSeqInfo.gamma[t][j] = itsSeqInfo.alpha[t][j] * itsSeqInfo.beta[t][j]; 00560 denominator += itsSeqInfo.gamma[t][j]; 00561 } 00562 00563 for(size_t i = 0; i < itsStates.size(); i++) 00564 itsSeqInfo.gamma[t][i] = itsSeqInfo.gamma[t][i]/denominator; 00565 } 00566 00567 } 00568 00569 // ###################################################################### 00570 template <class T> 00571 void HMM<T>::computeXi(const std::vector<T> observations) 00572 { 00573 for(size_t t = 0; t < observations.size()-1; t++) { 00574 double denominator = 0.0; 00575 for(size_t i = 0; i < itsStates.size(); i++) 00576 for(size_t j = 0; j < itsStates.size(); j++) { 00577 size_t observationIdx = itsObservationsMap[observations[t+1] ]; 00578 itsSeqInfo.xi[t][i][j] = itsSeqInfo.alpha[t][i] * itsSeqInfo.beta[t+1][j] * 00579 itsStateTransitions.getVal(i,j) * itsStateEmissions.getVal(j, observationIdx); 00580 denominator += itsSeqInfo.xi[t][i][j]; 00581 } 00582 00583 for(size_t i = 0; i < itsStates.size(); i++) 00584 for(size_t j = 0; j < itsStates.size(); j++) 00585 itsSeqInfo.xi[t][i][j] /= denominator; 00586 } 00587 00588 } 00589 00590 00591 00592 template class HMM<std::string>; 00593 template class HMM<uint>; 00594 00595 00596 // ###################################################################### 00597 /* So things look consistent in everyone's emacs... */ 00598 /* Local Variables: */ 00599 /* indent-tabs-mode: nil */ 00600 /* End: */