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.
GaussianMixtureModels.cpp
1 
2 #define GRT_DLL_EXPORTS
4 
5 GRT_BEGIN_NAMESPACE
6 
7 //Register the GaussianMixtureModels class with the Clusterer base class
8 RegisterClustererModule< GaussianMixtureModels > GaussianMixtureModels::registerModule("GaussianMixtureModels");
9 
10 //Constructor,destructor
11 GaussianMixtureModels::GaussianMixtureModels(const UINT numClusters,const UINT minNumEpochs,const UINT maxNumEpochs,const Float minChange){
12 
13  this->numClusters = numClusters;
14  this->minNumEpochs = minNumEpochs;
15  this->maxNumEpochs = maxNumEpochs;
16  this->minChange = minChange;
17 
19  numTrainingIterationsToConverge = 0;
20  trained = false;
21 
22  classType = "GaussianMixtureModels";
23  clustererType = classType;
24  debugLog.setProceedingText("[DEBUG GaussianMixtureModels]");
25  errorLog.setProceedingText("[ERROR GaussianMixtureModels]");
26  trainingLog.setProceedingText("[TRAINING GaussianMixtureModels]");
27  warningLog.setProceedingText("[WARNING GaussianMixtureModels]");
28 }
29 
31 
32  classType = "GaussianMixtureModels";
33  clustererType = classType;
34  debugLog.setProceedingText("[DEBUG GaussianMixtureModels]");
35  errorLog.setProceedingText("[ERROR GaussianMixtureModels]");
36  trainingLog.setProceedingText("[TRAINING GaussianMixtureModels]");
37  warningLog.setProceedingText("[WARNING GaussianMixtureModels]");
38 
39  if( this != &rhs ){
40 
42  this->loglike = rhs.loglike;
43  this->mu = rhs.mu;
44  this->resp = rhs.resp;
45  this->frac = rhs.frac;
46  this->lndets = rhs.lndets;
47  this->det = rhs.det;
48  this->sigma = rhs.sigma;
49  this->invSigma = rhs.invSigma;
50 
51  //Clone the Clusterer variables
52  copyBaseVariables( (Clusterer*)&rhs );
53  }
54 
55 }
56 
58 }
59 
61 
62  if( this != &rhs ){
63 
65  this->loglike = rhs.loglike;
66  this->mu = rhs.mu;
67  this->resp = rhs.resp;
68  this->frac = rhs.frac;
69  this->lndets = rhs.lndets;
70  this->det = rhs.det;
71  this->sigma = rhs.sigma;
72  this->invSigma = rhs.invSigma;
73 
74  //Clone the Clusterer variables
75  copyBaseVariables( (Clusterer*)&rhs );
76  }
77 
78  return *this;
79 }
80 
82 
83  if( clusterer == NULL ) return false;
84 
85  if( this->getClustererType() == clusterer->getClustererType() ){
86  //Clone the GaussianMixtureModels values
88 
90  this->loglike = ptr->loglike;
91  this->mu = ptr->mu;
92  this->resp = ptr->resp;
93  this->frac = ptr->frac;
94  this->lndets = ptr->lndets;
95  this->det = ptr->det;
96  this->sigma = ptr->sigma;
97  this->invSigma = ptr->invSigma;
98 
99  //Clone the Clusterer variables
100  return copyBaseVariables( clusterer );
101  }
102  return false;
103 }
104 
106 
108 
109  numTrainingSamples = 0;
110  loglike = 0;
111 
112  return true;
113 }
114 
116 
118 
119  numTrainingSamples = 0;
120  loglike = 0;
121  mu.clear();
122  resp.clear();
123  frac.clear();
124  lndets.clear();
125  det.clear();
126  sigma.clear();
127  invSigma.clear();
128 
129  return true;
130 }
131 
133 
134  trained = false;
135 
136  //Clear any previous training results
137  det.clear();
138  invSigma.clear();
139  numTrainingIterationsToConverge = 0;
140 
141  if( data.getNumRows() == 0 ){
142  errorLog << "train_(MatrixFloat &data) - Training Failed! Training data is empty!" << std::endl;
143  return false;
144  }
145 
146  //Resize the variables
148  numInputDimensions = data.getNumCols();
149 
150  //Resize mu and resp
151  mu.resize(numClusters,numInputDimensions);
153 
154  //Resize sigma
155  sigma.resize(numClusters);
156  for(UINT k=0; k<numClusters; k++){
157  sigma[k].resize(numInputDimensions,numInputDimensions);
158  }
159 
160  //Resize frac and lndets
161  frac.resize(numClusters);
162  lndets.resize(numClusters);
163 
164  //Scale the data if needed
165  ranges = data.getRanges();
166  if( useScaling ){
167  for(UINT i=0; i<numTrainingSamples; i++){
168  for(UINT j=0; j<numInputDimensions; j++){
169  data[i][j] = scale(data[i][j],ranges[j].minValue,ranges[j].maxValue,0,1);
170  }
171  }
172  }
173 
174  //Pick K random starting points for the inital guesses of Mu
175  Random random;
176  Vector< UINT > randomIndexs(numTrainingSamples);
177  for(UINT i=0; i<numTrainingSamples; i++) randomIndexs[i] = i;
178  for(UINT i=0; i<numClusters; i++){
179  SWAP(randomIndexs[ i ],randomIndexs[ random.getRandomNumberInt(0,numTrainingSamples) ]);
180  }
181  for(UINT k=0; k<numClusters; k++){
182  for(UINT n=0; n<numInputDimensions; n++){
183  mu[k][n] = data[ randomIndexs[k] ][n];
184  }
185  }
186 
187  //Setup sigma and the uniform prior on P(k)
188  for(UINT k=0; k<numClusters; k++){
189  frac[k] = 1.0/Float(numClusters);
190  for(UINT i=0; i<numInputDimensions; i++){
191  for(UINT j=0; j<numInputDimensions; j++) sigma[k][i][j] = 0;
192  sigma[k][i][i] = 1.0e-2; //Set the diagonal to a small number
193  }
194  }
195 
196  loglike = 0;
197  bool keepGoing = true;
198  Float change = 99.9e99;
199  UINT numIterationsNoChange = 0;
200  VectorFloat u(numInputDimensions);
201  VectorFloat v(numInputDimensions);
202 
203  while( keepGoing ){
204 
205  //Run the estep
206  if( estep( data, u, v, change ) ){
207 
208  //Run the mstep
209  mstep( data );
210 
211  //Check for convergance
212  if( fabs( change ) < minChange ){
213  if( ++numIterationsNoChange >= minNumEpochs ){
214  keepGoing = false;
215  }
216  }else numIterationsNoChange = 0;
217  if( ++numTrainingIterationsToConverge >= maxNumEpochs ) keepGoing = false;
218 
219  }else{
220  errorLog << "train_(MatrixFloat &data) - Estep failed at iteration " << numTrainingIterationsToConverge << std::endl;
221  return false;
222  }
223  }
224 
225  //Compute the inverse of sigma and the determinants for prediction
226  if( !computeInvAndDet() ){
227  det.clear();
228  invSigma.clear();
229  errorLog << "train_(MatrixFloat &data) - Failed to compute inverse and determinat!" << std::endl;
230  return false;
231  }
232 
233  //Flag that the model was trained
234  trained = true;
235 
236  //Setup the cluster labels
237  clusterLabels.resize(numClusters);
238  for(UINT i=0; i<numClusters; i++){
239  clusterLabels[i] = i+1;
240  }
241  clusterLikelihoods.resize(numClusters,0);
242  clusterDistances.resize(numClusters,0);
243 
244  return true;
245 }
246 
248  MatrixFloat data = trainingData.getDataAsMatrixFloat();
249  return train_( data );
250 }
251 
253  MatrixFloat data = trainingData.getDataAsMatrixFloat();
254  return train_( data );
255 }
256 
258 
259  if( !trained ){
260  return false;
261  }
262 
263  if( x.getSize() != numInputDimensions ){
264  return false;
265  }
266 
267  if( useScaling ){
268  for(UINT n=0; n<numInputDimensions; n++){
269  x[n] = grt_scale(x[n], ranges[n].minValue, ranges[n].maxValue, 0.0, 1.0);
270  }
271  }
272 
273  Float sum = 0;
274  Float dist = 0;
275  UINT minIndex = 0;
276  bestDistance = 0;
278  maxLikelihood = 0;
279  if( clusterLikelihoods.size() != numClusters )
280  clusterLikelihoods.resize( numClusters );
281  if( clusterDistances.size() != numClusters )
282  clusterDistances.resize( numClusters );
283 
284  for(UINT i=0; i<numClusters; i++){
285 
286  dist = gauss(x,i,det,mu,invSigma);
287 
288  clusterDistances[i] = dist;
289  clusterLikelihoods[i] = dist;
290 
291  sum += clusterLikelihoods[i];
292 
293  if( dist > bestDistance ){
294  bestDistance = dist;
295  minIndex = i;
296  }
297  }
298 
299  //Normalize the likelihood
300  for(UINT i=0; i<numClusters; i++){
301  clusterLikelihoods[i] /= sum;
302  }
303 
304  predictedClusterLabel = clusterLabels[ minIndex ];
305  maxLikelihood = clusterLikelihoods[ minIndex ];
306 
307  return true;
308 }
309 
310 bool GaussianMixtureModels::saveModelToFile( std::fstream &file ) const{
311 
312  if( !file.is_open() ){
313  errorLog << "saveModelToFile(string filename) - Failed to open file!" << std::endl;
314  return false;
315  }
316 
317  file << "GRT_GAUSSIAN_MIXTURE_MODELS_FILE_V1.0\n";
318 
319  if( !saveClustererSettingsToFile( file ) ){
320  errorLog << "saveModelToFile(fstream &file) - Failed to save cluster settings to file!" << std::endl;
321  return false;
322  }
323 
324  if( trained ){
325  file << "Mu:\n";
326  for(UINT k=0; k<numClusters; k++){
327  for(UINT n=0; n<numInputDimensions; n++){
328  file << mu[k][n] << "\t";
329  }
330  file << std::endl;
331  }
332 
333  file << "Sigma:\n";
334  for(UINT k=0; k<numClusters; k++){
335  for(UINT i=0; i<numInputDimensions; i++){
336  for(UINT j=0; j<numInputDimensions; j++){
337  file << sigma[k][i][j] << "\t";
338  }
339  }
340  file << std::endl;
341  }
342 
343  file << "InvSigma:\n";
344  for(UINT k=0; k<numClusters; k++){
345  for(UINT i=0; i<numInputDimensions; i++){
346  for(UINT j=0; j<numInputDimensions; j++){
347  file << invSigma[k][i][j] << "\t";
348  }
349  }
350  file << std::endl;
351  }
352 
353  file << "Det:\n";
354  for(UINT k=0; k<numClusters; k++){
355  file << det[k] << std::endl;
356  }
357  }
358 
359  return true;
360 
361 }
362 
363 bool GaussianMixtureModels::loadModelFromFile( std::fstream &file ){
364 
365  //Clear any previous model
366  clear();
367 
368  std::string word;
369  file >> word;
370  if( word != "GRT_GAUSSIAN_MIXTURE_MODELS_FILE_V1.0" ){
371  return false;
372  }
373 
374  if( !loadClustererSettingsFromFile( file ) ){
375  errorLog << "loadModelFromFile(fstream &file) - Failed to load cluster settings from file!" << std::endl;
376  return false;
377  }
378 
379  //Load the model
380  if( trained ){
381 
382  //Setup the memory
383  mu.resize(numClusters, numInputDimensions);
384  sigma.resize(numClusters);
385  invSigma.resize(numClusters);
386  det.resize(numClusters);
387 
388  //Load mu
389  file >> word;
390  if( word != "Mu:" ){
391  clear();
392  errorLog << "loadModelFromFile(fstream &file) - Failed to load Mu!" << std::endl;
393  return false;
394  }
395  for(UINT k=0; k<numClusters; k++){
396  for(UINT n=0; n<numInputDimensions; n++){
397  file >> mu[k][n];
398  }
399  }
400 
401  //Load Sigma
402  file >> word;
403  if( word != "Sigma:" ){
404  clear();
405  errorLog << "loadModelFromFile(fstream &file) - Failed to load Sigma!" << std::endl;
406  return false;
407  }
408  for(UINT k=0; k<numClusters; k++){
409  sigma[k].resize(numInputDimensions, numInputDimensions);
410  for(UINT i=0; i<numInputDimensions; i++){
411  for(UINT j=0; j<numInputDimensions; j++){
412  file >> sigma[k][i][j];
413  }
414  }
415  }
416 
417  //Load InvSigma
418  file >> word;
419  if( word != "InvSigma:" ){
420  clear();
421  errorLog << "loadModelFromFile(fstream &file) - Failed to load InvSigma!" << std::endl;
422  return false;
423  }
424  for(UINT k=0; k<numClusters; k++){
425  invSigma[k].resize(numInputDimensions, numInputDimensions);
426  for(UINT i=0; i<numInputDimensions; i++){
427  for(UINT j=0; j<numInputDimensions; j++){
428  file >> invSigma[k][i][j];
429  }
430  }
431  }
432 
433  //Load Det
434  file >> word;
435  if( word != "Det:" ){
436  clear();
437  errorLog << "loadModelFromFile(fstream &file) - Failed to load Det!" << std::endl;
438  return false;
439  }
440  for(UINT k=0; k<numClusters; k++){
441  file >> det[k];
442  }
443 
444  //Setup the cluster labels
445  clusterLabels.resize(numClusters);
446  for(UINT i=0; i<numClusters; i++){
447  clusterLabels[i] = i+1;
448  }
449  clusterLikelihoods.resize(numClusters,0);
450  clusterDistances.resize(numClusters,0);
451 
452  }
453 
454  return true;
455 }
456 
457 bool GaussianMixtureModels::estep( const MatrixFloat &data, VectorFloat &u, VectorFloat &v, Float &change ){
458 
459  Float tmp,sum,max,oldloglike;
460  for(UINT j=0; j<numInputDimensions; j++) u[j] = v[j] = 0;
461 
462  oldloglike = loglike;
463 
464  for(UINT k=0; k<numClusters; k++){
465  Cholesky cholesky( sigma[k] );
466  if( !cholesky.getSuccess() ){ return false; }
467  lndets[k] = cholesky.logdet();
468 
469  for(UINT i=0; i<numTrainingSamples; i++){
470  for(UINT j=0; j<numInputDimensions; j++) u[j] = data[i][j] - mu[k][j];
471  if( !cholesky.elsolve(u,v) ){ return false; }
472  sum=0;
473  for(UINT j=0; j<numInputDimensions; j++) sum += SQR(v[j]);
474  resp[i][k] = -0.5*(sum + lndets[k]) + log(frac[k]);
475  }
476  }
477 
478  //Compute the overall likelihood of the entire estimated paramter set
479  loglike = 0;
480  for(UINT i=0; i<numTrainingSamples; i++){
481  sum=0;
482  max = -99.9e99;
483  for(UINT k=0; k<numClusters; k++) if( resp[i][k] > max ) max = resp[i][k];
484  for(UINT k=0; k<numClusters; k++) sum += exp( resp[i][k]-max );
485  tmp = max + log( sum );
486  for(UINT k=0; k<numClusters; k++) resp[i][k] = exp( resp[i][k] - tmp );
487  loglike += tmp;
488  }
489 
490  change = (loglike - oldloglike);
491 
492  return true;
493 }
494 
495 bool GaussianMixtureModels::mstep( const MatrixFloat &data ){
496 
497  Float wgt, sum;
498  for(UINT k=0; k<numClusters; k++){
499  wgt = 0.0;
500  for(UINT m=0; m<numTrainingSamples; m++) wgt += resp[m][k];
501  frac[k] = wgt/Float(numTrainingSamples);
502  for(UINT n=0; n<numInputDimensions; n++){
503  sum = 0;
504  for(UINT m=0; m<numTrainingSamples; m++) sum += resp[m][k] * data[m][n];
505  mu[k][n] = sum/wgt;
506  for(UINT j=0; j<numInputDimensions; j++){
507  sum = 0;
508  for(UINT m=0; m<numTrainingSamples; m++){
509  sum += resp[m][k] * (data[m][n]-mu[k][n]) * (data[m][j]-mu[k][j]);
510  }
511  sigma[k][n][j] = sum/wgt;
512  }
513  }
514  }
515 
516  return true;
517 
518 }
519 
520 inline void GaussianMixtureModels::SWAP(UINT &a,UINT &b){
521  UINT temp = b;
522  b = a;
523  a = temp;
524 }
525 
526 bool GaussianMixtureModels::computeInvAndDet(){
527 
528  det.resize(numClusters);
529  invSigma.resize(numClusters);
530 
531  for(UINT k=0; k<numClusters; k++){
532  LUDecomposition lu(sigma[k]);
533  if( !lu.inverse( invSigma[k] ) ){
534  errorLog << "computeInvAndDet() - Matrix inversion failed for cluster " << k+1 << std::endl;
535  return false;
536  }
537  det[k] = lu.det();
538  }
539 
540  return true;
541 
542 }
543 
544 GRT_END_NAMESPACE
void clear()
Definition: Matrix.h:522
Float scale(const Float &x, const Float &minSource, const Float &maxSource, const Float &minTarget, const Float &maxTarget, const bool constrain=false)
Definition: MLBase.h:353
virtual bool train_(MatrixFloat &trainingData)
virtual bool predict_(VectorDouble &inputVector)
Definition: Random.h:40
VectorDouble lndets
A vector holding the log detminants of SIGMA'k.
Float loglike
The current loglikelihood value of the models given the data.
std::string getClustererType() const
Definition: Clusterer.cpp:260
MatrixFloat getDataAsMatrixFloat() const
virtual bool resize(const unsigned int size)
Definition: Vector.h:133
This class implements a Gaussian Miture Model clustering algorithm. The code is based on the GMM code...
UINT getSize() const
Definition: Vector.h:191
bool copyBaseVariables(const Clusterer *clusterer)
Definition: Clusterer.cpp:87
virtual bool loadModelFromFile(std::fstream &file)
bool loadClustererSettingsFromFile(std::fstream &file)
Definition: Clusterer.cpp:179
UINT predictedClusterLabel
Stores the predicted cluster label from the most recent predict( )
Definition: Clusterer.h:250
virtual bool saveModelToFile(std::fstream &file) const
bool saveClustererSettingsToFile(std::fstream &file) const
Definition: Clusterer.cpp:157
MatrixFloat mu
A matrix holding the estimated mean values of each Gaussian.
virtual bool reset()
Definition: Clusterer.cpp:128
UINT numClusters
Number of clusters in the model.
Definition: Clusterer.h:249
unsigned int getNumRows() const
Definition: Matrix.h:542
unsigned int getNumCols() const
Definition: Matrix.h:549
GaussianMixtureModels & operator=(const GaussianMixtureModels &rhs)
VectorDouble frac
A vector holding the P(k)'s.
Vector< MinMax > getRanges() const
int getRandomNumberInt(int minRange, int maxRange)
Definition: Random.h:88
MatrixFloat getDataAsMatrixFloat() const
virtual bool resize(const unsigned int r, const unsigned int c)
Definition: Matrix.h:232
virtual bool deepCopyFrom(const Clusterer *clusterer)
GaussianMixtureModels(const UINT numClusters=10, const UINT minNumEpochs=5, const UINT maxNumEpochs=1000, const Float minChange=1.0e-5)
MatrixFloat resp
The responsibility matrix.
virtual bool clear()
Definition: Clusterer.cpp:142
UINT numTrainingSamples
The number of samples in the training data.