PLearn 0.1
|
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 :