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