PLearn 0.1
LearnerCommand.cc
Go to the documentation of this file.
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 :
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines