SHOGUN  v2.0.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
GradientModelSelection.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  * Copyright (C) 2012 Jacob Walker
8  */
9 
13 #include <shogun/machine/Machine.h>
14 #include <shogun/lib/Map.h>
15 
16 using namespace shogun;
17 
18 #ifdef HAVE_NLOPT
19 
20 #include <nlopt.h>
21 
22 double CGradientModelSelection::nlopt_function(unsigned n,
23  const double *x, double *grad, void *my_func_data)
24 {
25  nlopt_package* pack = (nlopt_package*)my_func_data;
26 
27  shogun::CMachineEvaluation* m_machine_eval = pack->m_machine_eval;
28 
30  pack->m_current_combination;
31 
32  bool print_state = pack->print_state;
33 
34  /* Get result vector first to get names of parameters*/
35  shogun::CGradientResult* result =
36  (shogun::CGradientResult*)(m_machine_eval->evaluate());
37 
39  SG_SERROR("Evaluation result not a GradientEvaluationResult!");
40 
41  if ((unsigned)result->total_variables != n)
42  {
43  SG_SERROR("Mismatch between total variables in result and variables in " \
44  "NLOPT!\n");
45  }
46 
47  shogun::CMachine* machine = m_machine_eval->get_machine();
48 
49  if (print_state)
50  result->print_result();
51 
52  index_t curr_index = 0;
53 
54  /*Set parameter values from x vector*/
55  for (index_t i = 0; i < result->gradient.get_num_elements(); i++)
56  {
57  shogun::CMapNode<TParameter*, SGVector<float64_t> >* node =
58  result->gradient.get_node_ptr(i);
59 
60  TParameter* param = node->key;
61 
62  CSGObject* parent = result->parameter_dictionary.get_element(param);
63 
64  if (param->m_datatype.m_ctype == CT_VECTOR)
65  {
66  if (!param->m_datatype.m_length_y)
67  SG_SERROR("Parameter vector %s has no length\n", param->m_name);
68 
69  index_t length = *(param->m_datatype.m_length_y);
70 
71  for (index_t j = 0; j < length; j++)
72  {
73  if (!parent || !m_current_combination->set_parameter(
74  param->m_name, (float64_t)x[curr_index+j], parent, j))
75  {
76  SG_SERROR("Parameter %s not found in combination \
77  tree.\n",
78  param->m_name);
79  }
80  }
81 
82  curr_index += length;
83  }
84 
85  else if (param->m_datatype.m_ctype == CT_SGVECTOR)
86  {
87  if (!param->m_datatype.m_length_y)
88  SG_SERROR("Parameter vector %s has no length\n", param->m_name);
89 
90  index_t length = *(param->m_datatype.m_length_y);
91 
92  for (index_t j = 0; j < length; j++)
93  {
94  if (!parent || !m_current_combination->set_parameter(
95  param->m_name, (float64_t)x[curr_index+j], parent, j))
96  {
97  SG_SERROR("Parameter %s not found in combination \
98  tree.\n",
99  param->m_name);
100  }
101  }
102  curr_index += length;
103  }
104 
105  else
106  {
107  if (!parent || !m_current_combination->set_parameter(
108  param->m_name, (float64_t)x[curr_index], parent))
109  {
110  SG_SERROR("Parameter %s not found in combination tree.\n",
111  param->m_name);
112  }
113  curr_index++;
114  }
115  }
116 
117  /*Apply them to the machine*/
118  m_current_combination->apply_to_modsel_parameter(
120 
121  /*Get rid of this first result*/
122  SG_UNREF(result);
123 
124  /*Get a result based on updated parameter values*/
125  result = (shogun::CGradientResult*)(m_machine_eval->evaluate());
126 
128  SG_SERROR("Evaluation result not a GradientEvaluationResult!");
129 
130  curr_index = 0;
131 
132  /*Store the gradient into the grad vector*/
133  for (index_t i = 0; i < result->gradient.get_num_elements(); i++)
134  {
135  shogun::CMapNode<TParameter*, SGVector<float64_t> >* node =
136  result->gradient.get_node_ptr(i);
137 
138  for(index_t j = 0; j < node->data.vlen; j++)
139  grad[curr_index+j] = node->data[j];
140 
141  curr_index += node->data.vlen;
142  }
143 
144  /*Get function value*/
145  float64_t function_value = result->quantity[0];
146 
147  SG_UNREF(result);
148  SG_UNREF(machine);
149 
150  return function_value;
151 }
152 
153 #endif
154 
156  CModelSelectionParameters* model_parameters,
157  CMachineEvaluation* machine_eval) : CModelSelection(model_parameters,
158  machine_eval) {
159  init();
160 }
161 
162 void CGradientModelSelection::init()
163 {
164  m_max_evaluations = 1000;
165  m_grad_tolerance = 1e-4;
166  m_current_combination = NULL;
167 
168  SG_ADD((CSGObject**)&m_current_combination, "current_combination",
169  "Current Combination", MS_NOT_AVAILABLE);
170  SG_ADD(&m_grad_tolerance, "gradient_tolerance",
171  "gradient_tolerance", MS_NOT_AVAILABLE);
172  SG_ADD(&m_max_evaluations, "max_evaluations", "Max Evaluations",
174 }
175 
177  NULL)
178 {
179  init();
180 }
181 
183 {
184  SG_UNREF(m_current_combination);
185 }
186 
187 void CGradientModelSelection::test_gradients()
188 {
189 
190  float64_t delta = 0.001;
191  float64_t error_tol = 0.1;
192  float64_t orig_value, new_value;
193  float64_t orig_eval, new_eval;
194  float64_t approx_grad, true_grad;
195 
196  CMachine* machine = m_machine_eval->get_machine();
197 
198  m_current_combination->apply_to_modsel_parameter(
200 
201  CGradientResult* result = (CGradientResult*)(m_machine_eval->evaluate());
202 
203  /*Set parameter values from x vector*/
204  for (index_t i = 0; i < result->gradient.get_num_elements(); i++)
205  {
206  shogun::CMapNode<TParameter*, SGVector<float64_t> >* node =
207  result->gradient.get_node_ptr(i);
208 
209  orig_eval = result->quantity[0];
210 
211  TParameter* param = node->key;
212 
213  for (index_t j = 0; j < node->data.vlen; j++)
214  {
215  true_grad = node->data[j];
216 
217  index_t index = -1;
218 
219  if (param->m_datatype.m_ctype == CT_VECTOR ||
220  param->m_datatype.m_ctype == CT_SGVECTOR)
221  {
222  index = j;
223  orig_value = (*((float64_t**)param->m_parameter))[j];
224  }
225 
226  else
227  orig_value = *((float64_t*)param->m_parameter);
228 
229  new_value = orig_value+delta;
230 
231  CSGObject* parent = result->parameter_dictionary.get_element(param);
232 
233 
234  if (!parent || !m_current_combination->set_parameter(
235  param->m_name, new_value, parent, index))
236  {
237  SG_ERROR("Parameter %s not found in combination tree.\n",
238  param->m_name);
239  }
240 
241  m_current_combination->apply_to_modsel_parameter(
243 
244  CGradientResult* new_result =
245  (CGradientResult*)(m_machine_eval->evaluate());
246 
247  new_eval = new_result->quantity[0];
248 
249  approx_grad = (new_eval-orig_eval)/delta;
250 
251  if (abs(approx_grad - true_grad) > error_tol)
252  {
253  SG_ERROR("Gradient of function with respect to variable %i of %s" \
254  " is incorrect.\n" \
255  "True value is approximately %f, but calculated value is" \
256  " %f\n", j, param->m_name,
257  approx_grad, true_grad);
258  }
259 
260  if (!parent || !m_current_combination->set_parameter(
261  param->m_name, orig_value, parent, index))
262  {
263  SG_ERROR("Parameter %s not found in combination tree.\n",
264  param->m_name);
265  }
266 
267  m_current_combination->apply_to_modsel_parameter(
269 
270  SG_UNREF(new_result);
271  }
272  }
273 
274  SG_UNREF(machine);
275  SG_UNREF(result);
276 }
277 
279 {
280 
281 #ifdef HAVE_NLOPT
282 
283  //Get a random initial combination
284  SG_UNREF(m_current_combination);
285  m_current_combination = m_model_parameters->get_single_combination();
286  SG_REF(m_current_combination);
287 
288  CMachine* machine = m_machine_eval->get_machine();
289 
290  if (print_state)
291  {
292  SG_PRINT("trying combination:\n");
293  m_current_combination->print_tree();
294  }
295 
296  m_current_combination->apply_to_modsel_parameter(
298 
299  /*How many of these parameters have derivatives?*/
300  CGradientResult* result = (CGradientResult*)(m_machine_eval->evaluate());
301 
303  SG_ERROR("Evaluation result not a GradientEvaluationResult!");
304 
305  index_t n = result->total_variables;
306 
307  double* lb = SG_MALLOC(double, n);
308  double* x = SG_MALLOC(double, n);
309 
310  index_t cur_index = 0;
311 
312  //Update x with initial values
313  for (index_t i = 0; i < result->gradient.get_num_elements(); i++)
314  {
315  shogun::CMapNode<TParameter*, SGVector<float64_t> >* node =
316  result->gradient.get_node_ptr(i);
317 
318  TParameter* param = node->key;
319 
320  CSGObject* parent = result->parameter_dictionary.get_element(param);
321 
322  TParameter* final = m_current_combination->get_parameter(
323  param->m_name, parent);
324 
325  if (!final)
326  {
327  SG_ERROR("Could not find parameter %s "\
328  "in Parameter Combination\n", param->m_name);
329  }
330 
331  if (final->m_datatype.m_ctype == CT_VECTOR)
332  {
333  if (!param->m_datatype.m_length_y)
334  SG_ERROR("Parameter vector %s has no length\n", param->m_name);
335 
336  index_t length = *(final->m_datatype.m_length_y);
337 
338  for (index_t j = 0; j < length; j++)
339  x[cur_index+j] = *((float64_t**)(final->m_parameter))[j];
340 
341  cur_index += length;
342  }
343 
344  else if (final->m_datatype.m_ctype == CT_SGVECTOR)
345  {
346  if (!param->m_datatype.m_length_y)
347  SG_ERROR("Parameter vector %s has no length\n", param->m_name);
348 
349  index_t length = *(final->m_datatype.m_length_y);
350 
351  for (index_t j = 0; j < length; j++)
352  x[cur_index+j] = (*(float64_t**)(final->m_parameter))[j];
353 
354  cur_index += length;
355  }
356 
357  else
358  {
359  x[cur_index] = *((float64_t*)(final->m_parameter));
360  cur_index++;
361  }
362 
363  }
364 
365  cur_index = 0;
366 
367  CParameterCombination* lower_combination =
369 
370  //Update x with initial values
371  for (index_t i = 0; i < result->gradient.get_num_elements(); i++)
372  {
373  shogun::CMapNode<TParameter*, SGVector<float64_t> >* node =
374  result->gradient.get_node_ptr(i);
375 
376  TParameter* param = node->key;
377 
378  CSGObject* parent = result->parameter_dictionary.get_element(param);
379 
380  TParameter* final = lower_combination->get_parameter(
381  param->m_name, parent);
382 
383  if (!final)
384  {
385  SG_ERROR("Could not find parameter %s "\
386  "in Parameter Combination\n", param->m_name);
387  }
388 
389  if (final->m_datatype.m_ctype == CT_VECTOR)
390  {
391  if (!param->m_datatype.m_length_y)
392  SG_ERROR("Parameter vector %s has no length\n", param->m_name);
393 
394  index_t length = *(final->m_datatype.m_length_y);
395 
396  for (index_t j = 0; j < length; j++)
397  lb[cur_index+j] = *((float64_t**)(final->m_parameter))[j];
398 
399  cur_index += length;
400  }
401 
402  else if (final->m_datatype.m_ctype == CT_SGVECTOR)
403  {
404  if (!param->m_datatype.m_length_y)
405  SG_ERROR("Parameter vector %s has no length\n", param->m_name);
406 
407  index_t length = *(final->m_datatype.m_length_y);
408 
409  for (index_t j = 0; j < length; j++)
410  lb[cur_index+j] = (*(float64_t**)(final->m_parameter))[j];
411 
412  cur_index += length;
413  }
414 
415  else
416  {
417  lb[cur_index] = *((float64_t*)(final->m_parameter));
418  cur_index++;
419  }
420 
421  }
422 
423  //Setting up nlopt
424  nlopt_opt opt;
425 
426  nlopt_package pack;
427 
430  pack.print_state = print_state;
431 
432  opt = nlopt_create(NLOPT_LD_MMA, n); // algorithm and dimensionality
433  nlopt_set_maxeval(opt, m_max_evaluations);
434  nlopt_set_xtol_rel(opt, m_grad_tolerance);
435  nlopt_set_lower_bounds(opt, lb);
436 
437  if (m_machine_eval->get_evaluation_direction() == ED_MINIMIZE)
438  {
439  if (print_state)
440  SG_SPRINT("Minimizing Objective Function\n");
441 
442  nlopt_set_min_objective(opt, nlopt_function, &pack);
443  }
444 
445  else
446  {
447  if (print_state)
448  SG_SPRINT("Maximizing Objective Function\n");
449 
450  nlopt_set_max_objective(opt, nlopt_function, &pack);
451  }
452 
453  double minf; //the minimum objective value, upon return
454 
455 // test_gradients();
456 
457  //Optimize our function!
458  if (nlopt_optimize(opt, x, &minf) < 0)
459  SG_ERROR("nlopt failed!\n");
460 
461 // test_gradients();
462 
463  //Clean up.
464  SG_FREE(lb);
465  SG_FREE(x);
466  nlopt_destroy(opt);
467 
468  //Admittedly weird, but I am unreferencing
469  //m_current_combination from this stack and
470  //passing it on to another.
471  SG_UNREF(machine);
472  SG_UNREF(result);
473  SG_UNREF(lower_combination);
474 
475  SG_REF(m_current_combination);
476 
477  return m_current_combination;
478 
479 #endif
480 
481  //If we don't have NLOPT then return nothing.
482  SG_PRINT("Shogun not configured for NLOPT. Returning NULL combination\n");
483 
484  return NULL;
485 }

SHOGUN Machine Learning Toolbox - Documentation