GradientModelSelection.cpp

Go to the documentation of this file.
00001 /*
00002  * This program is free software; you can redistribute it and/or modify
00003  * it under the terms of the GNU General Public License as published by
00004  * the Free Software Foundation; either version 3 of the License, or
00005  * (at your option) any later version.
00006  *
00007  * Copyright (C) 2012 Jacob Walker
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     /* Get result vector first to get names of parameters*/
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     /*Set parameter values from x vector*/
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     /*Apply them to the machine*/
00118     m_current_combination->apply_to_modsel_parameter(
00119             machine->m_model_selection_parameters);
00120 
00121     /*Get rid of this first result*/
00122     SG_UNREF(result);
00123 
00124     /*Get a result based on updated parameter values*/
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     /*Store the gradient into the grad vector*/
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     /*Get function value*/
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     /*Set parameter values from x vector*/
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     //Get a random initial combination
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     /*How many of these parameters have derivatives?*/
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     //Update x with initial values
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     //Update x with initial values
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     //Setting up nlopt
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); // algorithm and dimensionality
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; //the minimum objective value, upon return
00454 
00455 //  test_gradients();
00456 
00457     //Optimize our function!
00458     if (nlopt_optimize(opt, x, &minf) < 0)
00459         SG_ERROR("nlopt failed!\n");
00460 
00461 //  test_gradients();
00462 
00463     //Clean up.
00464     SG_FREE(lb);
00465     SG_FREE(x);
00466     nlopt_destroy(opt);
00467 
00468     //Admittedly weird, but I am unreferencing
00469     //m_current_combination from this stack and
00470     //passing it on to another.
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     //If we don't have NLOPT then return nothing.
00482     SG_PRINT("Shogun not configured for NLOPT. Returning NULL combination\n");
00483 
00484     return NULL;
00485 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

SHOGUN Machine Learning Toolbox - Documentation