GestureRecognitionToolkit  Version: 1.0 Revision: 04-03-15
The Gesture Recognition Toolkit (GRT) is a cross-platform, open-source, c++ machine learning library for real-time gesture recognition.
RandomForests.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 #include "RandomForests.h"
22 
23 namespace GRT{
24 
25 //Register the RandomForests module with the Classifier base class
26 RegisterClassifierModule< RandomForests > RandomForests::registerModule("RandomForests");
27 
28 RandomForests::RandomForests(const DecisionTreeNode &decisionTreeNode,const UINT forestSize,const UINT numRandomSplits,const UINT minNumSamplesPerNode,const UINT maxDepth,const UINT trainingMode,const bool removeFeaturesAtEachSpilt,const bool useScaling)
29 {
30  this->decisionTreeNode = decisionTreeNode.deepCopy();
31  this->forestSize = forestSize;
32  this->numRandomSplits = numRandomSplits;
33  this->minNumSamplesPerNode = minNumSamplesPerNode;
34  this->maxDepth = maxDepth;
35  this->trainingMode = trainingMode;
36  this->removeFeaturesAtEachSpilt = removeFeaturesAtEachSpilt;
37  this->useScaling = useScaling;
38  classType = "RandomForests";
39  classifierType = classType;
40  classifierMode = STANDARD_CLASSIFIER_MODE;
41  useNullRejection = false;
42  supportsNullRejection = false;
43  debugLog.setProceedingText("[DEBUG RandomForests]");
44  errorLog.setProceedingText("[ERROR RandomForests]");
45  trainingLog.setProceedingText("[TRAINING RandomForests]");
46  warningLog.setProceedingText("[WARNING RandomForests]");
47 }
48 
50  this->decisionTreeNode = NULL;
51  classType = "RandomForests";
52  classifierType = classType;
53  classifierMode = STANDARD_CLASSIFIER_MODE;
54  debugLog.setProceedingText("[DEBUG RandomForests]");
55  errorLog.setProceedingText("[ERROR RandomForests]");
56  trainingLog.setProceedingText("[TRAINING RandomForests]");
57  warningLog.setProceedingText("[WARNING RandomForests]");
58  *this = rhs;
59 }
60 
62 {
63  clear();
64 
65  if( decisionTreeNode != NULL ){
66  delete decisionTreeNode;
67  decisionTreeNode = NULL;
68  }
69 }
70 
72  if( this != &rhs ){
73  //Clear this tree
74  clear();
75 
76  //Copy the base classifier variables
77  if( copyBaseVariables( (Classifier*)&rhs ) ){
78 
79  //Deep copy the main node
80  if( this->decisionTreeNode != NULL ){
81  delete decisionTreeNode;
82  decisionTreeNode = NULL;
83  }
84  this->decisionTreeNode = rhs.deepCopyDecisionTreeNode();
85 
86  if( rhs.getTrained() ){
87  //Deep copy the forest
88  for(UINT i=0; i<rhs.forest.size(); i++){
89  this->forest.push_back( rhs.forest[i]->deepCopy() );
90  }
91  }
92 
93  this->forestSize = rhs.forestSize;
94  this->numRandomSplits = rhs.numRandomSplits;
95  this->minNumSamplesPerNode = rhs.minNumSamplesPerNode;
96  this->maxDepth = rhs.maxDepth;
97  this->removeFeaturesAtEachSpilt = rhs.removeFeaturesAtEachSpilt;
98  this->trainingMode = rhs.trainingMode;
99 
100  }else errorLog << "deepCopyFrom(const Classifier *classifier) - Failed to copy base variables!" << endl;
101  }
102  return *this;
103 }
104 
105 bool RandomForests::deepCopyFrom(const Classifier *classifier){
106 
107  if( classifier == NULL ) return false;
108 
109  if( this->getClassifierType() == classifier->getClassifierType() ){
110 
111  RandomForests *ptr = (RandomForests*)classifier;
112 
113  //Clear this tree
114  this->clear();
115 
116  if( copyBaseVariables( classifier ) ){
117 
118  //Deep copy the main node
119  if( this->decisionTreeNode != NULL ){
120  delete decisionTreeNode;
121  decisionTreeNode = NULL;
122  }
123  this->decisionTreeNode = ptr->deepCopyDecisionTreeNode();
124 
125  if( ptr->getTrained() ){
126  //Deep copy the forest
127  for(UINT i=0; i<ptr->forest.size(); i++){
128  this->forest.push_back( ptr->forest[i]->deepCopy() );
129  }
130  }
131 
132  this->forestSize = ptr->forestSize;
133  this->numRandomSplits = ptr->numRandomSplits;
134  this->minNumSamplesPerNode = ptr->minNumSamplesPerNode;
135  this->maxDepth = ptr->maxDepth;
136  this->removeFeaturesAtEachSpilt = ptr->removeFeaturesAtEachSpilt;
137  this->trainingMode = ptr->trainingMode;
138 
139  return true;
140  }
141 
142  errorLog << "deepCopyFrom(const Classifier *classifier) - Failed to copy base variables!" << endl;
143  }
144  return false;
145 }
146 
148 
149  //Clear any previous model
150  clear();
151 
152  const unsigned int M = trainingData.getNumSamples();
153  const unsigned int N = trainingData.getNumDimensions();
154  const unsigned int K = trainingData.getNumClasses();
155 
156  if( M == 0 ){
157  errorLog << "train_(ClassificationData &labelledTrainingData) - Training data has zero samples!" << endl;
158  return false;
159  }
160 
161  numInputDimensions = N;
162  numClasses = K;
163  classLabels = trainingData.getClassLabels();
164  ranges = trainingData.getRanges();
165 
166  //Scale the training data if needed
167  if( useScaling ){
168  //Scale the training data between 0 and 1
169  trainingData.scale(0, 1);
170  }
171 
172  //Flag that the main algorithm has been trained encase we need to trigger any callbacks
173  trained = true;
174 
175  //Train the random forest
176  for(UINT i=0; i<forestSize; i++){
177 
178  //Get a bootstrapped dataset
179  ClassificationData data = trainingData.getBootstrappedDataset();
180 
181  DecisionTree tree;
182  tree.setDecisionTreeNode( *decisionTreeNode );
183  tree.enableScaling( false ); //We have already scaled the training data so we do not need to scale it again
184  tree.setTrainingMode( trainingMode );
185  tree.setNumSplittingSteps( numRandomSplits );
186  tree.setMinNumSamplesPerNode( minNumSamplesPerNode );
187  tree.setMaxDepth( maxDepth );
188  tree.enableNullRejection( useNullRejection );
189  tree.setRemoveFeaturesAtEachSpilt( removeFeaturesAtEachSpilt );
190 
191  //Train this tree
192  if( !tree.train( data ) ){
193  errorLog << "train_(ClassificationData &labelledTrainingData) - Failed to train tree at forest index: " << i << endl;
194  clear();
195  return false;
196  }
197 
198  //Deep copy the tree into the forest
199  forest.push_back( tree.deepCopyTree() );
200  }
201 
202  return true;
203 }
204 
205 bool RandomForests::predict_(VectorDouble &inputVector){
206 
207  predictedClassLabel = 0;
208  maxLikelihood = 0;
209 
210  if( !trained ){
211  errorLog << "predict_(VectorDouble &inputVector) - Model Not Trained!" << endl;
212  return false;
213  }
214 
215  if( inputVector.size() != numInputDimensions ){
216  errorLog << "predict_(VectorDouble &inputVector) - The size of the input vector (" << inputVector.size() << ") does not match the num features in the model (" << numInputDimensions << endl;
217  return false;
218  }
219 
220  if( useScaling ){
221  for(UINT n=0; n<numInputDimensions; n++){
222  inputVector[n] = scale(inputVector[n], ranges[n].minValue, ranges[n].maxValue, 0, 1);
223  }
224  }
225 
226  if( classLikelihoods.size() != numClasses ) classLikelihoods.resize(numClasses,0);
227  if( classDistances.size() != numClasses ) classDistances.resize(numClasses,0);
228 
229  std::fill(classDistances.begin(),classDistances.end(),0);
230 
231  //Run the prediction for each tree in the forest
232  VectorDouble y;
233  for(UINT i=0; i<forestSize; i++){
234  if( !forest[i]->predict(inputVector, y) ){
235  errorLog << "predict_(VectorDouble &inputVector) - Tree " << i << " failed prediction!" << endl;
236  return false;
237  }
238 
239  for(UINT j=0; j<numClasses; j++){
240  classDistances[j] += y[j];
241  }
242  }
243 
244  //Use the class distances to estimate the class likelihoods
245  bestDistance = 0;
246  UINT bestIndex = 0;
247  for(UINT k=0; k<numClasses; k++){
248  classLikelihoods[k] = classDistances[k] / double(forestSize);
249 
250  if( classLikelihoods[k] > maxLikelihood ){
251  maxLikelihood = classLikelihoods[k];
252  bestDistance = classDistances[k];
253  bestIndex = k;
254  }
255  }
256 
257  predictedClassLabel = classLabels[ bestIndex ];
258 
259  return true;
260 }
261 
263 
264  //Call the classifiers clear function
266 
267  //Delete the forest
268  for(UINT i=0; i<forest.size(); i++){
269  if( forest[i] != NULL ){
270  forest[i]->clear();
271  delete forest[i];
272  forest[i] = NULL;
273  }
274  }
275  forest.clear();
276 
277  return true;
278 }
279 
280 bool RandomForests::print() const{
281 
282  cout << "RandomForest\n";
283  cout << "ForestSize: " << forestSize << endl;
284  cout << "NumSplittingSteps: " << numRandomSplits << endl;
285  cout << "MinNumSamplesPerNode: " << minNumSamplesPerNode << endl;
286  cout << "MaxDepth: " << maxDepth << endl;
287  cout << "RemoveFeaturesAtEachSpilt: " << removeFeaturesAtEachSpilt << endl;
288  cout << "TrainingMode: " << trainingMode << endl;
289  cout << "ForestBuilt: " << (trained ? 1 : 0) << endl;
290 
291  if( trained ){
292  cout << "Forest:\n";
293  for(UINT i=0; i<forestSize; i++){
294  cout << "Tree: " << i+1 << endl;
295  forest[i]->print();
296  }
297  }
298 
299  return true;
300 }
301 
302 bool RandomForests::saveModelToFile(fstream &file) const{
303 
304  if(!file.is_open())
305  {
306  errorLog <<"saveModelToFile(fstream &file) - The file is not open!" << endl;
307  return false;
308  }
309 
310  //Write the header info
311  file << "GRT_RANDOM_FOREST_MODEL_FILE_V1.0\n";
312 
313  //Write the classifier settings to the file
315  errorLog <<"saveModelToFile(fstream &file) - Failed to save classifier base settings to file!" << endl;
316  return false;
317  }
318 
319  if( decisionTreeNode != NULL ){
320  file << "DecisionTreeNodeType: " << decisionTreeNode->getNodeType() << endl;
321  if( !decisionTreeNode->saveToFile( file ) ){
322  Classifier::errorLog <<"saveModelToFile(fstream &file) - Failed to save decisionTreeNode settings to file!" << endl;
323  return false;
324  }
325  }else{
326  file << "DecisionTreeNodeType: " << "NULL" << endl;
327  }
328 
329  file << "ForestSize: " << forestSize << endl;
330  file << "NumSplittingSteps: " << numRandomSplits << endl;
331  file << "MinNumSamplesPerNode: " << minNumSamplesPerNode << endl;
332  file << "MaxDepth: " << maxDepth << endl;
333  file << "RemoveFeaturesAtEachSpilt: " << removeFeaturesAtEachSpilt << endl;
334  file << "TrainingMode: " << trainingMode << endl;
335  file << "ForestBuilt: " << (trained ? 1 : 0) << endl;
336 
337  if( trained ){
338  file << "Forest:\n";
339  for(UINT i=0; i<forestSize; i++){
340  file << "Tree: " << i+1 << endl;
341  file << "TreeNodeType: " << forest[i]->getNodeType() << endl;
342  if( !forest[i]->saveToFile( file ) ){
343  errorLog << "saveModelToFile(fstream &file) - Failed to save tree " << i << " to file!" << endl;
344  return false;
345  }
346  }
347  }
348 
349  return true;
350 }
351 
353 
354  clear();
355 
356  if(!file.is_open())
357  {
358  errorLog << "loadModelFromFile(string filename) - Could not open file to load model" << endl;
359  return false;
360  }
361 
362  std::string word;
363  std::string treeNodeType;
364 
365  file >> word;
366 
367  //Find the file type header
368  if(word != "GRT_RANDOM_FOREST_MODEL_FILE_V1.0"){
369  errorLog << "loadModelFromFile(string filename) - Could not find Model File Header" << endl;
370  return false;
371  }
372 
373  //Load the base settings from the file
375  errorLog << "loadModelFromFile(string filename) - Failed to load base settings from file!" << endl;
376  return false;
377  }
378 
379  file >> word;
380  if(word != "DecisionTreeNodeType:"){
381  Classifier::errorLog << "loadModelFromFile(string filename) - Could not find the DecisionTreeNodeType!" << endl;
382  return false;
383  }
384  file >> treeNodeType;
385 
386  if( treeNodeType != "NULL" ){
387 
388  decisionTreeNode = dynamic_cast< DecisionTreeNode* >( DecisionTreeNode::createInstanceFromString( treeNodeType ) );
389 
390  if( decisionTreeNode == NULL ){
391  Classifier::errorLog << "loadModelFromFile(string filename) - Could not create new DecisionTreeNode from type: " << treeNodeType << endl;
392  return false;
393  }
394 
395  if( !decisionTreeNode->loadFromFile( file ) ){
396  Classifier::errorLog <<"loadModelFromFile(fstream &file) - Failed to load decisionTreeNode settings from file!" << endl;
397  return false;
398  }
399  }else{
400  Classifier::errorLog <<"loadModelFromFile(fstream &file) - Failed to load decisionTreeNode! DecisionTreeNodeType is NULL!" << endl;
401  return false;
402  }
403 
404  file >> word;
405  if(word != "ForestSize:"){
406  errorLog << "loadModelFromFile(string filename) - Could not find the ForestSize!" << endl;
407  return false;
408  }
409  file >> forestSize;
410 
411  file >> word;
412  if(word != "NumSplittingSteps:"){
413  errorLog << "loadModelFromFile(string filename) - Could not find the NumSplittingSteps!" << endl;
414  return false;
415  }
416  file >> numRandomSplits;
417 
418  file >> word;
419  if(word != "MinNumSamplesPerNode:"){
420  errorLog << "loadModelFromFile(string filename) - Could not find the MinNumSamplesPerNode!" << endl;
421  return false;
422  }
423  file >> minNumSamplesPerNode;
424 
425  file >> word;
426  if(word != "MaxDepth:"){
427  errorLog << "loadModelFromFile(string filename) - Could not find the MaxDepth!" << endl;
428  return false;
429  }
430  file >> maxDepth;
431 
432  file >> word;
433  if(word != "RemoveFeaturesAtEachSpilt:"){
434  errorLog << "loadModelFromFile(string filename) - Could not find the RemoveFeaturesAtEachSpilt!" << endl;
435  return false;
436  }
437  file >> removeFeaturesAtEachSpilt;
438 
439  file >> word;
440  if(word != "TrainingMode:"){
441  errorLog << "loadModelFromFile(string filename) - Could not find the TrainingMode!" << endl;
442  return false;
443  }
444  file >> trainingMode;
445 
446  file >> word;
447  if(word != "ForestBuilt:"){
448  errorLog << "loadModelFromFile(string filename) - Could not find the ForestBuilt!" << endl;
449  return false;
450  }
451  file >> trained;
452 
453  if( trained ){
454  //Find the forest header
455  file >> word;
456  if(word != "Forest:"){
457  errorLog << "loadModelFromFile(string filename) - Could not find the Forest!" << endl;
458  return false;
459  }
460 
461  //Load each tree
462  UINT treeIndex;
463  for(UINT i=0; i<forestSize; i++){
464 
465  file >> word;
466  if(word != "Tree:"){
467  errorLog << "loadModelFromFile(string filename) - Could not find the Tree Header!" << endl;
468  cout << "WORD: " << word << endl;
469  cout << "Tree i: " << i << endl;
470  return false;
471  }
472  file >> treeIndex;
473 
474  if( treeIndex != i+1 ){
475  errorLog << "loadModelFromFile(string filename) - Incorrect tree index: " << treeIndex << endl;
476  return false;
477  }
478 
479  file >> word;
480  if(word != "TreeNodeType:"){
481  errorLog << "loadModelFromFile(string filename) - Could not find the TreeNodeType!" << endl;
482  cout << "WORD: " << word << endl;
483  cout << "i: " << i << endl;
484  return false;
485  }
486  file >> treeNodeType;
487 
488  //Create a new DTree
489  DecisionTreeNode *tree = dynamic_cast< DecisionTreeNode* >( DecisionTreeNode::createInstanceFromString( treeNodeType ) );
490 
491  if( tree == NULL ){
492  errorLog << "loadModelFromFile(fstream &file) - Failed to create new Tree!" << endl;
493  return false;
494  }
495 
496  //Load the tree from the file
497  tree->setParent( NULL );
498  if( !tree->loadFromFile( file ) ){
499  errorLog << "loadModelFromFile(fstream &file) - Failed to load tree from file!" << endl;
500  return false;
501  }
502 
503  //Add the tree to the forest
504  forest.push_back( tree );
505  }
506  }
507 
508  return true;
509 }
510 
512  return forestSize;
513 }
514 
516  return numRandomSplits;
517 }
518 
520  return minNumSamplesPerNode;
521 }
522 
524  return maxDepth;
525 }
526 
528  return trainingMode;
529 }
530 
532  return removeFeaturesAtEachSpilt;
533 }
534 
535 const vector< DecisionTreeNode* > RandomForests::getForest() const {
536  return forest;
537 }
538 
540 
541  if( decisionTreeNode == NULL ){
542  return NULL;
543  }
544 
545  return decisionTreeNode->deepCopy();
546 }
547 
548 bool RandomForests::setForestSize(const UINT forestSize){
549  if( forestSize > 0 ){
550  this->forestSize = forestSize;
551  clear();
552  return true;
553  }
554  return false;
555 }
556 
557 bool RandomForests::setNumRandomSplits(const UINT numRandomSplits){
558  if( numRandomSplits > 0 ){
559  this->numRandomSplits = numRandomSplits;
560  return true;
561  }
562  return false;
563 }
564 
565 bool RandomForests::setMinNumSamplesPerNode(const UINT minNumSamplesPerNode){
566  if( minNumSamplesPerNode > 0 ){
567  this->minNumSamplesPerNode = minNumSamplesPerNode;
568  return true;
569  }
570  return false;
571 }
572 
573 bool RandomForests::setMaxDepth(const UINT maxDepth){
574  if( maxDepth > 0 ){
575  this->maxDepth = maxDepth;
576  return true;
577  }
578  return false;
579 }
580 
581 bool RandomForests::setRemoveFeaturesAtEachSpilt(const bool removeFeaturesAtEachSpilt){
582  this->removeFeaturesAtEachSpilt = removeFeaturesAtEachSpilt;
583  return true;
584 }
585 
586 bool RandomForests::setTrainingMode(const UINT trainingMode){
587 
588  if( trainingMode == DecisionTree::BEST_ITERATIVE_SPILT || trainingMode == DecisionTree::BEST_RANDOM_SPLIT ){
589  this->trainingMode = trainingMode;
590  return true;
591  }
592 
593  warningLog << "setTrainingMode(const UINT mode) - Unknown training mode!" << endl;
594  return false;
595 }
596 
598 
599  if( decisionTreeNode != NULL ){
600  delete decisionTreeNode;
601  decisionTreeNode = NULL;
602  }
603  this->decisionTreeNode = node.deepCopy();
604 
605  return true;
606 }
607 
608 } //End of namespace GRT
609 
virtual bool deepCopyFrom(const Classifier *classifier)
virtual ~RandomForests(void)
virtual bool print() const
bool getRemoveFeaturesAtEachSpilt() const
bool setMinNumSamplesPerNode(const UINT minNumSamplesPerNode)
This class implements a Random Decision Forest classifier.
vector< UINT > getClassLabels() const
bool copyBaseVariables(const Classifier *classifier)
Definition: Classifier.cpp:91
bool setTrainingMode(const UINT trainingMode)
Definition: Tree.cpp:120
bool setNumRandomSplits(const UINT numSplittingSteps)
Definition: AdaBoost.cpp:25
virtual bool loadFromFile(fstream &file)
Definition: Node.cpp:173
bool setDecisionTreeNode(const DecisionTreeNode &node)
bool loadBaseSettingsFromFile(fstream &file)
Definition: Classifier.cpp:301
RandomForests & operator=(const RandomForests &rhs)
virtual bool saveToFile(fstream &file) const
Definition: Node.cpp:131
virtual bool train(ClassificationData trainingData)
Definition: MLBase.cpp:80
virtual bool loadModelFromFile(fstream &file)
string getNodeType() const
Definition: Node.cpp:295
bool enableScaling(bool useScaling)
Definition: MLBase.cpp:235
virtual bool saveModelToFile(fstream &file) const
virtual bool clear()
bool saveBaseSettingsToFile(fstream &file) const
Definition: Classifier.cpp:254
bool setRemoveFeaturesAtEachSpilt(const bool removeFeaturesAtEachSpilt)
double scale(const double &x, const double &minSource, const double &maxSource, const double &minTarget, const double &maxTarget, const bool constrain=false)
Definition: MLBase.h:339
DecisionTreeNode * deepCopy() const
bool scale(const double minTarget, const double maxTarget)
bool setNumSplittingSteps(const UINT numSplittingSteps)
Definition: Tree.cpp:129
DecisionTreeNode * deepCopyTree() const
virtual bool predict(VectorDouble inputVector)
Definition: MLBase.cpp:104
bool setDecisionTreeNode(const DecisionTreeNode &node)
virtual bool predict_(VectorDouble &inputVector)
UINT getMinNumSamplesPerNode() const
virtual bool clear()
Definition: Classifier.cpp:140
bool setForestSize(const UINT forestSize)
UINT getTrainingMode() const
UINT getMaxDepth() const
virtual bool train_(ClassificationData &trainingData)
UINT getNumRandomSplits() const
bool getTrained() const
Definition: MLBase.cpp:223
vector< MinMax > getRanges() const
RandomForests(const DecisionTreeNode &decisionTreeNode=DecisionTreeClusterNode(), const UINT forestSize=10, const UINT numRandomSplits=100, const UINT minNumSamplesPerNode=5, const UINT maxDepth=10, const UINT trainingMode=DecisionTree::BEST_RANDOM_SPLIT, const bool removeFeaturesAtEachSpilt=true, const bool useScaling=false)
UINT getForestSize() const
string getClassifierType() const
Definition: Classifier.cpp:159
bool enableNullRejection(bool useNullRejection)
Definition: Classifier.cpp:229
bool setRemoveFeaturesAtEachSpilt(const bool removeFeaturesAtEachSpilt)
Definition: Tree.cpp:156
static Node * createInstanceFromString(string const &nodeType)
Definition: Node.cpp:28
bool setMaxDepth(const UINT maxDepth)
ClassificationData getBootstrappedDataset(UINT numSamples=0) const
DecisionTreeNode * deepCopyDecisionTreeNode() const
bool setMaxDepth(const UINT maxDepth)
Definition: Tree.cpp:147
bool setMinNumSamplesPerNode(const UINT minNumSamplesPerNode)
Definition: Tree.cpp:138
bool setTrainingMode(const UINT trainingMode)