latest version v1.9 - last update 10 Apr 2010 |
00001 /* 00002 * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006 00003 * Lehrstuhl fuer Technische Informatik, RWTH-Aachen, Germany 00004 * 00005 * This file is part of the LTI-Computer Vision Library (LTI-Lib) 00006 * 00007 * The LTI-Lib is free software; you can redistribute it and/or 00008 * modify it under the terms of the GNU Lesser General Public License (LGPL) 00009 * as published by the Free Software Foundation; either version 2.1 of 00010 * the License, or (at your option) any later version. 00011 * 00012 * The LTI-Lib is distributed in the hope that it will be 00013 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty 00014 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 * GNU Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with the LTI-Lib; see the file LICENSE. If 00019 * not, write to the Free Software Foundation, Inc., 59 Temple Place - 00020 * Suite 330, Boston, MA 02111-1307, USA. 00021 */ 00022 00023 00024 /*---------------------------------------------------------------- 00025 * project ....: LTI Digital Image/Signal Processing Library 00026 * file .......: ltiGaussianMixtureModel.h 00027 * authors ....: Jochen Wickel 00028 * organization: LTI, RWTH Aachen 00029 * creation ...: 25.9.2000 00030 * revisions ..: $Id: ltiGaussianMixtureModel.h,v 1.11 2006/02/08 12:23:56 ltilib Exp $ 00031 */ 00032 00033 #ifndef _LTI_GAUSSIAN_MIXTURE_MODEL_H_ 00034 #define _LTI_GAUSSIAN_MIXTURE_MODEL_H_ 00035 00036 #include "ltiConfig.h" 00037 #include "ltiStatisticsFunctor.h" 00038 #include "ltiMeansFunctor.h" 00039 #include "ltiVarianceFunctor.h" 00040 #include "ltiMatrixInversion.h" 00041 #include "ltiL2Distance.h" 00042 #include "ltiProgressInfo.h" 00043 #include "ltiMatrixDecomposition.h" 00044 00045 00046 #ifdef _LTI_WIN32 00047 #define random rand 00048 #endif 00049 00050 00051 namespace lti { 00052 /** 00053 * Functor which estimates a gaussian mixture model of a given data set. 00054 * See documentation of the apply() methods for details. 00055 * Please note that, even though this is a template class, it does not 00056 * make much sense to use it with anything but floating-point types. 00057 * The higher the precision, the better. So, if you use 00058 * gaussianMixtureModel<float> and get crappy results, you should not 00059 * wonder. If you get reasonable results, you're just lucky. 00060 * Other template arguments than float or double probably won't even 00061 * compile. 00062 */ 00063 template <class T> class gaussianMixtureModel : public statisticsFunctor { 00064 public: 00065 /** 00066 * The parameters for the class gaussianMixtureModel 00067 */ 00068 class parameters : public statisticsFunctor::parameters { 00069 public: 00070 /** 00071 * Default constructor 00072 */ 00073 parameters() 00074 : statisticsFunctor::parameters() { 00075 partialIterations=50; 00076 iterations=100; 00077 forceIterations=false; 00078 numberOfComponents=3; 00079 useSM=false; 00080 lambda=T(0.1); 00081 emergencyLambda=T(0.1); 00082 cMax=5; 00083 epsilon=T(0.2); 00084 reportProgress=false; 00085 }; 00086 00087 /** 00088 * copy constructor 00089 * @param other the parameters object to be copied 00090 */ 00091 parameters(const parameters& other) 00092 : statisticsFunctor::parameters() { 00093 copy(other); 00094 }; 00095 00096 /** 00097 * destructor 00098 */ 00099 ~parameters() { 00100 }; 00101 00102 /** 00103 * returns name of this type 00104 */ 00105 const char* getTypeName() const { 00106 return "gaussianMixtureModel::parameters"; 00107 }; 00108 00109 /** 00110 * copy the contents of a parameters object 00111 * @param other the parameters object to be copied 00112 * @return a reference to this parameters object 00113 */ 00114 parameters& copy(const parameters& other) { 00115 # ifndef _LTI_MSC_6 00116 // MS Visual C++ 6 is not able to compile this... 00117 statisticsFunctor::parameters::copy(other); 00118 # else 00119 // ...so we have to use this workaround. 00120 // Conditional on that, copy may not be virtual. 00121 statisticsFunctor::parameters& 00122 (statisticsFunctor::parameters::* p_copy) 00123 (const statisticsFunctor::parameters&) = 00124 statisticsFunctor::parameters::copy; 00125 (this->*p_copy)(other); 00126 # endif 00127 00128 numberOfComponents=other.numberOfComponents; 00129 partialIterations=other.partialIterations; 00130 iterations=other.iterations; 00131 forceIterations=other.forceIterations; 00132 useSM=other.useSM; 00133 lambda=other.lambda; 00134 emergencyLambda=other.emergencyLambda; 00135 cMax=other.cMax; 00136 epsilon=other.epsilon; 00137 reportProgress=other.reportProgress; 00138 return *this; 00139 }; 00140 00141 # ifndef _LTI_MSC_6 00142 /** 00143 * write the parameters in the given ioHandler 00144 * @param handler the ioHandler to be used 00145 * @param complete if true (the default) the enclosing begin/end will 00146 * be also written, otherwise only the data block will be 00147 * written. 00148 * @return true if write was successful 00149 */ 00150 virtual bool write(ioHandler& handler,const bool complete=true) const 00151 # else 00152 /** 00153 * workaround for MS-VC bug. Never use this method directly, 00154 * use write() instead 00155 */ 00156 bool writeMS(ioHandler& handler,const bool complete=true) const 00157 # endif 00158 { 00159 bool b = true; 00160 if (complete) { 00161 b = handler.writeBegin(); 00162 } 00163 00164 if (b) { 00165 lti::write(handler,"numberOfComponents",numberOfComponents); 00166 lti::write(handler,"iterations",iterations); 00167 lti::write(handler,"forceIterations",iterations); 00168 lti::write(handler,"partialIterations",partialIterations); 00169 lti::write(handler, "useSM", useSM); 00170 lti::write(handler, "lambda", lambda); 00171 lti::write(handler, "emergencyLambda", emergencyLambda); 00172 lti::write(handler, "Cmax", cMax); 00173 lti::write(handler, "epsilon", epsilon); 00174 lti::write(handler, "reportProgress", reportProgress); 00175 } 00176 00177 # ifndef _LTI_MSC_6 00178 // This is the standard C++ code, which MS Visual C++ 6 is not 00179 // able to compile... 00180 b = b && statisticsFunctor::parameters::write(handler,false); 00181 # else 00182 bool (functor::parameters::* p_writeMS)(ioHandler&, 00183 const bool) const = 00184 functor::parameters::writeMS; 00185 b = b && (this->*p_writeMS)(handler,false); 00186 # endif 00187 00188 if (complete) { 00189 b = b && handler.writeEnd(); 00190 } 00191 00192 return b; 00193 } 00194 00195 # ifdef _LTI_MSC_6 00196 /** 00197 * write the parameters in the given ioHandler 00198 * @param handler the ioHandler to be used 00199 * @param complete if true (the default) the enclosing begin/end will 00200 * be also written, otherwise only the data block will be 00201 * written. 00202 * @return true if write was successful 00203 */ 00204 virtual bool write(ioHandler& handler, 00205 const bool complete = true) const { 00206 // ...we need this workaround to cope with another really 00207 // awful MSVC bug. 00208 return writeMS(handler,complete); 00209 } 00210 # endif 00211 00212 # ifndef _LTI_MSC_6 00213 /** 00214 * read the parameters from the given ioHandler 00215 * @param handler the ioHandler to be used 00216 * @param complete if true (the default) the enclosing begin/end will 00217 * be also read, otherwise only the data block will be read. 00218 * @return true if write was successful 00219 */ 00220 virtual bool read(ioHandler& handler,const bool complete = true) 00221 # else 00222 /** 00223 * workaround for MS-VC bug. Never use this method directly, 00224 * use read() instead 00225 */ 00226 bool readMS(ioHandler& handler,const bool complete=true) 00227 # endif 00228 { 00229 bool b = true; 00230 if (complete) { 00231 b = handler.readBegin(); 00232 } 00233 00234 if (b) { 00235 lti::read(handler,"numberOfComponents",numberOfComponents); 00236 lti::read(handler,"iterations",iterations); 00237 lti::read(handler,"forceIterations",iterations); 00238 lti::read(handler,"partialIterations",partialIterations); 00239 lti::read(handler, "useSM", useSM); 00240 lti::read(handler, "lambda", lambda); 00241 lti::read(handler, "emergencyLambda", emergencyLambda); 00242 lti::read(handler, "Cmax", cMax); 00243 lti::read(handler, "epsilon", epsilon); 00244 lti::read(handler, "reportProgress", reportProgress); 00245 } 00246 00247 # ifndef _LTI_MSC_6 00248 // This is the standard C++ code, which MS Visual C++ 6 is not 00249 // able to compile... 00250 b = b && statisticsFunctor::parameters::read(handler,false); 00251 # else 00252 bool (functor::parameters::* p_readMS)(ioHandler&,const bool) = 00253 functor::parameters::readMS; 00254 b = b && (this->*p_readMS)(handler,false); 00255 # endif 00256 00257 00258 00259 if (complete) { 00260 b = b && handler.readEnd(); 00261 } 00262 00263 return b; 00264 } 00265 00266 # ifdef _LTI_MSC_6 00267 /** 00268 * read the parameters from the given ioHandler 00269 * @param handler the ioHandler to be used 00270 * @param complete if true (the default) the enclosing begin/end will 00271 * be also read, otherwise only the data block will be read. 00272 * @return true if write was successful 00273 */ 00274 virtual bool read(ioHandler& handler,const bool complete=true) { 00275 // ...we need this workaround to cope with another really awful MSVC 00276 // bug. 00277 return readMS(handler,complete); 00278 } 00279 # endif 00280 00281 00282 /** 00283 * returns a pointer to a clone of the parameters 00284 */ 00285 virtual functor::parameters* clone() const { 00286 return new parameters(*this); 00287 }; 00288 00289 /** 00290 * This is the number of components of the mixture. 00291 */ 00292 int numberOfComponents; 00293 00294 /** 00295 * This is the maximum number of iterations that the EM 00296 * algorithm is supposed to be run. 00297 */ 00298 int iterations; 00299 00300 /** 00301 * If this is true, the EM always executes the maximum 00302 * number of iterations, no matter if it converges earlier 00303 * or not. 00304 */ 00305 bool forceIterations; 00306 00307 /** 00308 * This is the maximum number of partial iterations that the SMEM 00309 * algorithm is supposed to be run. 00310 */ 00311 int partialIterations; 00312 00313 /** 00314 * This flag is true, if the estimator should use the 00315 * <a href="http://citeseer.nj.nec.com/ueda99smem.html">Split-and-Merge EM</a> 00316 * algorithm by <a href="http://www.kecl.ntt.co.jp/icl/as/members/ueda/">Ueda</a>, 00317 * <a href="http://www-nkn.ics.nitech.ac.jp/nakano/">Nakano</a>, 00318 * <a href="http://www.db.toronto.edu/~zoubin/">Ghahramani</a>, and 00319 * <a href="http://www.cs.utoronto.ca/~hinton/">Hinton</a> 00320 * (<a href="http://mitpress.mit.edu/NECO/">Neural Computation</a> 12(9), pp. 2109-2128). 00321 */ 00322 bool useSM; 00323 00324 00325 /** 00326 * If You use the split-and-merge version, this parameter is used as 00327 * the regularization constant. The larger this constant, the more 00328 * a covariance matrix will be drawn to be the identity matrix. 00329 */ 00330 T lambda; 00331 00332 /** 00333 * This is a factor for regularizing the covariance matrices in 00334 * emergencies. An emergency occurs when the determinant of a 00335 * covariance matrix is almost zero (which often occurs due to 00336 * numerical problems). In that case, the diagonal of the 00337 * matrix will be emphasized by this factor, until the 00338 * determinant becomes reasonable. 00339 */ 00340 T emergencyLambda; 00341 00342 /** 00343 * This is the maximum number of split-and-merge trials 00344 * that are done in each SM step. 00345 */ 00346 int cMax; 00347 00348 /** 00349 * The magnitude of the random perturbation of the split 00350 * algorithm. 00351 */ 00352 T epsilon; 00353 00354 /** 00355 * Is true if the functor should report its result to a progress 00356 * info object 00357 */ 00358 bool reportProgress; 00359 }; 00360 00361 /** 00362 * default constructor */ 00363 gaussianMixtureModel(); 00364 00365 /** 00366 * copy constructor 00367 * @param other the object to be copied 00368 */ 00369 gaussianMixtureModel(const gaussianMixtureModel& other); 00370 00371 /** 00372 * destructor 00373 */ 00374 virtual ~gaussianMixtureModel(); 00375 00376 /** 00377 * returns the name of this type ("gaussianMixtureModel") 00378 */ 00379 virtual const char* getTypeName() const; 00380 00381 00382 /** 00383 * Performs estimation of a gaussian mixture model for the given 00384 * data matrix. The matrix is interpreted as having a data point 00385 * in each row. 00386 * The method uses the Expectation Maximization method; for a really 00387 * nice explanation of the algorithm, see the paper 00388 * <a href="http://ssli.ee.washington.edu/people/bilmes/mypapers/em.ps.gz"> 00389 * A Gentle Tutorial of the EM Algorithm and its Application to 00390 * Parameter Estimation for Gaussian Mixture and Hidden Markov Models</a> 00391 * by <a href="http://ssli.ee.washington.edu/people/bilmes/">Jeff Bilmes</a>. 00392 * (published as Technical Report TR-97-021 by the 00393 * <a href="http://www.icsi.berkeley.edu/">International Computer 00394 * Science Institute</a>). 00395 * If the useSM parameter is true, the split-and-merge algorithm by 00396 * <a href="http://citeseer.nj.nec.com/ueda99smem.html">Split-and-Merge EM</a> 00397 * algorithm by <a href="http://www.kecl.ntt.co.jp/icl/as/members/ueda/">Ueda</a>, 00398 * <a href="http://www-nkn.ics.nitech.ac.jp/nakano/">Nakano</a>, 00399 * <a href="http://www.db.toronto.edu/~zoubin/">Ghahramani</a>, and 00400 * <a href="http://www.cs.utoronto.ca/~hinton/">Hinton</a> 00401 * (<a href="http://mitpress.mit.edu/NECO/">Neural Computation</a> 12(9), pp. 2109-2128) 00402 * is used instead. 00403 * 00404 * @param src matrix<T> with the source data, each row is 00405 * considered a data point. 00406 * @result true if the estimation was successfull. 00407 */ 00408 inline bool apply(const matrix<T>& src) { 00409 return estimate(src); 00410 } 00411 00412 /** 00413 * Equivalent to apply. 00414 * @param src matrix<T> with the source data, each row is 00415 * considered a data point. 00416 * @result true if the estimation was successfull. 00417 */ 00418 bool estimate(const matrix<T>& src); 00419 00420 /** 00421 * Returns a std::vector of covariance matrices of the 00422 * components. Each vector element is the covariance matrix 00423 * of a model component. 00424 * @result a reference to the component covariance matrices 00425 * of the last estimation 00426 */ 00427 inline const std::vector< matrix<T> >& getComponentSigmas() const { 00428 return sigmas; 00429 }; 00430 00431 /** 00432 * Returns a std::vector of mean vectors of the 00433 * components. Each vector element is the mean vector of 00434 * a model component. 00435 * @result a reference to the component means 00436 * of the last estimation 00437 */ 00438 const std::vector< vector<T> >& getComponentMus() const { 00439 return mus; 00440 }; 00441 00442 /** 00443 * Returns the mixing coefficient vector. Each element 00444 * is the mixing coefficient of a model component. It 00445 * can be viewed as a 00446 * @result a reference to the mixing coefficients 00447 * of the last estimation 00448 */ 00449 const vector<T>& getAlpha() const { 00450 return alpha; 00451 }; 00452 00453 /** 00454 * Copies a std::vector of covariance matrices of the 00455 * components to the given parameter. 00456 * Each vector element is the covariance matrix 00457 * of a model component. 00458 * @param sigs will receive the component covariance matrices 00459 * of the last estimation 00460 */ 00461 void getComponentSigmas(std::vector< matrix<T> >& sigs) const { 00462 sigs=sigmas; 00463 }; 00464 00465 /** 00466 * Copies a std::vector of mean vectors of the 00467 * components to the given parameter. 00468 * Each vector element is the mean vector 00469 * of a model component. 00470 * @param ms will receive the component means 00471 * of the last estimation 00472 */ 00473 void getComponentMus(std::vector< vector<T> >& ms) const { 00474 ms=mus; 00475 }; 00476 00477 /** 00478 * Copies the mixing coefficients to the given parameter. 00479 * Each vector element is the a-priori probability 00480 * of a model component. 00481 * @param a will receive the component mixture coefficients 00482 * of the last estimation 00483 */ 00484 void getAlpha(vector<T>& a) const { 00485 a=alpha; 00486 }; 00487 00488 00489 /* 00490 * This is exactly as the other apply method, however, with this 00491 * one you pass pointers to already-allocated objects within the 00492 * std::vector. The function only modifies the pointed-to vectors 00493 * and matrices, but not the pointers themselves, and not the 00494 * list. 00495 * 00496 bool apply(const matrix<T>& src, 00497 const std::vector< vector<T>* >& means, 00498 const std::vector< matrix<T>* >& sigmas, 00499 vector<T>& alphas) const; 00500 */ 00501 00502 /** 00503 * copy data of "other" functor. 00504 * @param other the functor to be copied 00505 * @return a reference to this functor object 00506 */ 00507 gaussianMixtureModel& copy(const gaussianMixtureModel& other); 00508 00509 /** 00510 * returns a pointer to a clone of this functor. 00511 */ 00512 virtual functor* clone() const; 00513 00514 /** 00515 * returns used parameters 00516 */ 00517 const parameters& getParameters() const; 00518 00519 /** 00520 * Sets the progress info object. 00521 */ 00522 void setProgressInfo(progressInfo& pi); 00523 00524 /* 00525 * This computes the likelihood of a vector x given this model. 00526 * 00527 T getLikelihood(const vector<T>& x); 00528 */ 00529 protected: 00530 // estimates a mixture model by plain EM 00531 bool plainEstimate(); 00532 // estimates a mixture model by SMEM 00533 bool smemEstimate(); 00534 00535 // returns the likelihood of x being generated by component k 00536 T getPartialLikelihood(const vector<T>& x, const int k); 00537 // returns the likelihood of x being generated by component k, 00538 // debug version 00539 T getDBGPartialLikelihood(const vector<T>& x, const int k); 00540 // returns the log of the likelihood of x being generated by component k 00541 T getPartialLogLikelihood(const vector<T>& x, const int k); 00542 // returns the log of the likelihood of x being generated by component k, 00543 // debug version 00544 T getDBGPartialLogLikelihood(const vector<T>& x, const int k); 00545 00546 // performs a full EM step over all components 00547 // if evalQ is true, the quality measure value will be returned in q 00548 bool fullEMStep(bool evalQ, T& q); 00549 00550 // performs a full EM step over all components 00551 inline bool fullEMStep() { 00552 T q; 00553 return fullEMStep(false,q); 00554 }; 00555 00556 // returns the quality measure value for the mixture; this is the 00557 // objective function which is maximized 00558 // if the result is below zero, all is in order. If it is above 00559 // zero, then you have most probably a crappy estimation 00560 T computeQ(); 00561 00562 // returns the quality measure value for the mixture of 00563 // the components whose indices are given in index. 00564 // This array MUST CONTAIN EXACTLY 3 elements (needed for SMEM) 00565 T updateQ(const int index[]); 00566 00567 // Performs a partial EM step, considering only the components 00568 // whose indices are given in index. 00569 // This array MUST CONTAIN EXACTLY 3 elements (needed for SMEM) 00570 bool partialEMStep(const int index[], bool evalQ, T& q); 00571 00572 // Performs a partial EM step, considering only the components 00573 // whose indices are given in index. 00574 // This array MUST CONTAIN EXACTLY 3 elements (needed for SMEM) 00575 inline bool partialEMStep(const int index[]) { 00576 T q; 00577 return partialEMStep(index,false,q); 00578 }; 00579 00580 private: 00581 00582 // tries to rescue a covariance matrix from singularity 00583 // returns the determinant of the rescued matrix 00584 T rescueMatrix(matrix<T>& victim); 00585 00586 /** 00587 * Returns a uniformly distributed random number between 0 and a. 00588 */ 00589 inline T frand(T a) const { 00590 return (T(random())/T(RAND_MAX))*a; 00591 } 00592 00593 /** 00594 * This computes the a-posteriori probability matrix p with 00595 * p[l][i]=P(l|xi); 00596 * Therefore, the sum of a row l is the a-posteriori probability of model 00597 * l. 00598 */ 00599 void computeP(); 00600 00601 /** 00602 * This computes the a-posteriori probability matrix p with 00603 * p[l][i]=P(l|xi); 00604 * The values of l are given in the parameter. The array must have 00605 * exactly 3 elements. 00606 * Therefore, the sum of a row l is the a-posteriori probability of model 00607 * l. 00608 */ 00609 void updateP(const int ijk[]); 00610 00611 // functors 00612 meansFunctor<T> mean; 00613 matrixInversion<T> inverter; 00614 varianceFunctor<T> var; 00615 luDecomposition<T> luDecomposer; 00616 l2Distance<T> dist; 00617 00618 // the data for which the model is generated 00619 const matrix<T>* data; 00620 00621 // the regularizer matrix 00622 matrix<T> regularizer; 00623 00624 // the mean vectors of the components 00625 std::vector< vector<T> > mus; 00626 // the covariance matrices of the components 00627 std::vector< matrix<T> > sigmas; 00628 // the inverses of the covariance matrices of the components 00629 std::vector< matrix<T> > sigmaInvs; 00630 // the determinants of the covariance matrices of the components 00631 vector<T> dets; 00632 // the a-priori probabilities of the components 00633 vector<T> alpha; 00634 // the a-posteriori probabilities of each data point 00635 // being generated by each component 00636 matrix<T> p; 00637 // number of components and number of data vectors. 00638 int M,N; 00639 00640 progressInfo* reporter; 00641 }; 00642 } 00643 00644 #endif