PLearn 0.1
ClassifierFromDensity.cc
Go to the documentation of this file.
00001 
00002 // -*- C++ -*-
00003 
00004 // ClassifierFromDensity.cc
00005 //
00006 // Copyright (C) 2003-2005  Pascal Vincent & Olivier Delalleau
00007 // 
00008 // Redistribution and use in source and binary forms, with or without
00009 // modification, are permitted provided that the following conditions are met:
00010 // 
00011 //  1. Redistributions of source code must retain the above copyright
00012 //     notice, this list of conditions and the following disclaimer.
00013 // 
00014 //  2. Redistributions in binary form must reproduce the above copyright
00015 //     notice, this list of conditions and the following disclaimer in the
00016 //     documentation and/or other materials provided with the distribution.
00017 // 
00018 //  3. The name of the authors may not be used to endorse or promote
00019 //     products derived from this software without specific prior written
00020 //     permission.
00021 // 
00022 // THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
00023 // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00024 // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
00025 // NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00026 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
00027 // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00028 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00029 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00030 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00031 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00032 // 
00033 // This file is part of the PLearn library. For more information on the PLearn
00034 // library, go to the PLearn Web site at www.plearn.org
00035 
00036 /* *******************************************************      
00037  * $Id: ClassifierFromDensity.cc 6639 2007-02-08 20:31:30Z tihocan $ 
00038  ******************************************************* */
00039 
00041 #include "ClassifierFromDensity.h"
00042 #include <plearn/base/tostring.h>
00043 #include <plearn/io/PPath.h>
00044 #include <plearn/ker/ClassErrorCostFunction.h>
00045 #include <plearn/ker/Kernel.h>
00046 #include <plearn/ker/NegLogProbCostFunction.h>
00047 #include <plearn/vmat/ConcatColumnsVMatrix.h>
00048 #include <plearn/vmat/VMat_operations.h>
00049 
00050 namespace PLearn {
00051 using namespace std;
00052 
00053 ClassifierFromDensity::ClassifierFromDensity() 
00054     : nclasses(-1),
00055       output_log_probabilities(false),
00056       normalize_probabilities(true),
00057       using_template_estimator(false)
00058 {}
00059 
00060 PLEARN_IMPLEMENT_OBJECT(
00061     ClassifierFromDensity,
00062     "A classifier built from density estimators using Bayes' rule.", 
00063     "ClassifierFromDensity allows to build a classifier\n"
00064     "by building one density estimator for each class, \n"
00065     "and using Bayes rule to combine them. It is assumed that the target\n"
00066     "variable in the training set represents the class number, coded as\n"
00067     "an integer from 0 to nclasses-1.");
00068 
00070 // declareOptions //
00072 void ClassifierFromDensity::declareOptions(OptionList& ol)
00073 {
00074 
00075     // Build options.
00076 
00077     declareOption(ol, "nclasses", &ClassifierFromDensity::nclasses, OptionBase::buildoption,
00078                   "The number of classes");
00079 
00080     declareOption(ol, "estimators", &ClassifierFromDensity::estimators, OptionBase::buildoption,
00081                   "The array of density estimators, one for each class.\n"
00082                   "You may also specify just one that will be replicated as many times as there are classes.");
00083 
00084     declareOption(ol, "output_log_probabilities", &ClassifierFromDensity::output_log_probabilities, OptionBase::buildoption,
00085                   "Whether computeOutput yields log-probabilities or probabilities (of classes given inputs)");
00086  
00087     declareOption(ol, "normalize_probabilities", &ClassifierFromDensity::normalize_probabilities, OptionBase::buildoption,
00088                   "Whether to normalize the probabilities (if not just compute likelihood * prior for each class)");
00089 
00090     declareOption(ol, "use_these_priors", &ClassifierFromDensity::use_these_priors, OptionBase::buildoption,
00091                   "If empty (the default), then prior class probability is determined upon \n"
00092                   "training from the empirical class count in the training set. Otherwise this\n"
00093                   "must be a vector of length nclasses, specifying these prior class prob. \n");
00094 
00095     // Learnt options.
00096 
00097     declareOption(ol, "log_priors", &ClassifierFromDensity::log_priors, OptionBase::learntoption,
00098                   "The log of the class prior probabilities");
00099 
00100     declareOption(ol, "using_template_estimator", &ClassifierFromDensity::using_template_estimator, OptionBase::learntoption,
00101                   "Indication that the estimators were originally obtained from one single template");
00102 
00103     // Now call the parent class' declareOptions
00104     inherited::declareOptions(ol);
00105 }
00106 
00108 // build //
00110 void ClassifierFromDensity::build()
00111 {
00112     inherited::build();
00113     build_();
00114 }
00115 
00117 // build_ //
00119 void ClassifierFromDensity::build_()
00120 {
00121     if(estimators.size()==1)
00122     {
00123         using_template_estimator = true;
00124         estimators.resize(nclasses);
00125         for(int i=1; i<nclasses; i++)
00126             estimators[i] = PLearn::deepCopy(estimators[0]);
00127     }
00128     else if(estimators.size() != nclasses)
00129         PLERROR("In ClassifierFromDensity: specified %d estimators but there are %d classes",estimators.size(), nclasses);
00130 }
00131 
00133 // makeDeepCopyFromShallowCopy //
00135 void ClassifierFromDensity::makeDeepCopyFromShallowCopy(CopiesMap& copies)
00136 {
00137     inherited::makeDeepCopyFromShallowCopy(copies);
00138     deepCopyField(estimators,       copies);
00139     deepCopyField(log_priors,       copies);
00140     deepCopyField(use_these_priors, copies);
00141 }
00142 
00144 // outputsize //
00146 int ClassifierFromDensity::outputsize() const
00147 {
00148     return nclasses;
00149 }
00150 
00152 // forget //
00154 void ClassifierFromDensity::forget()
00155 {
00156     stage=0;
00157     if (using_template_estimator)
00158         estimators.resize(1);
00159     for(int c=0; c<estimators.length(); c++)
00160         estimators[c]->forget();
00161     if (using_template_estimator)
00162         build(); // Ensure the first estimator is duplicated.
00163 }
00164 
00166 // train //
00168 void ClassifierFromDensity::train()
00169 {
00170     if(targetsize()!=1)
00171         PLERROR("In ClassifierFromDensity::train - Expecting a targetsize of 1 (class index between 0 and nclasses-1), not %d !!",targetsize());
00172 
00173     if(nstages<stage) // asking to revert to a previous stage!
00174         forget();  // reset the learner to stage=0
00175 
00176     if(stage==0)
00177     {
00178         map<real, TVec<int> > indices = indicesOfOccurencesInColumn(train_set, inputsize());
00179 
00180         log_priors.resize(nclasses);
00181         if(use_these_priors.length()==0)
00182         {
00183             for(int c=0; c<nclasses; c++)
00184                 log_priors[c] = pl_log(real(indices[real(c)].length())) - pl_log(real(train_set.length())); // how many do we have?
00185         }
00186         else
00187         {
00188             if(use_these_priors.length()!=nclasses)
00189                 PLERROR("In ClassifierFromDensity::train, when compute_empirical_priors is false, you must specify a proper log_priors of length nclasses");  
00190             for(int c=0; c<nclasses; c++)
00191                 log_priors[c] = pl_log(use_these_priors[c]);
00192         }
00193 
00194         PPath expd = getExperimentDirectory();
00195 
00196         for(int c=0; c<nclasses; c++)
00197         {
00198             if(verbosity>=1)
00199                 perr << ">>> Training class " << c << endl;
00200             VMat set_c = train_set.rows(indices[c]);
00201             int in_sz = set_c->inputsize();
00202             int targ_sz = set_c->targetsize();
00203             int we_sz = set_c->weightsize();
00204             // Removing target from set
00205             if (we_sz==0)
00206             {
00207                 set_c = set_c.subMatColumns(0,in_sz);
00208                 set_c->defineSizes(in_sz, 0, 0);
00209             }
00210             else // There are some weights.
00211             {
00212                 set_c = hconcat(set_c.subMatColumns(0,in_sz), set_c.subMatColumns(in_sz+targ_sz,we_sz));
00213                 set_c->defineSizes(in_sz,0,we_sz);
00214             }
00215             if (expd!="")
00216                 estimators[c]->setExperimentDirectory(expd / "Class" / tostring(c));
00217             if (verbosity>=1)
00218                 perr << " ( " << set_c.length() << " samples)" << endl;
00219             estimators[c]->setTrainingSet(set_c);
00220             PP<VecStatsCollector> train_stats = new VecStatsCollector();
00221             train_stats->setFieldNames(estimators[c]->getTrainCostNames());
00222             estimators[c]->setTrainStatsCollector(train_stats);
00223             estimators[c]->nstages = nstages;
00224             estimators[c]->train();
00225         }
00226         stage = nstages; // trained!
00227         if (verbosity >= 2)
00228             perr << ">>> Training is over" << endl;
00229     }
00230     if(stage>0 && stage<nstages)
00231     {
00232         for(int c=0; c<nclasses; c++)
00233         {
00234             if(verbosity>=1)
00235                 perr << ">>> Training class " << c;
00236             estimators[c]->nstages = nstages;
00237             estimators[c]->train();
00238         }
00239         stage = nstages;
00240         if (verbosity >= 2)
00241             perr << ">>> Training is over" << endl;
00242     }
00243     
00244 }
00245 
00247 // computeOutput //
00249 void ClassifierFromDensity::computeOutput(const Vec& input, Vec& output) const
00250 {
00251     output.resize(nclasses);
00252     double log_of_sumprob = 0.;
00253 
00254     for(int c=0; c<nclasses; c++)
00255     {
00256         // Be slightly careful in the case were the log_prior is -Inf, i.e.
00257         // no observation of the current class appears in the training set.
00258         // Don't call the estimator in that case, since its output might be
00259         // ill-defined (NaN or some such), thereby polluting the rest of the
00260         // output computation
00261 
00262         double logprob_c = log_priors[c];
00263         if (! isinf(log_priors[c]))
00264             logprob_c += estimators[c]->log_density(input);  // multiply p by the prior
00265 
00266         output[c] = logprob_c;
00267         if (normalize_probabilities)
00268         {
00269             if(c==0)
00270                 log_of_sumprob = logprob_c;
00271             else
00272                 log_of_sumprob = logadd(log_of_sumprob, logprob_c);
00273         }
00274     }      
00275 
00276     if (normalize_probabilities)
00277         output -= real(log_of_sumprob); // divide by the sum
00278 
00279     // Make it probabilities rather than log probabilities...
00280     if (!output_log_probabilities)
00281         exp(output, output);
00282 }
00283 
00285 // computeCostsFromOutputs //
00287 void ClassifierFromDensity::computeCostsFromOutputs(const Vec& input, const Vec& output, 
00288                                                     const Vec& target, Vec& costs) const
00289 {
00290     static CostFunc cl_er;
00291     static CostFunc cond_p;
00292     if(!cl_er)
00293         cl_er = class_error();
00294     if(!cond_p)
00295         cond_p = condprob_cost();
00296     costs.resize(2);
00297     costs[0] = cl_er->evaluate(output, target);
00298     costs[1] = cond_p->evaluate(output, target);
00299 }                                
00300 
00302 // getTestCostNames //
00304 TVec<string> ClassifierFromDensity::getTestCostNames() const
00305 {
00306     TVec<string> cnames(2);
00307     cnames[0] = "class_error";
00308     cnames[1] = "condprob_cost";
00309     return cnames;
00310 }
00311 
00313 // getTrainCostNames //
00315 TVec<string> ClassifierFromDensity::getTrainCostNames() const
00316 {
00317     return TVec<string>();
00318 }
00319 
00320 } // end of namespace PLearn
00321 
00322 
00323 /*
00324   Local Variables:
00325   mode:c++
00326   c-basic-offset:4
00327   c-file-style:"stroustrup"
00328   c-file-offsets:((innamespace . 0)(inline-open . 0))
00329   indent-tabs-mode:nil
00330   fill-column:79
00331   End:
00332 */
00333 // 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