PLearn 0.1
NormalizationLearner.cc
Go to the documentation of this file.
00001 // -*- C++ -*-
00002 
00003 // NormalizationLearner.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 "NormalizationLearner.h"
00041 
00042 namespace PLearn {
00043 using namespace std;
00044 
00045 PLEARN_IMPLEMENT_OBJECT(
00046     NormalizationLearner,
00047     "This learner can perform normalization of its input (subtracting the mean and dividing by stddev) "
00048     "and can also exclude input components that have too low standard deviation, or too many missing values.",
00049     "NormalizationLearner produces as output a possibly normalized version of its input\n"
00050     "obtained by subtracting the mean and dividing by the standard deviation.\n"
00051     "It can also exclude input components whose standard deviation is below a specified value,\n"
00052     "or whose missing values exceed a certain proportion of times."
00053     "It also has a simple policy switch for deciding to keep missing values as is or replace them by 0.\n"
00054     "It is typically used as an early preprocessing step in a ChainedLearner \n"
00055     "NOTE: you may also consider using PCA(normalize=1), if you wan to obtain \n"
00056     "decorrelated 'sphered' data." );
00057 
00058 NormalizationLearner::NormalizationLearner()
00059     :min_allowed_stddev(1e-6),
00060      remove_components_with_stddev_smaller_than(-1),
00061      remove_components_whose_missing_proportion_exceeds(1),
00062      set_missing_to_zero(true),
00063      do_normalize(true)
00064 {
00065 }
00066 
00067 void NormalizationLearner::declareOptions(OptionList& ol)
00068 {
00069     declareOption(ol, "min_allowed_stddev", &NormalizationLearner::min_allowed_stddev,
00070                   OptionBase::buildoption,
00071                   "If the empirical standard deviation is lower than this, we'll use this value to \n"
00072                   "compute inv_stddev (this is to prevent getting too large or even infinite values for inv_stddev");
00073 
00074     declareOption(ol, "remove_components_with_stddev_smaller_than", &NormalizationLearner::remove_components_with_stddev_smaller_than,
00075                   OptionBase::buildoption,
00076                   "Components of the input whose stddev is strictly below that value will be excluded from the output\n");
00077 
00078     declareOption(ol, "remove_components_whose_missing_proportion_exceeds", &NormalizationLearner::remove_components_whose_missing_proportion_exceeds,
00079                   OptionBase::buildoption,
00080                   "Components of the input that are missing more than that given fraction of times will be excluded from the output\n"
00081                   "The default 1 means no component will be excluded for being missing.\n"
00082                   "At the other extreme 0 means any component that was missing at east once wil be excluded\n"
00083                   "0.75 would exclude components that are missing more than 75 percent of the time\n");
00084 
00085     declareOption(ol, "do_normalize", &NormalizationLearner::do_normalize,
00086                   OptionBase::buildoption,
00087                   "If true (the default) then subtract mean and divide by stddev.\n"
00088                   "It can be useful to set this to false if all you want to do is remove components with small\n"
00089                   "stddev (see option remove_components with_small_stddev) but leave the others untouched.");
00090 
00091     declareOption(ol, "set_missing_to_zero", &NormalizationLearner::set_missing_to_zero,
00092                   OptionBase::buildoption,
00093                   "How to handle missing values: \n"
00094                   "  If true (the default), missing values will be replaced by 0\n"
00095                   "  (this corresponds to post-normalization mean if indeed we d_normakize) \n"
00096                   "  If false missing values will be left as missing values. \n");
00097 
00098     declareOption(ol, "meanvec", &NormalizationLearner::meanvec,
00099                   OptionBase::learntoption,
00100                   "The empirical mean to subtract from the input\n");
00101 
00102     declareOption(ol, "inv_stddev", &NormalizationLearner::inv_stddev,
00103                   OptionBase::learntoption,
00104                   "The vector of factors by which to multiply (input-meanvec)\n");
00105 
00106     declareOption(ol, "kept_components", &NormalizationLearner::kept_components,
00107                   OptionBase::learntoption,
00108                   "The indices of the input components kept in the final output\n");
00109 
00110     declareOption(ol, "inputnames", &NormalizationLearner::inputnames,
00111                   OptionBase::learntoption,
00112                   "We store the inputnames, which are also the outputnames\n");
00113 
00114 
00115 
00116     // Now call the parent class' declareOptions
00117     inherited::declareOptions(ol);
00118 }
00119 
00120 void NormalizationLearner::build_()
00121 {
00122     // ### This method should do the real building of the object,
00123     // ### according to set 'options', in *any* situation.
00124     // ### Typical situations include:
00125     // ###  - Initial building of an object from a few user-specified options
00126     // ###  - Building of a "reloaded" object: i.e. from the complete set of
00127     // ###    all serialised options.
00128     // ###  - Updating or "re-building" of an object after a few "tuning"
00129     // ###    options have been modified.
00130     // ### You should assume that the parent class' build_() has already been
00131     // ### called.
00132 
00133     int d = meanvec.length();
00134     if(d>0 && kept_components.length()==0) // fill uninitialized kept_components
00135     {
00136         kept_components.resize(d);
00137         for(int k=0; k<d; k++)
00138             kept_components[k] = k;
00139     }
00140 }
00141 
00142 // ### Nothing to add here, simply calls build_
00143 void NormalizationLearner::build()
00144 {
00145     inherited::build();
00146     build_();
00147 }
00148 
00149 
00150 void NormalizationLearner::makeDeepCopyFromShallowCopy(CopiesMap& copies)
00151 {
00152     inherited::makeDeepCopyFromShallowCopy(copies);
00153 
00154     // ### Call deepCopyField on all "pointer-like" fields
00155     // ### that you wish to be deepCopied rather than
00156     // ### shallow-copied.
00157     // ### ex:
00158     // deepCopyField(trainvec, copies);
00159 
00160     // ### Remove this line when you have fully implemented this method.
00161     PLERROR("NormalizationLearner::makeDeepCopyFromShallowCopy not fully (correctly) implemented yet!");
00162 }
00163 
00164 
00165 int NormalizationLearner::outputsize() const
00166 {
00167     return kept_components.length();
00168 }
00169 
00170 void NormalizationLearner::forget()
00171 {
00172     inherited::forget();
00173     stage = 0;
00174 }
00175 
00176 void NormalizationLearner::train()
00177 {
00178     if (!initTrain())
00179         return;
00180 
00181     if(stage<1)
00182     {
00183         inputnames = train_set->inputFieldNames();
00184 
00185         train_stats->forget();
00186         int l = train_set->length();
00187         int n = train_set->inputsize();
00188         Vec input;
00189         Vec target;
00190         real weight;
00191 
00192         VecStatsCollector st;
00193         PP<ProgressBar> pb;
00194         if(report_progress)
00195             pb = new ProgressBar("NormalizationLearner computing statistics ",l);
00196 
00197         for(int i=0; i<l; i++)
00198         {
00199             train_set->getExample(i, input, target, weight);
00200             st.update(input, weight);
00201             if(pb)
00202                 pb->update(i);
00203         }
00204         st.finalize();
00205 
00206         st.getMean(meanvec);
00207         inv_stddev.resize(n);
00208         kept_components.resize(n);
00209         kept_components.resize(0);
00210         for(int k=0; k<n; k++)
00211         {
00212             const StatsCollector& stk = st.stats[k];
00213             real sd = stk.stddev();
00214             inv_stddev[k] = 1/max(min_allowed_stddev,sd);
00215             double missing_proportion = (double)stk.nmissing()/(double)l;
00216             if( (missing_proportion<=remove_components_whose_missing_proportion_exceeds)
00217                 && (sd>=remove_components_with_stddev_smaller_than) )
00218                 kept_components.append(k);
00219         }
00220         ++stage;
00221         train_stats->finalize(); 
00222     }
00223 }
00224 
00225 
00226 void NormalizationLearner::computeOutput(const Vec& input, Vec& output) const
00227 {
00228     int n = meanvec.length();
00229     if(input.length()!=n)
00230         PLERROR("length of input differs from length of meanvec!");
00231     int n2 = kept_components.length();
00232     output.resize(n2);
00233     real* p_input = input.data();
00234     real* p_output = output.data();
00235     real* p_meanvec = meanvec.data();
00236     real* p_inv_stddev = inv_stddev.data();
00237     int*  p_kept_components = kept_components.data();
00238 
00239     for(int k=0; k<n2; k++)
00240     {
00241         int pos = p_kept_components[k];
00242         real val = p_input[pos];
00243         if(is_missing(val))
00244         {
00245             if(set_missing_to_zero)
00246                 val = 0;
00247         }
00248         else if(do_normalize)
00249             val = p_inv_stddev[pos]*(val - p_meanvec[pos]);
00250 
00251         p_output[k] = val;
00252     }
00253 }
00254 
00255 void NormalizationLearner::computeCostsFromOutputs(const Vec& input, const Vec& output,
00256                                            const Vec& target, Vec& costs) const
00257 {
00258     costs.resize(0);
00259 }
00260 
00261 TVec<string> NormalizationLearner::getTestCostNames() const
00262 {
00263     return TVec<string>();
00264 }
00265 
00266 TVec<string> NormalizationLearner::getTrainCostNames() const
00267 {
00268     return TVec<string>();
00269 }
00270 
00271 TVec<string> NormalizationLearner::getOutputNames() const
00272 {
00273     TVec<string> outnames;
00274     if(kept_components.length()==inputnames.length())
00275         outnames = inputnames;
00276     else
00277     {
00278         int n2 = kept_components.length();
00279         outnames.resize(n2);
00280         for(int k=0; k<n2; k++)
00281             outnames[k] = inputnames[kept_components[k]];
00282     }
00283     return outnames;
00284 }
00285 
00286 
00287 } // end of namespace PLearn
00288 
00289 
00290 /*
00291   Local Variables:
00292   mode:c++
00293   c-basic-offset:4
00294   c-file-style:"stroustrup"
00295   c-file-offsets:((innamespace . 0)(inline-open . 0))
00296   indent-tabs-mode:nil
00297   fill-column:79
00298   End:
00299 */
00300 // 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