PLearn 0.1
ChainedLearners.cc
Go to the documentation of this file.
00001 // -*- C++ -*-
00002 
00003 // ChainedLearners.cc
00004 //
00005 // Copyright (C) 2006 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 // Authors: Pascal Vincent
00036 
00040 #include "ChainedLearners.h"
00041 
00042 namespace PLearn {
00043 using namespace std;
00044 
00045 PLEARN_IMPLEMENT_OBJECT(
00046     ChainedLearners,
00047     "A learner that allows to chain several learners to perform various preprocessing steps.",
00048     "Learners in the list will be trained in sequence. After a learner_k has been trained,\n"
00049     "the training set for learner_{k+1} is obtained with learner_k->processDataSet(...) \n"
00050     "By default, this is a view of the learner_k's training set but with the inputs\n"
00051     "replaced by learner_k's computed outputs. This allows for ex. to chain a \n"
00052     "NormalizationLearner, followed by a PCA, followed by a NNet\n"
00053     "Note: StackedLearner offers a similar functionality and much more, but it is\n"
00054     "more cumbersome to use for multiple chaining. Consider using StackedLEarner if\n"
00055     "you need extra functionality, such as using a concatenation of the outputs of \n"
00056     "several learners at the same level.\n");
00057 
00058 ChainedLearners::ChainedLearners()
00059 {}
00060 
00061 void ChainedLearners::declareOptions(OptionList& ol)
00062 {
00063     declareOption(ol, "learners", &ChainedLearners::learners, 
00064                   OptionBase::buildoption,
00065                   "This is a list of learners to train in sequence.");
00066 
00067     // Now call the parent class' declareOptions
00068     inherited::declareOptions(ol);
00069 }
00070 
00071 void ChainedLearners::build_()
00072 {
00073     // ### This method should do the real building of the object,
00074     // ### according to set 'options', in *any* situation.
00075     // ### Typical situations include:
00076     // ###  - Initial building of an object from a few user-specified options
00077     // ###  - Building of a "reloaded" object: i.e. from the complete set of
00078     // ###    all serialised options.
00079     // ###  - Updating or "re-building" of an object after a few "tuning"
00080     // ###    options have been modified.
00081     // ### You should assume that the parent class' build_() has already been
00082     // ### called.
00083 }
00084 
00085 // ### Nothing to add here, simply calls build_
00086 void ChainedLearners::build()
00087 {
00088     inherited::build();
00089     build_();
00090 }
00091 
00092 
00093 void ChainedLearners::makeDeepCopyFromShallowCopy(CopiesMap& copies)
00094 {
00095     inherited::makeDeepCopyFromShallowCopy(copies);
00096     deepCopyField(learners, copies);
00097     deepCopyField(tmp_input, copies);
00098     deepCopyField(tmp_output, copies);
00099 }
00100 
00101 
00102 int ChainedLearners::outputsize() const
00103 {
00104     return learners.lastElement()->outputsize();
00105 }
00106 
00107 void ChainedLearners::forget()
00108 {
00112 
00120     for(int k=0; k<learners.length(); k++)
00121         learners[k]->forget();
00122     inherited::forget();
00123     stage = 0;
00124 }
00125 
00126 void ChainedLearners::setTrainingSet(VMat training_set, bool call_forget)
00127 {
00128     inherited::setTrainingSet(training_set, call_forget);
00129     VMat dataset = getTrainingSet();
00130     int nlearners = learners.length();
00131     for(int k=0; k<nlearners; k++)
00132     {
00133         learners[k]->setTrainingSet(dataset, call_forget);
00134         if(k<nlearners-1)
00135             dataset = learners[k]->processDataSet(dataset);            
00136     }
00137 }
00138 
00139 void ChainedLearners::train()
00140 {
00141     // The role of the train method is to bring the learner up to
00142     // stage==nstages, updating train_stats with training costs measured
00143     // on-line in the process.
00144 
00145     // This generic PLearner method does a number of standard stuff useful for
00146     // (almost) any learner, and return 'false' if no training should take
00147     // place. See PLearner.h for more details.
00148     if (!initTrain())
00149         return;
00150 
00151     VMat dataset = getTrainingSet();
00152     int nlearners = learners.length();
00153     if(stage>nstages)
00154         forget();
00155 
00156     if(stage==0)
00157     {
00158         for(int k=0; k<nlearners; k++)
00159         {
00160             learners[k]->setTrainingSet(dataset);
00161             if(k<nlearners-1)
00162             {
00163                 learners[k]->train();
00164                 dataset = learners[k]->processDataSet(dataset);            
00165             }
00166             else // last learner
00167             {
00168                 learners[k]->setTrainStatsCollector(train_stats);
00169                 train_stats->forget();
00170                 learners[k]->train();                
00171                 train_stats->finalize(); 
00172             }
00173         }
00174         ++stage;
00175     }
00176     else // stage already==1
00177     { // only call train on last learner, in case its own nstages has changed or something similar
00178         learners[nlearners-1]->train();
00179     }
00180 }
00181 
00182 void ChainedLearners::computeOutput(const Vec& input, Vec& output) const
00183 {
00184     int nlearners = learners.length();
00185     if(nlearners==1)
00186         learners[0]->computeOutput(input, output);
00187     else
00188     {
00189         tmp_output.resize(learners[0]->outputsize());
00190         learners[0]->computeOutput(input, tmp_output);
00191         for(int k=1; k<nlearners-1; k++)
00192         {
00193             int n = tmp_output.length();
00194             tmp_input.resize(n);
00195             tmp_input << tmp_output;
00196             tmp_output->resize(learners[k]->outputsize());
00197             learners[k]->computeOutput(tmp_input, tmp_output);
00198         }
00199         learners[nlearners-1]->computeOutput(tmp_output, output);
00200         
00201     }
00202 }
00203 
00204 void ChainedLearners::computeCostsFromOutputs(const Vec& input, const Vec& output,
00205                                            const Vec& target, Vec& costs) const
00206 {
00207     // this is generally called after a computeOutput, so the last learner's input 
00208     // we used was stored in tmp_output
00209     return learners.lastElement()->computeCostsFromOutputs(tmp_output, output, target, costs);
00210 }
00211 
00212 TVec<string> ChainedLearners::getTestCostNames() const
00213 {
00214     return learners.lastElement()->getTestCostNames();
00215 }
00216 
00217 TVec<string> ChainedLearners::getTrainCostNames() const
00218 {
00219     return learners.lastElement()->getTrainCostNames();
00220 }
00221 
00222 
00223 TVec<string> ChainedLearners::getOutputNames() const
00224 {
00225     return learners.lastElement()->getOutputNames();
00226 }
00227 
00228 
00229 void ChainedLearners::setExperimentDirectory(const PPath& the_expdir)
00230 {
00231     inherited::setExperimentDirectory(the_expdir);
00232     if (! the_expdir.isEmpty())
00233         for(int k= 0; k < learners.length(); ++k)
00234             learners[k]->setExperimentDirectory(the_expdir /
00235                                                 ("SubLearner_"+tostring(k)));
00236 }
00237 
00238 
00239 } // end of namespace PLearn
00240 
00241 
00242 /*
00243   Local Variables:
00244   mode:c++
00245   c-basic-offset:4
00246   c-file-style:"stroustrup"
00247   c-file-offsets:((innamespace . 0)(inline-open . 0))
00248   indent-tabs-mode:nil
00249   fill-column:79
00250   End:
00251 */
00252 // 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