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