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