SHOGUN  v2.0.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
QDA.cpp
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 3 of the License, or
5  * (at your option) any later version.
6  *
7  * Written (W) 2012 Fernando José Iglesias García
8  * Copyright (C) 2012 Fernando José Iglesias García
9  */
10 
11 #include <shogun/lib/common.h>
12 
13 #ifdef HAVE_LAPACK
14 
15 #include <shogun/multiclass/QDA.h>
18 #include <shogun/labels/Labels.h>
22 
23 using namespace shogun;
24 
25 CQDA::CQDA(float64_t tolerance, bool store_covs)
26 : CNativeMulticlassMachine(), m_tolerance(tolerance),
27  m_store_covs(store_covs), m_num_classes(0), m_dim(0)
28 {
29  init();
30 }
31 
32 CQDA::CQDA(CDenseFeatures<float64_t>* traindat, CLabels* trainlab, float64_t tolerance, bool store_covs)
33 : CNativeMulticlassMachine(), m_tolerance(tolerance), m_store_covs(store_covs), m_num_classes(0), m_dim(0)
34 {
35  init();
36  set_features(traindat);
37  set_labels(trainlab);
38 }
39 
41 {
42  SG_UNREF(m_features);
43 
44  cleanup();
45 }
46 
47 void CQDA::init()
48 {
49  SG_ADD(&m_tolerance, "m_tolerance", "Tolerance member.", MS_AVAILABLE);
50  SG_ADD(&m_store_covs, "m_store_covs", "Store covariances member", MS_NOT_AVAILABLE);
51  SG_ADD((CSGObject**) &m_features, "m_features", "Feature object.", MS_NOT_AVAILABLE);
52  SG_ADD(&m_means, "m_means", "Mean vectors list", MS_NOT_AVAILABLE);
53  SG_ADD(&m_slog, "m_slog", "Vector used in classification", MS_NOT_AVAILABLE);
54 
55  //TODO include SGNDArray objects for serialization
56 
57  m_features = NULL;
58 }
59 
60 void CQDA::cleanup()
61 {
62  if ( m_store_covs )
63  m_covs.destroy_ndarray();
64 
65  m_covs.free_ndarray();
66  m_M.free_ndarray();
67  m_means=SGMatrix<float64_t>();
68 
69  m_num_classes = 0;
70 }
71 
73 {
74  if (data)
75  {
76  if (!data->has_property(FP_DOT))
77  SG_ERROR("Specified features are not of type CDotFeatures\n");
78 
79  set_features((CDotFeatures*) data);
80  }
81 
82  if ( !m_features )
83  return NULL;
84 
85  int32_t num_vecs = m_features->get_num_vectors();
86  ASSERT(num_vecs > 0);
87  ASSERT( m_dim == m_features->get_dim_feature_space() );
88 
90 
91  SGMatrix< float64_t > X(num_vecs, m_dim);
92  SGMatrix< float64_t > A(num_vecs, m_dim);
93  SGVector< float64_t > norm2(num_vecs*m_num_classes);
94  norm2.zero();
95 
96  int i, j, k, vlen;
97  bool vfree;
98  float64_t* vec;
99  for ( k = 0 ; k < m_num_classes ; ++k )
100  {
101  // X = features - means
102  for ( i = 0 ; i < num_vecs ; ++i )
103  {
104  vec = rf->get_feature_vector(i, vlen, vfree);
105  ASSERT(vec);
106 
107  for ( j = 0 ; j < m_dim ; ++j )
108  X[i + j*num_vecs] = vec[j] - m_means[k*m_dim + j];
109 
110  rf->free_feature_vector(vec, i, vfree);
111 
112  }
113 
114  cblas_dgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, num_vecs, m_dim,
115  m_dim, 1.0, X.matrix, num_vecs, m_M.get_matrix(k), m_dim, 0.0,
116  A.matrix, num_vecs);
117 
118  for ( i = 0 ; i < num_vecs ; ++i )
119  for ( j = 0 ; j < m_dim ; ++j )
120  norm2[i + k*num_vecs] += CMath::sq(A[i + j*num_vecs]);
121 
122 #ifdef DEBUG_QDA
123  CMath::display_matrix(A.matrix, num_vecs, m_dim, "A");
124 #endif
125  }
126 
127  for ( i = 0 ; i < num_vecs ; ++i )
128  for ( k = 0 ; k < m_num_classes ; ++k )
129  {
130  norm2[i + k*num_vecs] += m_slog[k];
131  norm2[i + k*num_vecs] *= -0.5;
132  }
133 
134  CMulticlassLabels* out = new CMulticlassLabels(num_vecs);
135 
136  for ( i = 0 ; i < num_vecs ; ++i )
137  out->set_label(i, SGVector<float64_t>::arg_max(norm2.vector+i, num_vecs, m_num_classes));
138 
139 #ifdef DEBUG_QDA
140  CMath::display_matrix(norm2.vector, num_vecs, m_num_classes, "norm2");
141  CMath::display_vector(out->get_labels().vector, num_vecs, "Labels");
142 #endif
143 
144  return out;
145 }
146 
148 {
149  if ( !m_labels )
150  SG_ERROR("No labels allocated in QDA training\n");
151 
152  if ( data )
153  {
154  if ( !data->has_property(FP_DOT) )
155  SG_ERROR("Speficied features are not of type CDotFeatures\n");
156  set_features((CDotFeatures*) data);
157  }
158  if ( !m_features )
159  SG_ERROR("No features allocated in QDA training\n");
160  SGVector< int32_t > train_labels = ((CMulticlassLabels*) m_labels)->get_int_labels();
161  if ( !train_labels.vector )
162  SG_ERROR("No train_labels allocated in QDA training\n");
163 
164  cleanup();
165 
166  m_num_classes = ((CMulticlassLabels*) m_labels)->get_num_classes();
167  m_dim = m_features->get_dim_feature_space();
168  int32_t num_vec = m_features->get_num_vectors();
169  if ( num_vec != train_labels.vlen )
170  SG_ERROR("Dimension mismatch between features and labels in QDA training");
171 
172  int32_t* class_idxs = SG_MALLOC(int32_t, num_vec*m_num_classes);
173  // number of examples of each class
174  int32_t* class_nums = SG_MALLOC(int32_t, m_num_classes);
175  memset(class_nums, 0, m_num_classes*sizeof(int32_t));
176  int32_t class_idx;
177  int32_t i, j, k;
178  for ( i = 0 ; i < train_labels.vlen ; ++i )
179  {
180  class_idx = train_labels.vector[i];
181 
182  if ( class_idx < 0 || class_idx >= m_num_classes )
183  {
184  SG_ERROR("found label out of {0, 1, 2, ..., num_classes-1}...");
185  return false;
186  }
187  else
188  {
189  class_idxs[ class_idx*num_vec + class_nums[class_idx]++ ] = i;
190  }
191  }
192 
193  for ( i = 0 ; i < m_num_classes ; ++i )
194  {
195  if ( class_nums[i] <= 0 )
196  {
197  SG_ERROR("What? One class with no elements\n");
198  return false;
199  }
200  }
201 
202  if ( m_store_covs )
203  {
204  // cov_dims will be free in m_covs.destroy_ndarray()
205  index_t * cov_dims = SG_MALLOC(index_t, 3);
206  cov_dims[0] = m_dim;
207  cov_dims[1] = m_dim;
208  cov_dims[2] = m_num_classes;
209  m_covs = SGNDArray< float64_t >(cov_dims, 3, true);
210  }
211 
212  m_means = SGMatrix< float64_t >(m_dim, m_num_classes, true);
213  SGMatrix< float64_t > scalings = SGMatrix< float64_t >(m_dim, m_num_classes);
214 
215  // rot_dims will be freed in rotations.destroy_ndarray()
216  index_t* rot_dims = SG_MALLOC(index_t, 3);
217  rot_dims[0] = m_dim;
218  rot_dims[1] = m_dim;
219  rot_dims[2] = m_num_classes;
220  SGNDArray< float64_t > rotations = SGNDArray< float64_t >(rot_dims, 3);
221 
223 
224  m_means.zero();
225 
226  int32_t vlen;
227  bool vfree;
228  float64_t* vec;
229  for ( k = 0 ; k < m_num_classes ; ++k )
230  {
231  SGMatrix< float64_t > buffer(class_nums[k], m_dim);
232  for ( i = 0 ; i < class_nums[k] ; ++i )
233  {
234  vec = rf->get_feature_vector(class_idxs[k*num_vec + i], vlen, vfree);
235  ASSERT(vec);
236 
237  for ( j = 0 ; j < vlen ; ++j )
238  {
239  m_means[k*m_dim + j] += vec[j];
240  buffer[i + j*class_nums[k]] = vec[j];
241  }
242 
243  rf->free_feature_vector(vec, class_idxs[k*num_vec + i], vfree);
244  }
245 
246  for ( j = 0 ; j < m_dim ; ++j )
247  m_means[k*m_dim + j] /= class_nums[k];
248 
249  for ( i = 0 ; i < class_nums[k] ; ++i )
250  for ( j = 0 ; j < m_dim ; ++j )
251  buffer[i + j*class_nums[k]] -= m_means[k*m_dim + j];
252 
253  /* calling external lib, buffer = U * S * V^T, U is not interesting here */
254  char jobu = 'N', jobvt = 'A';
255  int m = class_nums[k], n = m_dim;
256  int lda = m, ldu = m, ldvt = n;
257  int info = -1;
258  float64_t * col = scalings.get_column_vector(k);
259  float64_t * rot_mat = rotations.get_matrix(k);
260 
261  wrap_dgesvd(jobu, jobvt, m, n, buffer.matrix, lda, col, NULL, ldu,
262  rot_mat, ldvt, &info);
263  ASSERT(info == 0);
264  buffer=SGMatrix<float64_t>();
265 
266  SGVector<float64_t>::vector_multiply(col, col, col, m_dim);
267  SGVector<float64_t>::scale_vector(1.0/(m-1), col, m_dim);
268  rotations.transpose_matrix(k);
269 
270  if ( m_store_covs )
271  {
272  SGMatrix< float64_t > M(n ,n);
273 
274  M.matrix = SGVector<float64_t>::clone_vector(rot_mat, n*n);
275  for ( i = 0 ; i < m_dim ; ++i )
276  for ( j = 0 ; j < m_dim ; ++j )
277  M[i + j*m_dim] *= scalings[k*m_dim + j];
278 
279  cblas_dgemm(CblasColMajor, CblasNoTrans, CblasTrans, n, n, n, 1.0,
280  M.matrix, n, rot_mat, n, 0.0, m_covs.get_matrix(k), n);
281  }
282  }
283 
284  /* Computation of terms required for classification */
285 
286  SGVector< float32_t > sinvsqrt(m_dim);
287 
288  // M_dims will be freed in m_M.destroy_ndarray()
289  index_t* M_dims = SG_MALLOC(index_t, 3);
290  M_dims[0] = m_dim;
291  M_dims[1] = m_dim;
292  M_dims[2] = m_num_classes;
293  m_M = SGNDArray< float64_t >(M_dims, 3, true);
294 
295  m_slog = SGVector< float32_t >(m_num_classes);
296  m_slog.zero();
297 
298  index_t idx = 0;
299  for ( k = 0 ; k < m_num_classes ; ++k )
300  {
301  for ( j = 0 ; j < m_dim ; ++j )
302  {
303  sinvsqrt[j] = 1.0 / CMath::sqrt(scalings[k*m_dim + j]);
304  m_slog[k] += CMath::log(scalings[k*m_dim + j]);
305  }
306 
307  for ( i = 0 ; i < m_dim ; ++i )
308  for ( j = 0 ; j < m_dim ; ++j )
309  {
310  idx = k*m_dim*m_dim + i + j*m_dim;
311  m_M[idx] = rotations[idx] * sinvsqrt[j];
312  }
313  }
314 
315 #ifdef DEBUG_QDA
316  SG_PRINT(">>> QDA machine trained with %d classes\n", m_num_classes);
317 
318  SG_PRINT("\n>>> Displaying means ...\n");
319  CMath::display_matrix(m_means.matrix, m_dim, m_num_classes);
320 
321  SG_PRINT("\n>>> Displaying scalings ...\n");
322  CMath::display_matrix(scalings.matrix, m_dim, m_num_classes);
323 
324  SG_PRINT("\n>>> Displaying rotations ... \n");
325  for ( k = 0 ; k < m_num_classes ; ++k )
326  CMath::display_matrix(rotations.get_matrix(k), m_dim, m_dim);
327 
328  SG_PRINT("\n>>> Displaying sinvsqrt ... \n");
329  sinvsqrt.display_vector();
330 
331  SG_PRINT("\n>>> Diplaying m_M matrices ... \n");
332  for ( k = 0 ; k < m_num_classes ; ++k )
333  CMath::display_matrix(m_M.get_matrix(k), m_dim, m_dim);
334 
335  SG_PRINT("\n>>> Exit DEBUG_QDA\n");
336 #endif
337 
338  rotations.destroy_ndarray();
339  SG_FREE(class_idxs);
340  SG_FREE(class_nums);
341  return true;
342 }
343 
344 #endif /* HAVE_LAPACK */

SHOGUN Machine Learning Toolbox - Documentation