CEBL  2.1
LDA.cpp
Go to the documentation of this file.
1 #include "LDA.hpp"
2 
3 using namespace cppR;
4 using namespace ublas;
5 using std::cout;
6 using std::cerr;
7 
8 namespace CEBL {
9 
14  void LDA::train(const EEGTrainingData& data)
15  {
16  matrix<double> X = t(data.collapse());
17  ublas::vector<int> Y = data.getTargets();
18 
19  // save vector of class numbers
20  this->classes = unique(Y);
21  int n_classes = classes.size();
22  int n_samples = nrow(X);
23  int n_features = ncol(X);
24 
25  // create an empty member variables, not necessary, but makes clear
26  // what the sizes of these are going to be
27  this->covariance = createMatrix(0,n_features,n_features);
28  this->covarianceInv = createMatrix(0,n_features,n_features);
29  this->priors = createVector(0,n_classes);
30  this->means = createMatrix(0, n_classes, n_features);
31  this->weights = createMatrix( 0,n_classes, n_features );
32  this->bias = createVector(0,n_classes);
33 
34 
35  // compute the covariance matrix by looping though classes and updating
36  for(int k=0; k < n_classes; k++) {
37 
38  //allow for inturruption here
39  this->inturruptionPoint();
40 
41  std::vector<bool> mask = createMask(classes[k], Y);
42  matrix<double> Z = rowMask(X,mask);
43 
44  int n_samples_this_class = nrow(Z);
45  priors(k) = double(n_samples_this_class) / n_samples;
46 
47  row(this->means,k) = colMeans(Z);
48 
49  matrix<double> temp =
50  createMatrix(ublas::vector<double>(row(means,k)),
51  n_samples_this_class, n_features, true);
52  matrix<double> Zc = Z - temp;
53 
54  //update covariance
55  this->covariance = this->covariance + prod(t(Zc), Zc);
56  }
57 
58  //divide covariance by N - K
59  this->covariance = this->covariance / (n_samples - n_classes);
60 
61  //invert covariance
62  this->covarianceInv = solve(this->covariance);
63 
64  //compute weights and biases
65  this->weights = prod(means,covarianceInv);
66  matrix<double> wm = compProd(weights,means);
67  for (unsigned int i=0; i < priors.size(); i++)
68  priors[i] = log(priors[i]);
69  this->bias = -0.5 * rowSums(wm) + priors;
70 
71  // set trained flag
72  trained = true;
73  }
74 
75 
82  ublas::vector<int> LDA::use(const ublas::matrix<double> & data)
83  {
84  //X is transpose of data
85  ublas::matrix<double> X = t(data);
86 
87  int n_classes = classes.size();
88  int n_samples = nrow(X);
89 
90  //compute discriminant functions
91  ublas::matrix<double> disc_functions(n_samples,n_classes);
92  disc_functions = prod(X,t(weights));
93 
94  //add class bias to discriminant functions for each sample
95  for (unsigned int s = 0; s < disc_functions.size1(); s++)
96  row(disc_functions,s) = row(disc_functions,s) + bias;
97 
98  //for each column of deltas find max
99  ublas::vector<int> predicted_classes;
100  predicted_classes.resize(n_samples);
101 
102  for(int j=0; j<nrow(disc_functions); j++) {
103  ublas::vector<double> disc_functionsRow = row(disc_functions,j);
104  predicted_classes[j] = classes[whichMax(disc_functionsRow)];
105  }
106 
107  //----------------------------------------------------------------------
108  //compute probabilities for each class
109 
110  if(compute_probs)
111  {
112  // get vector of maximum for each row in disc functions
113  ublas::vector<double> max_disc =
114  cppR::rowApply<double>(disc_functions,&cppR::max<double>);
115 
116  // create a matrix of the same size as disc functions whose
117  // columns are max_disc
118  matrix<double> max_discs =
119  createMatrix(max_disc,disc_functions.size1(),disc_functions.size2());
120 
121  //take the exponent of the disc functions subtracted from their max
122  matrix<double> probabilities =
123  apply(matrix<double>(disc_functions - max_discs), exp);
124 
125  //get rowsums from probabilities
126  ublas::vector<double> sum_p = cppR::rowSums(probabilities);
127 
128  //divide probabilities by rowsums
129  matrix<double> sum_p_rep =
130  createMatrix(sum_p,probabilities.size1(),probabilities.size2());
131  probabilities = compDiv(probabilities, sum_p_rep);
132 
133  //copy to probabilities member variable
134  this->probabilities.resize(probabilities.size1());
135  for(unsigned i=0;i<probabilities.size1();i++)
136  this->probabilities[i] =
137  asStdVector(ublas::vector<double>(row(probabilities,i)));
138 
139  }
140 
141 
142  //return prediction
143  return predicted_classes;
144  }
145 
150  map<string, SerializedObject> LDA::save() const
151  {
152  map<string, SerializedObject> ret;
153  ret["trained"] = serialize(trained);
154  ret["means"] = serialize(means);
155  ret["covariance"] = serialize(covariance);
156  ret["covarianceInv"] = serialize(covarianceInv);
157  ret["classes"] = serialize(classes);
158  ret["weights"] = serialize(weights);
159  ret["bias"] = serialize(bias);
160  return ret;
161  }
162 
166  void LDA::load(map<string, SerializedObject> objects)
167  {
168  deserialize(objects["trained"],trained);
169  deserialize(objects["means"],means);
170  deserialize(objects["covariance"],covariance);
171  deserialize(objects["covarianceInv"],covarianceInv);
172  deserialize(objects["classes"],classes);
173  deserialize(objects["weights"],weights);
174  deserialize(objects["bias"],bias);
175  }
176 
177 
179  std::map<std::string, CEBL::Param> LDA::getParamsList()
180  {
181  std::map<std::string, CEBL::Param> params;
182  CEBL::Param probs("Compute Probabilities",
183  "Should LDA compute probabilities? If this is checked, LDA will need to create covariance matrices when training.",
184  compute_probs);
185  params["probs"] = probs;
186 
187  return params;
188  }
189 
191  void LDA::setParamsList( std::map<std::string, CEBL::Param> &p)
192  {
193  compute_probs = p["probs"].getBool();
194  }
195 }
196 
197 
198 /*************************************************************/
199 //DYNAMIC LOADING
200 
202 {
203  return new CEBL::LDA;
204 }
205 
206 extern "C" void ObjectDestroy(CEBL::Classifier* p)
207 {
208  delete p;
209 }