00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include <shogun/modelselection/GradientModelSelection.h>
00011 #include <shogun/modelselection/ParameterCombination.h>
00012 #include <shogun/modelselection/ModelSelectionParameters.h>
00013 #include <shogun/machine/Machine.h>
00014 #include <shogun/lib/Map.h>
00015
00016 using namespace shogun;
00017
00018 #ifdef HAVE_NLOPT
00019
00020 #include <nlopt.h>
00021
00022 double CGradientModelSelection::nlopt_function(unsigned n,
00023 const double *x, double *grad, void *my_func_data)
00024 {
00025 nlopt_package* pack = (nlopt_package*)my_func_data;
00026
00027 shogun::CMachineEvaluation* m_machine_eval = pack->m_machine_eval;
00028
00029 shogun::CParameterCombination* m_current_combination =
00030 pack->m_current_combination;
00031
00032 bool print_state = pack->print_state;
00033
00034
00035 shogun::CGradientResult* result =
00036 (shogun::CGradientResult*)(m_machine_eval->evaluate());
00037
00038 if (result->get_result_type() != GRADIENTEVALUATION_RESULT)
00039 SG_SERROR("Evaluation result not a GradientEvaluationResult!");
00040
00041 if ((unsigned)result->total_variables != n)
00042 {
00043 SG_SERROR("Mismatch between total variables in result and variables in " \
00044 "NLOPT!\n");
00045 }
00046
00047 shogun::CMachine* machine = m_machine_eval->get_machine();
00048
00049 if (print_state)
00050 result->print_result();
00051
00052 index_t curr_index = 0;
00053
00054
00055 for (index_t i = 0; i < result->gradient.get_num_elements(); i++)
00056 {
00057 shogun::CMapNode<TParameter*, SGVector<float64_t> >* node =
00058 result->gradient.get_node_ptr(i);
00059
00060 TParameter* param = node->key;
00061
00062 CSGObject* parent = result->parameter_dictionary.get_element(param);
00063
00064 if (param->m_datatype.m_ctype == CT_VECTOR)
00065 {
00066 if (!param->m_datatype.m_length_y)
00067 SG_SERROR("Parameter vector %s has no length\n", param->m_name);
00068
00069 index_t length = *(param->m_datatype.m_length_y);
00070
00071 for (index_t j = 0; j < length; j++)
00072 {
00073 if (!parent || !m_current_combination->set_parameter(
00074 param->m_name, (float64_t)x[curr_index+j], parent, j))
00075 {
00076 SG_SERROR("Parameter %s not found in combination \
00077 tree.\n",
00078 param->m_name);
00079 }
00080 }
00081
00082 curr_index += length;
00083 }
00084
00085 else if (param->m_datatype.m_ctype == CT_SGVECTOR)
00086 {
00087 if (!param->m_datatype.m_length_y)
00088 SG_SERROR("Parameter vector %s has no length\n", param->m_name);
00089
00090 index_t length = *(param->m_datatype.m_length_y);
00091
00092 for (index_t j = 0; j < length; j++)
00093 {
00094 if (!parent || !m_current_combination->set_parameter(
00095 param->m_name, (float64_t)x[curr_index+j], parent, j))
00096 {
00097 SG_SERROR("Parameter %s not found in combination \
00098 tree.\n",
00099 param->m_name);
00100 }
00101 }
00102 curr_index += length;
00103 }
00104
00105 else
00106 {
00107 if (!parent || !m_current_combination->set_parameter(
00108 param->m_name, (float64_t)x[curr_index], parent))
00109 {
00110 SG_SERROR("Parameter %s not found in combination tree.\n",
00111 param->m_name);
00112 }
00113 curr_index++;
00114 }
00115 }
00116
00117
00118 m_current_combination->apply_to_modsel_parameter(
00119 machine->m_model_selection_parameters);
00120
00121
00122 SG_UNREF(result);
00123
00124
00125 result = (shogun::CGradientResult*)(m_machine_eval->evaluate());
00126
00127 if (result->get_result_type() != GRADIENTEVALUATION_RESULT)
00128 SG_SERROR("Evaluation result not a GradientEvaluationResult!");
00129
00130 curr_index = 0;
00131
00132
00133 for (index_t i = 0; i < result->gradient.get_num_elements(); i++)
00134 {
00135 shogun::CMapNode<TParameter*, SGVector<float64_t> >* node =
00136 result->gradient.get_node_ptr(i);
00137
00138 for(index_t j = 0; j < node->data.vlen; j++)
00139 grad[curr_index+j] = node->data[j];
00140
00141 curr_index += node->data.vlen;
00142 }
00143
00144
00145 float64_t function_value = result->quantity[0];
00146
00147 SG_UNREF(result);
00148 SG_UNREF(machine);
00149
00150 return function_value;
00151 }
00152
00153 #endif
00154
00155 CGradientModelSelection::CGradientModelSelection(
00156 CModelSelectionParameters* model_parameters,
00157 CMachineEvaluation* machine_eval) : CModelSelection(model_parameters,
00158 machine_eval) {
00159 init();
00160 }
00161
00162 void CGradientModelSelection::init()
00163 {
00164 m_max_evaluations = 1000;
00165 m_grad_tolerance = 1e-4;
00166 m_current_combination = NULL;
00167
00168 SG_ADD((CSGObject**)&m_current_combination, "current_combination",
00169 "Current Combination", MS_NOT_AVAILABLE);
00170 SG_ADD(&m_grad_tolerance, "gradient_tolerance",
00171 "gradient_tolerance", MS_NOT_AVAILABLE);
00172 SG_ADD(&m_max_evaluations, "max_evaluations", "Max Evaluations",
00173 MS_NOT_AVAILABLE);
00174 }
00175
00176 CGradientModelSelection::CGradientModelSelection() : CModelSelection(NULL,
00177 NULL)
00178 {
00179 init();
00180 }
00181
00182 CGradientModelSelection::~CGradientModelSelection()
00183 {
00184 SG_UNREF(m_current_combination);
00185 }
00186
00187 void CGradientModelSelection::test_gradients()
00188 {
00189
00190 float64_t delta = 0.001;
00191 float64_t error_tol = 0.1;
00192 float64_t orig_value, new_value;
00193 float64_t orig_eval, new_eval;
00194 float64_t approx_grad, true_grad;
00195
00196 CMachine* machine = m_machine_eval->get_machine();
00197
00198 m_current_combination->apply_to_modsel_parameter(
00199 machine->m_model_selection_parameters);
00200
00201 CGradientResult* result = (CGradientResult*)(m_machine_eval->evaluate());
00202
00203
00204 for (index_t i = 0; i < result->gradient.get_num_elements(); i++)
00205 {
00206 shogun::CMapNode<TParameter*, SGVector<float64_t> >* node =
00207 result->gradient.get_node_ptr(i);
00208
00209 orig_eval = result->quantity[0];
00210
00211 TParameter* param = node->key;
00212
00213 for (index_t j = 0; j < node->data.vlen; j++)
00214 {
00215 true_grad = node->data[j];
00216
00217 index_t index = -1;
00218
00219 if (param->m_datatype.m_ctype == CT_VECTOR ||
00220 param->m_datatype.m_ctype == CT_SGVECTOR)
00221 {
00222 index = j;
00223 orig_value = (*((float64_t**)param->m_parameter))[j];
00224 }
00225
00226 else
00227 orig_value = *((float64_t*)param->m_parameter);
00228
00229 new_value = orig_value+delta;
00230
00231 CSGObject* parent = result->parameter_dictionary.get_element(param);
00232
00233
00234 if (!parent || !m_current_combination->set_parameter(
00235 param->m_name, new_value, parent, index))
00236 {
00237 SG_ERROR("Parameter %s not found in combination tree.\n",
00238 param->m_name);
00239 }
00240
00241 m_current_combination->apply_to_modsel_parameter(
00242 machine->m_model_selection_parameters);
00243
00244 CGradientResult* new_result =
00245 (CGradientResult*)(m_machine_eval->evaluate());
00246
00247 new_eval = new_result->quantity[0];
00248
00249 approx_grad = (new_eval-orig_eval)/delta;
00250
00251 if (abs(approx_grad - true_grad) > error_tol)
00252 {
00253 SG_ERROR("Gradient of function with respect to variable %i of %s" \
00254 " is incorrect.\n" \
00255 "True value is approximately %f, but calculated value is" \
00256 " %f\n", j, param->m_name,
00257 approx_grad, true_grad);
00258 }
00259
00260 if (!parent || !m_current_combination->set_parameter(
00261 param->m_name, orig_value, parent, index))
00262 {
00263 SG_ERROR("Parameter %s not found in combination tree.\n",
00264 param->m_name);
00265 }
00266
00267 m_current_combination->apply_to_modsel_parameter(
00268 machine->m_model_selection_parameters);
00269
00270 SG_UNREF(new_result);
00271 }
00272 }
00273
00274 SG_UNREF(machine);
00275 SG_UNREF(result);
00276 }
00277
00278 CParameterCombination* CGradientModelSelection::select_model(bool print_state)
00279 {
00280
00281 #ifdef HAVE_NLOPT
00282
00283
00284 SG_UNREF(m_current_combination);
00285 m_current_combination = m_model_parameters->get_single_combination();
00286 SG_REF(m_current_combination);
00287
00288 CMachine* machine = m_machine_eval->get_machine();
00289
00290 if (print_state)
00291 {
00292 SG_PRINT("trying combination:\n");
00293 m_current_combination->print_tree();
00294 }
00295
00296 m_current_combination->apply_to_modsel_parameter(
00297 machine->m_model_selection_parameters);
00298
00299
00300 CGradientResult* result = (CGradientResult*)(m_machine_eval->evaluate());
00301
00302 if (result->get_result_type() != GRADIENTEVALUATION_RESULT)
00303 SG_ERROR("Evaluation result not a GradientEvaluationResult!");
00304
00305 index_t n = result->total_variables;
00306
00307 double* lb = SG_MALLOC(double, n);
00308 double* x = SG_MALLOC(double, n);
00309
00310 index_t cur_index = 0;
00311
00312
00313 for (index_t i = 0; i < result->gradient.get_num_elements(); i++)
00314 {
00315 shogun::CMapNode<TParameter*, SGVector<float64_t> >* node =
00316 result->gradient.get_node_ptr(i);
00317
00318 TParameter* param = node->key;
00319
00320 CSGObject* parent = result->parameter_dictionary.get_element(param);
00321
00322 TParameter* final = m_current_combination->get_parameter(
00323 param->m_name, parent);
00324
00325 if (!final)
00326 {
00327 SG_ERROR("Could not find parameter %s "\
00328 "in Parameter Combination\n", param->m_name);
00329 }
00330
00331 if (final->m_datatype.m_ctype == CT_VECTOR)
00332 {
00333 if (!param->m_datatype.m_length_y)
00334 SG_ERROR("Parameter vector %s has no length\n", param->m_name);
00335
00336 index_t length = *(final->m_datatype.m_length_y);
00337
00338 for (index_t j = 0; j < length; j++)
00339 x[cur_index+j] = *((float64_t**)(final->m_parameter))[j];
00340
00341 cur_index += length;
00342 }
00343
00344 else if (final->m_datatype.m_ctype == CT_SGVECTOR)
00345 {
00346 if (!param->m_datatype.m_length_y)
00347 SG_ERROR("Parameter vector %s has no length\n", param->m_name);
00348
00349 index_t length = *(final->m_datatype.m_length_y);
00350
00351 for (index_t j = 0; j < length; j++)
00352 x[cur_index+j] = (*(float64_t**)(final->m_parameter))[j];
00353
00354 cur_index += length;
00355 }
00356
00357 else
00358 {
00359 x[cur_index] = *((float64_t*)(final->m_parameter));
00360 cur_index++;
00361 }
00362
00363 }
00364
00365 cur_index = 0;
00366
00367 CParameterCombination* lower_combination =
00368 m_model_parameters->get_single_combination(false);
00369
00370
00371 for (index_t i = 0; i < result->gradient.get_num_elements(); i++)
00372 {
00373 shogun::CMapNode<TParameter*, SGVector<float64_t> >* node =
00374 result->gradient.get_node_ptr(i);
00375
00376 TParameter* param = node->key;
00377
00378 CSGObject* parent = result->parameter_dictionary.get_element(param);
00379
00380 TParameter* final = lower_combination->get_parameter(
00381 param->m_name, parent);
00382
00383 if (!final)
00384 {
00385 SG_ERROR("Could not find parameter %s "\
00386 "in Parameter Combination\n", param->m_name);
00387 }
00388
00389 if (final->m_datatype.m_ctype == CT_VECTOR)
00390 {
00391 if (!param->m_datatype.m_length_y)
00392 SG_ERROR("Parameter vector %s has no length\n", param->m_name);
00393
00394 index_t length = *(final->m_datatype.m_length_y);
00395
00396 for (index_t j = 0; j < length; j++)
00397 lb[cur_index+j] = *((float64_t**)(final->m_parameter))[j];
00398
00399 cur_index += length;
00400 }
00401
00402 else if (final->m_datatype.m_ctype == CT_SGVECTOR)
00403 {
00404 if (!param->m_datatype.m_length_y)
00405 SG_ERROR("Parameter vector %s has no length\n", param->m_name);
00406
00407 index_t length = *(final->m_datatype.m_length_y);
00408
00409 for (index_t j = 0; j < length; j++)
00410 lb[cur_index+j] = (*(float64_t**)(final->m_parameter))[j];
00411
00412 cur_index += length;
00413 }
00414
00415 else
00416 {
00417 lb[cur_index] = *((float64_t*)(final->m_parameter));
00418 cur_index++;
00419 }
00420
00421 }
00422
00423
00424 nlopt_opt opt;
00425
00426 nlopt_package pack;
00427
00428 pack.m_current_combination = m_current_combination;
00429 pack.m_machine_eval = m_machine_eval;
00430 pack.print_state = print_state;
00431
00432 opt = nlopt_create(NLOPT_LD_MMA, n);
00433 nlopt_set_maxeval(opt, m_max_evaluations);
00434 nlopt_set_xtol_rel(opt, m_grad_tolerance);
00435 nlopt_set_lower_bounds(opt, lb);
00436
00437 if (m_machine_eval->get_evaluation_direction() == ED_MINIMIZE)
00438 {
00439 if (print_state)
00440 SG_SPRINT("Minimizing Objective Function\n");
00441
00442 nlopt_set_min_objective(opt, nlopt_function, &pack);
00443 }
00444
00445 else
00446 {
00447 if (print_state)
00448 SG_SPRINT("Maximizing Objective Function\n");
00449
00450 nlopt_set_max_objective(opt, nlopt_function, &pack);
00451 }
00452
00453 double minf;
00454
00455
00456
00457
00458 if (nlopt_optimize(opt, x, &minf) < 0)
00459 SG_ERROR("nlopt failed!\n");
00460
00461
00462
00463
00464 SG_FREE(lb);
00465 SG_FREE(x);
00466 nlopt_destroy(opt);
00467
00468
00469
00470
00471 SG_UNREF(machine);
00472 SG_UNREF(result);
00473 SG_UNREF(lower_combination);
00474
00475 SG_REF(m_current_combination);
00476
00477 return m_current_combination;
00478
00479 #endif
00480
00481
00482 SG_PRINT("Shogun not configured for NLOPT. Returning NULL combination\n");
00483
00484 return NULL;
00485 }