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.
SelfOrganizingMap.cpp
1 /*
2  GRT MIT License
3  Copyright (c) <2012> <Nicholas Gillian, Media Lab, MIT>
4 
5  Permission is hereby granted, free of charge, to any person obtaining a copy of this software
6  and associated documentation files (the "Software"), to deal in the Software without restriction,
7  including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
9  subject to the following conditions:
10 
11  The above copyright notice and this permission notice shall be included in all copies or substantial
12  portions of the Software.
13 
14  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
15  LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
17  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
18  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19  */
20 
21 #define GRT_DLL_EXPORTS
22 #include "SelfOrganizingMap.h"
23 
24 GRT_BEGIN_NAMESPACE
25 
26 //Register the SelfOrganizingMap class with the Clusterer base class
27 RegisterClustererModule< SelfOrganizingMap > SelfOrganizingMap::registerModule("SelfOrganizingMap");
28 
29 SelfOrganizingMap::SelfOrganizingMap( const UINT networkSize, const UINT networkTypology, const UINT maxNumEpochs, const Float alphaStart, const Float alphaEnd ){
30 
31  this->numClusters = networkSize;
32  this->networkTypology = networkTypology;
33  this->maxNumEpochs = maxNumEpochs;
34  this->alphaStart = alphaStart;
35  this->alphaEnd = alphaEnd;
36 
37  classType = "SelfOrganizingMap";
38  clustererType = classType;
39  debugLog.setProceedingText("[DEBUG SelfOrganizingMap]");
40  errorLog.setProceedingText("[ERROR SelfOrganizingMap]");
41  trainingLog.setProceedingText("[TRAINING SelfOrganizingMap]");
42  warningLog.setProceedingText("[WARNING SelfOrganizingMap]");
43 }
44 
46 
47  classType = "SelfOrganizingMap";
48  clustererType = classType;
49  debugLog.setProceedingText("[DEBUG KMeans]");
50  errorLog.setProceedingText("[ERROR KMeans]");
51  trainingLog.setProceedingText("[TRAINING KMeans]");
52  warningLog.setProceedingText("[WARNING KMeans]");
53 
54  if( this != &rhs ){
55 
56  this->networkTypology = rhs.networkTypology;
57  this->alphaStart = rhs.alphaStart;
58  this->alphaEnd = rhs.alphaEnd;
59 
60  //Clone the Clusterer variables
61  copyBaseVariables( (Clusterer*)&rhs );
62  }
63 }
64 
66 
67 }
68 
70 
71  if( this != &rhs ){
72 
73  this->networkTypology = rhs.networkTypology;
74  this->alphaStart = rhs.alphaStart;
75  this->alphaEnd = rhs.alphaEnd;
76 
77  //Clone the Clusterer variables
78  copyBaseVariables( (Clusterer*)&rhs );
79  }
80 
81  return *this;
82 }
83 
85 
86  if( clusterer == NULL ) return false;
87 
88  if( this->getClustererType() == clusterer->getClustererType() ){
89  //Clone the SelfOrganizingMap values
90  SelfOrganizingMap *ptr = (SelfOrganizingMap*)clusterer;
91 
92  this->networkTypology = ptr->networkTypology;
93  this->alphaStart = ptr->alphaStart;
94  this->alphaEnd = ptr->alphaEnd;
95 
96  //Clone the Clusterer variables
97  return copyBaseVariables( clusterer );
98  }
99 
100  return false;
101 }
102 
104 
105  //Reset the base class
107 
108  return true;
109 }
110 
112 
113  //Reset the base class
115 
116  //Clear the SelfOrganizingMap models
117  neurons.clear();
118  networkWeights.clear();
119 
120  return true;
121 }
122 
124 
125  //Clear any previous models
126  clear();
127 
128  const UINT M = data.getNumRows();
129  const UINT N = data.getNumCols();
130  numInputDimensions = N;
131  numOutputDimensions = numClusters;
132  Random rand;
133 
134  //Setup the neurons
135  neurons.resize( numClusters );
136 
137  if( neurons.size() != numClusters ){
138  errorLog << "train_( MatrixFloat &data ) - Failed to resize neurons Vector, there might not be enough memory!" << std::endl;
139  return false;
140  }
141 
142  for(UINT j=0; j<numClusters; j++){
143 
144  //Init the neuron
145  neurons[j].init( N, 0.5 );
146 
147  //Set the weights as a random training example
148  neurons[j].weights = data.getRowVector( rand.getRandomNumberInt(0, M) );
149  }
150 
151  //Setup the network weights
152  switch( networkTypology ){
153  case RANDOM_NETWORK:
154  networkWeights.resize(numClusters, numClusters);
155 
156  //Set the diagonal weights as 1 (as i==j)
157  for(UINT i=0; i<numClusters; i++){
158  networkWeights[i][i] = 1;
159  }
160 
161  //Randomize the other weights
162  UINT indexA = 0;
163  UINT indexB = 0;
164  Float weight = 0;
165  for(UINT i=0; i<numClusters*numClusters; i++){
166  indexA = rand.getRandomNumberInt(0, numClusters);
167  indexB = rand.getRandomNumberInt(0, numClusters);
168 
169  //Make sure the two random indexs are the same (as this is a diagonal and should be 1)
170  if( indexA != indexB ){
171  //Pick a random weight between these two neurons
172  weight = rand.getRandomNumberUniform(0,1);
173 
174  //The weight betwen neurons a and b is the mirrored
175  networkWeights[indexA][indexB] = weight;
176  networkWeights[indexB][indexA] = weight;
177  }
178  }
179  break;
180  }
181 
182  //Scale the data if needed
183  ranges = data.getRanges();
184  if( useScaling ){
185  for(UINT i=0; i<M; i++){
186  for(UINT j=0; j<numInputDimensions; j++){
187  data[i][j] = scale(data[i][j],ranges[j].minValue,ranges[j].maxValue,0,1);
188  }
189  }
190  }
191 
192  Float error = 0;
193  Float lastError = 0;
194  Float trainingSampleError = 0;
195  Float delta = 0;
196  Float minChange = 0;
197  Float weightUpdate = 0;
198  Float weightUpdateSum = 0;
199  Float alpha = 1.0;
200  Float neuronDiff = 0;
201  UINT iter = 0;
202  bool keepTraining = true;
203  VectorFloat trainingSample;
204  Vector< UINT > randomTrainingOrder(M);
205 
206  //In most cases, the training data is grouped into classes (100 samples for class 1, followed by 100 samples for class 2, etc.)
207  //This can cause a problem for stochastic gradient descent algorithm. To avoid this issue, we randomly shuffle the order of the
208  //training samples. This random order is then used at each epoch.
209  for(UINT i=0; i<M; i++){
210  randomTrainingOrder[i] = i;
211  }
212  std::random_shuffle(randomTrainingOrder.begin(), randomTrainingOrder.end());
213 
214  //Enter the main training loop
215  while( keepTraining ){
216 
217  //Update alpha based on the current iteration
218  alpha = Util::scale(iter,0,maxNumEpochs,alphaStart,alphaEnd);
219 
220  //Run one epoch of training using the online best-matching-unit algorithm
221  error = 0;
222  for(UINT i=0; i<M; i++){
223 
224  trainingSampleError = 0;
225 
226  //Get the i'th random training sample
227  trainingSample = data.getRowVector( randomTrainingOrder[i] );
228 
229  //Find the best matching unit
230  Float dist = 0;
231  Float bestDist = grt_numeric_limits< Float >::max();
232  UINT bestIndex = 0;
233  for(UINT j=0; j<numClusters; j++){
234  dist = neurons[j].getSquaredWeightDistance( trainingSample );
235  if( dist < bestDist ){
236  bestDist = dist;
237  bestIndex = j;
238  }
239  }
240 
241  //Update the weights based on the distance to the winning neuron
242  //Neurons closer to the winning neuron will have their weights update more
243  for(UINT j=0; j<numClusters; j++){
244 
245  //Update the weights for the j'th neuron
246  weightUpdateSum = 0;
247  neuronDiff = 0;
248  for(UINT n=0; n<N; n++){
249  neuronDiff = trainingSample[n] - neurons[j][n];
250  weightUpdate = networkWeights[bestIndex][j] * alpha * neuronDiff;
251  neurons[j][n] += weightUpdate;
252  weightUpdateSum += neuronDiff;
253  }
254 
255  trainingSampleError += grt_sqr( weightUpdateSum );
256  }
257 
258  error += grt_sqrt( trainingSampleError / numClusters );
259  }
260 
261  //Compute the error
262  delta = fabs( error-lastError );
263  lastError = error;
264 
265  //Check to see if we should stop
266  if( delta <= minChange ){
267  converged = true;
268  keepTraining = false;
269  }
270 
271  if( grt_isinf( error ) ){
272  errorLog << "train_(MatrixFloat &data) - Training failed! Error is NAN!" << std::endl;
273  return false;
274  }
275 
276  if( ++iter >= maxNumEpochs ){
277  keepTraining = false;
278  }
279 
280  trainingLog << "Epoch: " << iter << " Squared Error: " << error << " Delta: " << delta << " Alpha: " << alpha << std::endl;
281  }
282 
283  numTrainingIterationsToConverge = iter;
284  trained = true;
285 
286  return true;
287 }
288 
290  MatrixFloat data = trainingData.getDataAsMatrixFloat();
291  return train_(data);
292 }
293 
295  MatrixFloat data = trainingData.getDataAsMatrixFloat();
296  return train_(data);
297 }
298 
300 
301  if( !trained ){
302  return false;
303  }
304 
305  if( useScaling ){
306  for(UINT i=0; i<numInputDimensions; i++){
307  x[i] = scale(x[i], ranges[i].minValue, ranges[i].maxValue, 0, 1);
308  }
309  }
310 
311  if( mappedData.getSize() != numClusters )
312  mappedData.resize( numClusters );
313 
314  for(UINT i=0; i<numClusters; i++){
315  mappedData[i] = neurons[i].fire( x );
316  }
317 
318  return true;
319 }
320 
321 bool SelfOrganizingMap::saveModelToFile( std::fstream &file ) const{
322 
323  if( !trained ){
324  errorLog << "saveModelToFile(fstream &file) - Can't save model to file, the model has not been trained!" << std::endl;
325  return false;
326  }
327 
328  file << "GRT_SELF_ORGANIZING_MAP_MODEL_FILE_V1.0\n";
329 
330  if( !saveClustererSettingsToFile( file ) ){
331  errorLog << "saveModelToFile(fstream &file) - Failed to save cluster settings to file!" << std::endl;
332  return false;
333  }
334 
335  file << "NetworkTypology: " << networkTypology << std::endl;
336  file << "AlphaStart: " << alphaStart << std::endl;
337  file << "AlphaEnd: " << alphaEnd << std::endl;
338 
339  if( trained ){
340  file << "NetworkWeights: \n";
341  for(UINT i=0; i<networkWeights.getNumRows(); i++){
342  for(UINT j=0; j<networkWeights.getNumCols(); j++){
343  file << networkWeights[i][j];
344  if( j<networkWeights.getNumCols()-1 ) file << "\t";
345  }
346  file << "\n";
347  }
348 
349  file << "Neurons: \n";
350  for(UINT i=0; i<neurons.getSize(); i++){
351  if( !neurons[i].saveNeuronToFile( file ) ){
352  errorLog << "saveModelToFile(fstream &file) - Failed to save neuron to file!" << std::endl;
353  return false;
354  }
355  }
356  }
357 
358  return true;
359 
360 }
361 
362 bool SelfOrganizingMap::loadModelFromFile( std::fstream &file ){
363 
364  //Clear any previous model
365  clear();
366 
367  std::string word;
368  file >> word;
369  if( word != "GRT_SELF_ORGANIZING_MAP_MODEL_FILE_V1.0" ){
370  errorLog << "loadModelFromFile(fstream &file) - Failed to load file header!" << std::endl;
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  file >> word;
380  if( word != "NetworkTypology:" ){
381  errorLog << "loadModelFromFile(fstream &file) - Failed to load NetworkTypology header!" << std::endl;
382  return false;
383  }
384  file >> networkTypology;
385 
386  file >> word;
387  if( word != "AlphaStart:" ){
388  errorLog << "loadModelFromFile(fstream &file) - Failed to load AlphaStart header!" << std::endl;
389  return false;
390  }
391  file >> alphaStart;
392 
393  file >> word;
394  if( word != "AlphaEnd:" ){
395  errorLog << "loadModelFromFile(fstream &file) - Failed to load alphaEnd header!" << std::endl;
396  return false;
397  }
398  file >> alphaEnd;
399 
400  //Load the model if it has been trained
401  if( trained ){
402  file >> word;
403  if( word != "NetworkWeights:" ){
404  errorLog << "loadModelFromFile(fstream &file) - Failed to load NetworkWeights header!" << std::endl;
405  return false;
406  }
407 
408  networkWeights.resize(numClusters, numClusters);
409  for(UINT i=0; i<networkWeights.getNumRows(); i++){
410  for(UINT j=0; j<networkWeights.getNumCols(); j++){
411  file >> networkWeights[i][j];
412  }
413  }
414 
415  file >> word;
416  if( word != "Neurons:" ){
417  errorLog << "loadModelFromFile(fstream &file) - Failed to load Neurons header!" << std::endl;
418  return false;
419  }
420 
421  neurons.resize(numClusters);
422  for(UINT i=0; i<neurons.size(); i++){
423  if( !neurons[i].loadNeuronFromFile( file ) ){
424  errorLog << "loadModelFromFile(fstream &file) - Failed to save neuron to file!" << std::endl;
425  return false;
426  }
427  }
428  }
429 
430  return true;
431 }
432 
433 bool SelfOrganizingMap::validateNetworkTypology( const UINT networkTypology ){
434  if( networkTypology == RANDOM_NETWORK ) return true;
435 
436  warningLog << "validateNetworkTypology(const UINT networkTypology) - Unknown networkTypology!" << std::endl;
437 
438  return false;
439 }
440 
442  return numClusters;
443 }
444 
445 Float SelfOrganizingMap::getAlphaStart() const{
446  return alphaStart;
447 }
448 
449 Float SelfOrganizingMap::getAlphaEnd() const{
450  return alphaEnd;
451 }
452 
453 VectorFloat SelfOrganizingMap::getMappedData() const{
454  return mappedData;
455 }
456 
457 Vector< GaussNeuron > SelfOrganizingMap::getNeurons() const{
458  return neurons;
459 }
460 
461 const Vector< GaussNeuron >& SelfOrganizingMap::getNeuronsRef() const{
462  return neurons;
463 }
464 
465 MatrixFloat SelfOrganizingMap::getNetworkWeights() const{
466  return networkWeights;
467 }
468 
469 bool SelfOrganizingMap::setNetworkSize( const UINT networkSize ){
470  if( networkSize > 0 ){
471  this->numClusters = networkSize;
472  return true;
473  }
474 
475  warningLog << "setNetworkSize(const UINT networkSize) - The networkSize must be greater than 0!" << std::endl;
476 
477  return false;
478 }
479 
480 bool SelfOrganizingMap::setNetworkTypology( const UINT networkTypology ){
481  if( validateNetworkTypology( networkTypology ) ){
482  this->networkTypology = networkTypology;
483  return true;
484  }
485  return false;
486 }
487 
488 bool SelfOrganizingMap::setAlphaStart( const Float alphaStart ){
489 
490  if( alphaStart > 0 ){
491  this->alphaStart = alphaStart;
492  return true;
493  }
494 
495  warningLog << "setAlphaStart(const Float alphaStart) - AlphaStart must be greater than zero!" << std::endl;
496 
497  return false;
498 }
499 
500 bool SelfOrganizingMap::setAlphaEnd( const Float alphaEnd ){
501 
502  if( alphaEnd > 0 ){
503  this->alphaEnd = alphaEnd;
504  return true;
505  }
506 
507  warningLog << "setAlphaEnd(const Float alphaEnd) - AlphaEnd must be greater than zero!" << std::endl;
508 
509  return false;
510 }
511 
512 GRT_END_NAMESPACE
513 
void clear()
Definition: Matrix.h:522
virtual bool deepCopyFrom(const Clusterer *clusterer)
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
This class implements the Self Oganizing Map clustering algorithm.
static Float scale(const Float &x, const Float &minSource, const Float &maxSource, const Float &minTarget, const Float &maxTarget, const bool constrain=false)
Definition: Util.cpp:53
Definition: Random.h:40
std::string getClustererType() const
Definition: Clusterer.cpp:260
MatrixFloat getDataAsMatrixFloat() const
virtual bool resize(const unsigned int size)
Definition: Vector.h:133
virtual bool train_(MatrixFloat &trainingData)
UINT getSize() const
Definition: Vector.h:191
UINT getNetworkSize() const
bool copyBaseVariables(const Clusterer *clusterer)
Definition: Clusterer.cpp:87
bool validateNetworkTypology(const UINT networkTypology)
bool loadClustererSettingsFromFile(std::fstream &file)
Definition: Clusterer.cpp:179
virtual bool map_(VectorFloat &x)
SelfOrganizingMap(const UINT networkSize=20, const UINT networkTypology=RANDOM_NETWORK, const UINT maxNumEpochs=1000, const Float alphaStart=0.8, const Float alphaEnd=0.1)
bool saveClustererSettingsToFile(std::fstream &file) const
Definition: Clusterer.cpp:157
Vector< T > getRowVector(const unsigned int r) const
Definition: Matrix.h:171
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
virtual bool saveModelToFile(std::fstream &file) const
virtual bool loadModelFromFile(std::fstream &file)
Vector< MinMax > getRanges() const
Float getRandomNumberUniform(Float minRange=0.0, Float maxRange=1.0)
Definition: Random.h:198
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 clear()
Definition: Clusterer.cpp:142
SelfOrganizingMap & operator=(const SelfOrganizingMap &rhs)