SHOGUN  v3.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  * Written (W) 2013 Roman Votyakov
8  * Copyright (C) 2012 Jacob Walker
9  */
10 
12 
13 #ifdef HAVE_NLOPT
14 
18 #include <shogun/machine/Machine.h>
19 #include <nlopt.h>
20 
21 using namespace shogun;
22 
23 #ifndef DOXYGEN_SHOULD_SKIP_THIS
24 
26 struct nlopt_params
27 {
29  CMachineEvaluation* machine_eval;
30 
32  CParameterCombination* current_combination;
33 
35  CMap<TParameter*, CSGObject*>* parameter_dictionary;
36 
38  bool print_state;
39 };
40 
51 double nlopt_function(unsigned n, const double* x, double* grad, void* func_data)
52 {
53  nlopt_params* params=(nlopt_params*)func_data;
54 
55  CMachineEvaluation* machine_eval=params->machine_eval;
56  CParameterCombination* current_combination=params->current_combination;
57  CMap<TParameter*, CSGObject*>* parameter_dictionary=params->parameter_dictionary;
58  bool print_state=params->print_state;
59 
60  index_t offset=0;
61 
62  // set parameters from vector x
63  for (index_t i=0; i<parameter_dictionary->get_num_elements(); i++)
64  {
65  CMapNode<TParameter*, CSGObject*>* node=parameter_dictionary->get_node_ptr(i);
66 
67  TParameter* param=node->key;
68  CSGObject* parent=node->data;
69 
70  if (param->m_datatype.m_ctype==CT_VECTOR ||
71  param->m_datatype.m_ctype==CT_SGVECTOR)
72  {
73  REQUIRE(param->m_datatype.m_length_y, "Parameter vector %s has no "
74  "length\n", param->m_name)
75 
76  for (index_t j=0; j<*(param->m_datatype.m_length_y); j++)
77  {
78 
79  bool result=current_combination->set_parameter(param->m_name,
80  (float64_t)x[offset++], parent, j);
81  REQUIRE(result, "Parameter %s not found in combination tree\n",
82  param->m_name)
83  }
84  }
85  else
86  {
87  bool result=current_combination->set_parameter(param->m_name,
88  (float64_t)x[offset++], parent);
89  REQUIRE(result, "Parameter %s not found in combination tree\n",
90  param->m_name)
91  }
92  }
93 
94  // apply current combination to the machine
95  CMachine* machine=machine_eval->get_machine();
96  current_combination->apply_to_machine(machine);
97  SG_UNREF(machine);
98 
99  // evaluate the machine
100  CEvaluationResult* evaluation_result=machine_eval->evaluate();
102  evaluation_result);
103  SG_UNREF(evaluation_result);
104 
105  if (print_state)
106  {
107  gradient_result->print_result();
108  }
109 
110  // get value of the function, gradients and parameter dictionary
111  SGVector<float64_t> value=gradient_result->get_value();
112  CMap<TParameter*, SGVector<float64_t> >* gradient=gradient_result->get_gradient();
113  CMap<TParameter*, CSGObject*>* gradient_dictionary=
114  gradient_result->get_paramter_dictionary();
115  SG_UNREF(gradient_result);
116 
117  offset=0;
118 
119  // set derivative for each parameter from parameter dictionary
120  for (index_t i=0; i<parameter_dictionary->get_num_elements(); i++)
121  {
122  CMapNode<TParameter*, CSGObject*>* node=parameter_dictionary->get_node_ptr(i);
123 
124  SGVector<float64_t> derivative;
125 
126  for (index_t j=0; j<gradient_dictionary->get_num_elements(); j++)
127  {
128  CMapNode<TParameter*, CSGObject*>* gradient_node=
129  gradient_dictionary->get_node_ptr(j);
130 
131  if (gradient_node->data==node->data &&
132  !strcmp(gradient_node->key->m_name, node->key->m_name))
133  {
134  derivative=gradient->get_element(gradient_node->key);
135  }
136  }
137 
138  REQUIRE(derivative.vlen, "Can't find gradient wrt %s parameter!\n",
139  node->key->m_name);
140 
141  memcpy(grad+offset, derivative.vector, sizeof(double)*derivative.vlen);
142 
143  offset+=derivative.vlen;
144  }
145 
146  SG_UNREF(gradient);
147  SG_UNREF(gradient_dictionary);
148 
149  return (double)(SGVector<float64_t>::sum(value));
150 }
151 
152 #endif /* DOXYGEN_SHOULD_SKIP_THIS */
153 
155 {
156  init();
157 }
158 
160  CModelSelectionParameters* model_parameters)
161  : CModelSelection(machine_eval, model_parameters)
162 {
163  init();
164 }
165 
167 {
168 }
169 
170 void CGradientModelSelection::init()
171 {
172  m_max_evaluations=1000;
173  m_grad_tolerance=1e-6;
174 
175  SG_ADD(&m_grad_tolerance, "gradient_tolerance", "Gradient tolerance",
177  SG_ADD(&m_max_evaluations, "max_evaluations", "Maximum number of evaluations",
179 }
180 
182 {
183  if (!m_model_parameters)
184  {
185  CMachine* machine=m_machine_eval->get_machine();
186 
187  CParameterCombination* current_combination=new CParameterCombination(machine);
188  SG_REF(current_combination);
189 
190  if (print_state)
191  {
192  SG_PRINT("Initial combination:\n");
193  current_combination->print_tree();
194  }
195 
196  // get total length of variables
197  index_t total_variables=current_combination->get_parameters_length();
198 
199  // build parameter->value map
202  current_combination->build_parameter_values_map(argument);
203 
204  // unroll current parameter combination into vector
205  SGVector<double> x(total_variables);
206  index_t offset=0;
207 
208  for (index_t i=0; i<argument->get_num_elements(); i++)
209  {
210  CMapNode<TParameter*, SGVector<float64_t> >* node=argument->get_node_ptr(i);
211  memcpy(x.vector+offset, node->data.vector, sizeof(double)*node->data.vlen);
212  offset+=node->data.vlen;
213  }
214 
215  SG_UNREF(argument);
216 
217  // create nlopt object and choose MMA (Method of Moving Asymptotes)
218  // optimization algorithm
219  nlopt_opt opt=nlopt_create(NLOPT_LD_MMA, total_variables);
220 
221  // create lower bound vector (lb=-inf)
222  SGVector<double> lower_bound(total_variables);
223  lower_bound.set_const(1e-6);
224 
225  // create upper bound vector (ub=inf)
226  SGVector<double> upper_bound(total_variables);
227  upper_bound.set_const(HUGE_VAL);
228 
229  // set upper and lower bound
230  nlopt_set_lower_bounds(opt, lower_bound.vector);
231  nlopt_set_upper_bounds(opt, upper_bound.vector);
232 
233  // set maximum number of evaluations
234  nlopt_set_maxeval(opt, m_max_evaluations);
235 
236  // set absolute argument tolearance
237  nlopt_set_xtol_abs1(opt, m_grad_tolerance);
238  nlopt_set_ftol_abs(opt, m_grad_tolerance);
239 
240  // build parameter->sgobject map from current parameter combination
241  CMap<TParameter*, CSGObject*>* parameter_dictionary=
243  current_combination->build_parameter_parent_map(parameter_dictionary);
244 
245  // nlopt parameters
246  nlopt_params params;
247 
248  params.current_combination=current_combination;
249  params.machine_eval=m_machine_eval;
250  params.print_state=print_state;
251  params.parameter_dictionary=parameter_dictionary;
252 
253  // choose evaluation direction (minimize or maximize objective function)
255  {
256  if (print_state)
257  SG_PRINT("Minimizing objective function:\n");
258 
259  nlopt_set_min_objective(opt, nlopt_function, &params);
260  }
261  else
262  {
263  if (print_state)
264  SG_PRINT("Maximizing objective function:\n");
265 
266  nlopt_set_max_objective(opt, nlopt_function, &params);
267  }
268 
269  // the minimum objective value, upon return
270  double minf;
271 
272  // optimize our function
273  nlopt_result result=nlopt_optimize(opt, x.vector, &minf);
274 
275  REQUIRE(result>0, "NLopt failed while optimizing objective function!\n");
276 
277  if (print_state)
278  {
279  SG_PRINT("Best combination:\n");
280  current_combination->print_tree();
281  }
282 
283  // clean up
284  nlopt_destroy(opt);
285  SG_UNREF(machine);
286  SG_UNREF(parameter_dictionary);
287 
288  return current_combination;
289  }
290  else
291  {
293  return NULL;
294  }
295 }
296 
297 #endif /* HAVE_NLOPT */

SHOGUN Machine Learning Toolbox - Documentation