PLearn 0.1
|
00001 // -*- C++ -*- 00002 00003 // LearnerCommand.cc 00004 // 00005 // Copyright (C) 2004 Pascal Vincent 00006 // 00007 // Redistribution and use in source and binary forms, with or without 00008 // modification, are permitted provided that the following conditions are met: 00009 // 00010 // 1. Redistributions of source code must retain the above copyright 00011 // notice, this list of conditions and the following disclaimer. 00012 // 00013 // 2. Redistributions in binary form must reproduce the above copyright 00014 // notice, this list of conditions and the following disclaimer in the 00015 // documentation and/or other materials provided with the distribution. 00016 // 00017 // 3. The name of the authors may not be used to endorse or promote 00018 // products derived from this software without specific prior written 00019 // permission. 00020 // 00021 // THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 00022 // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 00023 // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 00024 // NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 00025 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 00026 // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 00027 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 00028 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 00029 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 00030 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00031 // 00032 // This file is part of the PLearn library. For more information on the PLearn 00033 // library, go to the PLearn Web site at www.plearn.org 00034 00035 /* ******************************************************* 00036 * $Id: LearnerCommand.cc 9751 2008-12-08 19:15:15Z nouiz $ 00037 ******************************************************* */ 00038 00039 // Authors: Pascal Vincent 00040 00044 #include "LearnerCommand.h" 00045 #include <plearn_learners/generic/PLearner.h> 00046 #include <plearn/vmat/FileVMatrix.h> 00047 #include <plearn/db/getDataSet.h> 00048 #include <plearn/io/load_and_save.h> 00049 #include <plearn/io/openString.h> 00050 #include <plearn/io/PyPLearnScript.h> 00051 #include <plearn/base/lexical_cast.h> 00052 00053 namespace PLearn { 00054 using namespace std; 00055 00057 PLearnCommandRegistry LearnerCommand::reg_(new LearnerCommand); 00058 00059 LearnerCommand::LearnerCommand(): 00060 PLearnCommand("learner", 00061 00062 "Allows to train, use and test a learner", 00063 00064 "The following forms of the learner command are allowed:\n" 00065 "\n" 00066 "learner train <learner_spec.plearn> <trainset.vmat> <trained_learner.psave> [no_forget]\n" 00067 " - Will train the specified learner on the specified trainset and save the resulting trained learner as\n" 00068 " trained_learner.psave. If the optional keyword argument 'no_forget' is provided, then the learner will\n" 00069 " not be reset by calling forget before training.\n" 00070 "\n" 00071 "learner test <trained_learner.psave> <testset.vmat> <cost.stats> [<outputs.pmat> [<costs.pmat>]] [--set_testset_as_trainingset]\n" 00072 " - Tests the specified learner on the testset. Will produce a cost.stats file (viewable with the plearn stats\n" 00073 " command) and optionally saves individual outputs and costs\n" 00074 "\n" 00075 "learner compute_outputs <trained_learner.psave> <test_inputs.vmat> <outputs.pmat> (or 'learner co' as a shortcut)\n" 00076 "\n" 00077 "learner process_dataset <trained_learner.psave> <dataset.vmat> <processed_dataset.pmat>\n" 00078 " - process a full dataset (possibly containing input,target,weight,extra,parts). \n" 00079 " writes processed dataset as a pmat. This calls method processDataset whose default version \n" 00080 " uses computeOutput to process the input part, and simply passes on the other parts unchanged.\n" 00081 " Typical usage: preprocessing data with PCA for ex. \n\n" 00082 // "learner compute_costs <trained_learner.psave> <testset.vmat> <outputs.pmat> <costs.pmat>\n" 00083 "learner compute_outputs_on_1D_grid <trained_learner.psave> <gridoutputs.pmat> <xmin> <xmax> <nx> (shortcut: learner cg1)\n" 00084 " - Computes output of learner on nx equally spaced points in range [xmin, xmax] and writes the list of (x,output)\n" 00085 " in gridoutputs.pmat \n" 00086 "\n" 00087 "learner compute_outputs_on_2D_grid <trained_learner.psave> <gridoutputs.pmat> <xmin> <xmax> <ymin> <ymax> <nx> <ny> (shortcut: learner cg2)\n" 00088 " - Computes output of learner on the regular 2d grid specified and writes the list of (x,y,output) in gridoutputs.pmat\n" 00089 "\n" 00090 "learner compute_outputs_on_auto_grid <trained_learner.psave> <gridoutputs.pmat> <trainset.vmat> <nx> [<ny>] (shortcut: learner cg)\n" 00091 " - Automatically determines a bounding-box from the trainset (enlarged by 5%), and computes the output along a\n" 00092 " regular 1D grid of <nx> points or a regular 2D grid of <nx>*<ny> points. (Note: you can also invoke command vmat\n" 00093 " bbox to determine the bounding-box by yourself, and then invoke learner cg1 or learner cg2 appropriately)\n" 00094 "\n" 00095 "learner analyze_inputs <data.vmat> <results.pmat> <epsilon> <learner_1> ... <learner_n>\n" 00096 " - Analyze the influence of inputs of given learners. The output of each sample in the data VMatrix is computed\n" 00097 " when each input is perturbed, so as to estimate the derivative of the output with respect to the input. This\n" 00098 " is averaged over all samples and all learners so as to estimate the influence of each input. In the results.pmat\n" 00099 " file, are stored the average, variance, min and max of the derivative for all inputs (and outputs).\n" 00100 "\n" 00101 "The datasets do not need to be .vmat they can be any valid vmatrix (.amat .pmat .dmat)" 00102 ) 00103 {} 00104 00106 // train // 00108 void LearnerCommand::train(const PPath& learner_spec_file, 00109 const PPath& trainset_spec, 00110 const PPath& save_learner_file, 00111 bool no_forget) 00112 { 00113 PP<PLearner> learner; 00114 string learner_spec = readFileAndMacroProcess(learner_spec_file); 00115 PStream in = openString(learner_spec, PStream::plearn_ascii); 00116 in >> learner; 00117 if (learner.isNull()) 00118 PLERROR("This file cannot be correctly parsed (syntax error?): %s", 00119 learner_spec_file.absolute().c_str()); 00120 VMat trainset = getDataSet(trainset_spec); 00121 PP<VecStatsCollector> train_stats = new VecStatsCollector(); 00122 learner->setTrainStatsCollector(train_stats); 00123 learner->setTrainingSet(trainset, !no_forget); 00124 learner->train(); 00125 PLearn::save(save_learner_file, learner); 00126 } 00127 00129 // test // 00131 void LearnerCommand::test(const string& trained_learner_file, const string& testset_spec, const string& stats_file, const string& outputs_file, const string& costs_file, const bool set_testset_as_trainingset) 00132 { 00133 PP<PLearner> learner = 00134 (PLearner*) smartLoadObject(trained_learner_file); 00135 VMat testset = getDataSet(testset_spec); 00136 int l = testset.length(); 00137 VMat testoutputs; 00138 if(outputs_file!="") 00139 testoutputs = new FileVMatrix(outputs_file,l,learner->outputsize()); 00140 VMat testcosts; 00141 if(set_testset_as_trainingset){ 00142 learner->setTrainingSet(testset, false); 00143 learner->finalize(); 00144 } 00145 if(costs_file!="") 00146 testcosts = new FileVMatrix(costs_file,l,learner->getTestCostNames()); 00147 00148 PP<VecStatsCollector> test_stats = new VecStatsCollector; 00149 test_stats->build(); 00150 test_stats->forget(); 00151 learner->test(testset, test_stats, testoutputs, testcosts); 00152 test_stats->finalize(); 00153 00154 PLearn::save(stats_file,test_stats); 00155 } 00156 00158 // compute_outputs // 00160 void LearnerCommand::compute_outputs(const string& trained_learner_file, const string& test_inputs_spec, const string& outputs_file) 00161 { 00162 PP<PLearner> learner = 00163 (PLearner*) smartLoadObject(trained_learner_file); 00164 VMat testinputs = getDataSet(test_inputs_spec); 00165 int l = testinputs.length(); 00166 VMat testoutputs = new FileVMatrix(outputs_file,l,learner->getOutputNames()); 00167 learner->use(testinputs,testoutputs); 00168 } 00169 00171 // process_dataset // 00173 void LearnerCommand::process_dataset(const string& trained_learner_file, const string& dataset_spec, const string& processed_dataset_pmat) 00174 { 00175 PP<PLearner> learner = 00176 (PLearner*) smartLoadObject(trained_learner_file); 00177 VMat dataset = getDataSet(dataset_spec); 00178 VMat processed = learner->processDataSet(dataset); 00179 processed->savePMAT(processed_dataset_pmat); 00180 } 00181 00183 // compute_outputs_on_2D_grid // 00185 void LearnerCommand::compute_outputs_on_2D_grid(const string& trained_learner_file, const string& grid_outputs_file, 00186 real xmin, real xmax, real ymin, real ymax, 00187 int nx, int ny) 00188 { 00189 if(nx<2 || ny<2) 00190 PLERROR("In LearnerCommand::compute_outputs_on_2D_grid invalid nx or ny. Must have at least a 2x2 grid"); 00191 PP<PLearner> learner; 00192 PLearn::load(trained_learner_file,learner); 00193 if(learner->inputsize()!=2) 00194 PLERROR("In LearnerCommand::compute_outputs_on_2D_grid learner must have inputsize==2 (it's %d)",learner->inputsize()); 00195 int outputsize = learner->outputsize(); 00196 VMat gridoutputs = new FileVMatrix(grid_outputs_file,0,2+outputsize); 00197 real deltax = (xmax-xmin)/(nx-1); 00198 real deltay = (ymax-ymin)/(ny-1); 00199 00200 Vec v(2+outputsize); 00201 Vec input = v.subVec(0,2); 00202 Vec output = v.subVec(2,outputsize); 00203 00204 real outputsum = 0; 00205 00206 real x = xmin; 00207 for(int i=0; i<nx; i++, x+=deltax) 00208 { 00209 input[0] = x; 00210 real y = ymin; 00211 for(int j=0; j<ny; j++, y+=deltay) 00212 { 00213 input[1] = y; 00214 learner->computeOutput(input,output); 00215 outputsum += output[0]; 00216 gridoutputs->appendRow(v); 00217 } 00218 } 00219 00220 cerr << "integral: " << outputsum*deltax*deltay << endl; 00221 00222 } 00223 00225 // compute_outputs_on_1D_grid // 00227 void LearnerCommand::compute_outputs_on_1D_grid(const string& trained_learner_file, const string& grid_outputs_file, 00228 real xmin, real xmax, int nx) 00229 { 00230 if(nx<2) 00231 PLERROR("In LearnerCommand::compute_outputs_on_1D_grid invalid nx. Must be at least 2"); 00232 PP<PLearner> learner; 00233 PLearn::load(trained_learner_file,learner); 00234 if(learner->inputsize()!=1) 00235 PLERROR("In LearnerCommand::compute_outputs_on_1D_grid learner must have inputsize==1 (it's %d)",learner->inputsize()); 00236 int outputsize = learner->outputsize(); 00237 VMat gridoutputs = new FileVMatrix(grid_outputs_file,0,1+outputsize); 00238 real deltax = (xmax-xmin)/(nx-1); 00239 00240 Vec v(1+outputsize); 00241 Vec input = v.subVec(0,1); 00242 Vec output = v.subVec(1,outputsize); 00243 00244 real x=xmin; 00245 for(int i=0; i<nx; i++, x+=deltax) 00246 { 00247 input[0] = x; 00248 learner->computeOutput(input,output); 00249 gridoutputs->appendRow(v); 00250 } 00251 } 00252 00254 // compute_outputs_on_auto_grid // 00256 void LearnerCommand::compute_outputs_on_auto_grid(const string& trained_learner_file, const string& grid_outputs_file, 00257 const string& dataset_spec, real extra_percent, 00258 int nx, int ny) 00259 { 00260 TVec< pair<real,real> > bbox = getDataSet(dataset_spec)->getBoundingBox(extra_percent); 00261 if(ny>0) 00262 compute_outputs_on_2D_grid(trained_learner_file, grid_outputs_file, 00263 bbox[0].first, bbox[0].second, bbox[1].first, bbox[1].second, 00264 nx, ny); 00265 else 00266 compute_outputs_on_1D_grid(trained_learner_file, grid_outputs_file, 00267 bbox[0].first, bbox[0].second, 00268 nx); 00269 } 00270 00272 // analyze_inputs // 00274 void LearnerCommand::analyze_inputs(const string& data_file, const string& result_file, real epsilon, const TVec<string>& learner_files) { 00275 // Load dataset and learners. 00276 cout << "Loading dataset and learners" << endl; 00277 VMat data = getDataSet(data_file); 00278 int dim = data->inputsize(); 00279 if (dim <= 0) 00280 PLERROR("In LearnerCommand::analyze_inputs - Cannot analyze inputs if the data's inputsize is not set"); 00281 int n_learners = learner_files.length(); 00282 TVec< PP<PLearner> > learners(n_learners); 00283 for (int i = 0; i < learner_files.length(); i++) 00284 PLearn::load(learner_files[i], learners[i]); 00285 int n_outputs = learners[0]->outputsize(); 00286 // Analyze inputs. 00287 PP<ProgressBar> pb = new ProgressBar("Analyzing inputs", data->length()); 00288 Vec v(dim); 00289 Vec w(dim); 00290 Mat outputs(n_learners, n_outputs); 00291 Vec new_output(n_outputs); 00292 Vec dummy_target; 00293 real dummy_weight; 00294 Vec deriv(n_outputs); 00295 TVec<string> stats; 00296 stats.append("E"); 00297 stats.append("V"); 00298 stats.append("MIN"); 00299 stats.append("MAX"); 00300 int n_stats = stats.length(); // Number of statistics computed for each input and output. 00301 VMat results = new FileVMatrix(result_file, data->inputsize(), n_stats * n_outputs + 1); 00302 TVec<VecStatsCollector> statscol(dim); 00303 Vec output_k; 00304 for (int i = 0; i < data->length(); pb->update(++i)) { 00305 data->getExample(i, v, dummy_target, dummy_weight); 00306 w << v; 00307 for (int k = 0; k < n_learners; k++) { 00308 output_k = outputs(k); 00309 learners[k]->computeOutput(w, output_k); 00310 } 00311 for (int j = 0; j < dim; j++) { 00312 // Analyze j-th input. 00313 w[j] += epsilon; 00314 for (int k = 0; k < n_learners; k++) { 00315 learners[k]->computeOutput(w, new_output); 00316 // Compute the derivative of the m-th output with respect to the j-th input. 00317 for (int m = 0; m < n_outputs; m++) 00318 deriv[m] = (new_output[m] - outputs(k,m)) / epsilon; 00319 statscol[j].update(deriv); 00320 } 00321 w[j] = v[j]; 00322 } 00323 } 00324 // Compiling stats. 00325 pb = new ProgressBar("Compiling statistics", dim); 00326 for (int j = 0; j < dim; pb->update(++j)) { 00327 statscol[j].finalize(); 00328 Vec all(1 + n_outputs * n_stats); 00329 for (int i = 0; i < n_stats; i++) 00330 all.subVec(1 + i * n_outputs, n_outputs) << statscol[j].getAllStats(stats[i]); 00331 all[0] = j; 00332 results->putRow(j, all); 00333 results->addStringMapping(0, data->fieldName(j), j); 00334 } 00335 TVec<string> fieldnames; 00336 fieldnames.append("Field"); 00337 for (int i = 0; i < n_stats; i++) 00338 for (int j = 0; j < n_outputs; j++) 00339 fieldnames.append(stats[i]); 00340 results->declareFieldNames(fieldnames); 00341 results->saveAllStringMappings(); 00342 } 00343 00345 // run // 00348 void LearnerCommand::run(const vector<string>& args) 00349 { 00350 string command = args[0]; 00351 if(command=="train") 00352 { 00353 if (args.size()==4 || args.size() == 5) 00354 train(args[1], args[2], args[3], 00355 args.size() == 5 && args[4] == "no_forget"); 00356 else 00357 PLERROR("LearnerCommand::run you must provide 'plearn learner " 00358 "train learner_spec_file trainset_spec save_learner_file " 00359 "[no_forget]'"); 00360 } 00361 else if(command=="test") 00362 { 00363 if (args.size()>3) 00364 { 00365 string trained_learner_file = args[1]; 00366 string testset_spec = args[2]; 00367 string stats_basename = args[3]; 00368 string outputs_file; 00369 bool set_testset_as_trainingset = false; 00370 if(args.size()>4){ 00371 if(args[4]=="--set_testset_as_trainingset") 00372 set_testset_as_trainingset = true; 00373 else 00374 outputs_file = args[4]; 00375 } 00376 string costs_file; 00377 if(args.size()>5){ 00378 if(args[5]=="--set_testset_as_trainingset") 00379 set_testset_as_trainingset = true; 00380 else 00381 costs_file = args[5]; 00382 } 00383 if(args.size()>6){ 00384 PLCHECK(args[6]=="--set_testset_as_trainingset"); 00385 set_testset_as_trainingset = true; 00386 } 00387 test(trained_learner_file, testset_spec, stats_basename, outputs_file, costs_file, 00388 set_testset_as_trainingset); 00389 } 00390 else 00391 PLERROR("LearnerCommand::run you must provide at least 'plearn learner test <trained_learner.psave> <testset.vmat> <cost.stats>'"); 00392 } 00393 else if ((command=="compute_outputs") ||(command=="co")) 00394 { 00395 if (args.size()==4) 00396 compute_outputs(args[1],args[2],args[3]); 00397 else 00398 PLERROR("LearnerCommand::run you must provide 'plearn learner compute_outputs learner_spec_file trainset_spec save_learner_file'"); 00399 } 00400 else if (command=="process_dataset") 00401 { 00402 if (args.size()==4) 00403 process_dataset(args[1],args[2],args[3]); 00404 else 00405 PLERROR("LearnerCommand::run you must provide: plearn learner process_dataset <trained_learner.psave> <dataset.vmat> <processed_dataset.pmat>"); 00406 } 00407 else if (command=="compute_outputs_on_1D_grid" || command=="cg1") 00408 { 00409 if(args.size()!=6) 00410 PLERROR("Subcommand learner compute_outputs_on_1D_grid requires 5 arguments. Check the help!"); 00411 compute_outputs_on_1D_grid(args[1], args[2], toreal(args[3]), toreal(args[4]), toint(args[5])); 00412 } 00413 else if (command=="compute_outputs_on_2D_grid" || command=="cg2") 00414 { 00415 if(args.size()!=9) 00416 PLERROR("Subcommand learner compute_outputs_on_2D_grid requires 8 arguments. Check the help!"); 00417 compute_outputs_on_2D_grid(args[1], args[2], 00418 toreal(args[3]), toreal(args[4]), 00419 toreal(args[5]), toreal(args[6]), 00420 toint(args[7]), toint(args[8]) ); 00421 } 00422 else if (command=="compute_outputs_on_auto_grid" || command=="cg") 00423 { 00424 if(args.size()<5) 00425 PLERROR("Subcommand learner compute_outputs_on_auto_grid requires 4 or 5 arguments. Check the help!"); 00426 int nx = toint(args[4]); 00427 int ny = 0; 00428 if(args.size()==6) 00429 ny = toint(args[5]); 00430 compute_outputs_on_auto_grid(args[1], args[2], 00431 args[3], 0.05, 00432 nx, ny); 00433 } 00434 else if (command == "analyze_inputs") { 00435 if (args.size() < 5) 00436 PLERROR("In LearnerCommand::run - The 'analyze_inputs' subcommand requires 4 arguments (see help)"); 00437 real epsilon; 00438 if (!pl_isnumber(args[3], &epsilon)) 00439 PLERROR("In LearnerCommand::run - The 'epsilon' option must be a real number"); 00440 TVec<string> learners; 00441 for (size_t i = 4; i < args.size(); i++) 00442 learners.append(args[i]); 00443 analyze_inputs(args[1], args[2], epsilon, learners); 00444 } 00445 else 00446 PLERROR("Invalid command %s check the help for available commands",command.c_str()); 00447 } 00448 00449 } // end of namespace PLearn 00450 00451 00452 /* 00453 Local Variables: 00454 mode:c++ 00455 c-basic-offset:4 00456 c-file-style:"stroustrup" 00457 c-file-offsets:((innamespace . 0)(inline-open . 0)) 00458 indent-tabs-mode:nil 00459 fill-column:79 00460 End: 00461 */ 00462 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=79 :