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