PLearn 0.1
|
00001 00002 // -*- C++ -*- 00003 00004 // GenerateDecisionPlot.cc 00005 // 00006 // Copyright (C) 2003 Pascal Vincent 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: GenerateDecisionPlot.cc 4052 2005-09-07 17:13:17Z plearner $ 00038 ******************************************************* */ 00039 00041 #include "GenerateDecisionPlot.h" 00042 #include <plearn/math/VecStatsCollector.h> 00043 #include <plearn/vmat/VMat_basic_stats.h> 00044 #include <plearn/io/load_and_save.h> 00045 00046 namespace PLearn { 00047 using namespace std; 00048 00049 00054 void DX_write_2D_fields(ostream& out, const string& basename, TVec<Mat> fields, real x0, real y0, real deltax, real deltay, 00055 TVec<string> fieldnames=TVec<string>()) 00056 { 00057 int nfields = fields.length(); 00058 int nx = fields[0].length(); 00059 int ny = fields[0].width(); 00060 00061 string posname = string("\"") + basename + "_gridpos\""; 00062 00063 out << "object " << posname << " class gridpositions counts " << nx << " " << ny << "\n" 00064 << "origin " << x0 << " " << y0 << "\n" 00065 << "delta " << deltax << " 0 \n" 00066 << "delta 0 " << deltay << " \n\n\n"; 00067 00068 string conname = string("\"") + basename + "_gridcon\""; 00069 00070 out << "object " << conname << " class gridconnections counts " << nx << " " << ny << "\n" 00071 // << "attribute \"element type\" string \"cubes\" \n" 00072 << "attribute \"ref\" string \"positions\" \n\n\n"; 00073 00074 for(int k=0; k<nfields; k++) 00075 { 00076 Mat& m = fields[k]; 00077 string fieldname = tostring(k); 00078 if(fieldnames) 00079 fieldname = fieldnames[k]; 00080 00081 string dataname = string("\"") + basename + "_" + fieldname + "_data\""; 00082 00083 out << "object " << dataname << " class array type float rank 0 items " << nx*ny << " data follows \n"; 00084 for(int i=0; i<nx; i++) 00085 { 00086 for(int j=0; j<ny; j++) 00087 out << m(i,j) << " "; 00088 out << "\n"; 00089 } 00090 out << "attribute \"dep\" string \"positions\" \n\n\n"; 00091 00092 out << "object \"" << fieldname << "\" class field \n" 00093 << "component \"positions\" " << posname << " \n" 00094 << "component \"connections\" " << conname << " \n" 00095 << "component \"data\" " << dataname << " \n\n\n"; 00096 } 00097 } 00098 00099 00100 void DX_write_2D_fields(ostream& out, const string& basename, Vec X, Vec Y, TVec<Mat> fields) 00101 { 00102 int nfields = fields.length(); 00103 int nx = fields[0].length(); 00104 int ny = fields[0].width(); 00105 00106 /* 00107 out << "object \"" << basename << "_X\" class array type float rank 0 items " << nx << " data follows \n"; 00108 for(int i=0; i<nx; i++) 00109 out << X[i] << "\n"; 00110 out << "\n\n"; 00111 00112 out << "object \"" << basename << "_Y\" class array type float rank 0 items " << ny << " data follows \n"; 00113 for(int i=0; i<ny; i++) 00114 out << Y[i] << "\n"; 00115 */ 00116 00117 string posname = string("\"") + basename + "_gridpos\""; 00118 out << "object " << posname << " class array type float rank 1 shape 2 items " << nx*ny << " data follows\n"; 00119 for(int i=0; i<nx; i++) 00120 for(int j=0; j<ny; j++) 00121 out << X[i] << " " << Y[j] << "\n"; 00122 out << "\n\n"; 00123 00124 string conname = string("\"") + basename + "_gridcon\""; 00125 out << "object " << conname << " class gridconnections counts " << nx << " " << ny << "\n" 00126 // << "attribute \"element type\" string \"cubes\" \n" 00127 << "attribute \"ref\" string \"positions\" \n\n\n"; 00128 00129 for(int k=0; k<nfields; k++) 00130 { 00131 Mat& m = fields[k]; 00132 string fieldname = "output" + tostring(k); 00133 string dataname = string("\"") + basename + "_" + fieldname + "_data\""; 00134 00135 out << "object " << dataname << " class array type float rank 0 items " << nx*ny << " data follows \n"; 00136 for(int i=0; i<nx; i++) 00137 { 00138 for(int j=0; j<ny; j++) 00139 out << m(i,j) << " "; 00140 out << "\n"; 00141 } 00142 out << "attribute \"dep\" string \"positions\" \n\n\n"; 00143 00144 out << "object \"" << fieldname << "\" class field \n" 00145 << "component \"positions\" " << posname << " \n" 00146 << "component \"connections\" " << conname << " \n" 00147 << "component \"data\" " << dataname << " \n\n\n"; 00148 } 00149 } 00150 00151 00152 TVec<Mat> computeOutputFields(PP<PLearner> learner, Vec X, Vec Y) 00153 { 00154 int noutputs = learner->outputsize(); 00155 00156 int nx = X.length(); 00157 int ny = Y.length(); 00158 int nfields = noutputs; 00159 TVec<Mat> fields(nfields); 00160 00161 for(int k=0; k<nfields; k++) 00162 fields[k].resize(nx,ny); 00163 00164 Vec input(2); 00165 Vec output(noutputs); 00166 00167 ProgressBar pb("Computing " + tostring(nx) + " x " + tostring(ny) + " output field",nx*ny); 00168 00169 for(int i=0; i<nx; i++) 00170 for(int j=0; j<ny; j++) 00171 { 00172 input[0] = X[i]; 00173 input[1] = Y[j]; 00174 learner->computeOutput(input,output); 00175 // cerr << "in: " << input << " out: " << output << endl; 00176 for(int k=0; k<noutputs; k++) 00177 fields[k](i,j) = output[k]; 00178 pb.update(i*nx+j); 00179 } 00180 00181 return fields; 00182 } 00183 00184 00185 TVec<Mat> computeOutputFields(PP<PLearner> learner, int nx, int ny, real x0, real y0, real deltax, real deltay) 00186 { 00187 int noutputs = learner->outputsize(); 00188 int nfields = noutputs; 00189 00190 TVec<Mat> fields(nfields); 00191 for(int k=0; k<nfields; k++) 00192 fields[k].resize(nx,ny); 00193 00194 Vec input(2); 00195 Vec output(noutputs); 00196 00197 ProgressBar pb("Computing " + tostring(nx) + " x " + tostring(ny) + " output field",nx*ny); 00198 00199 real x = x0; 00200 real y = y0; 00201 for(int i=0; i<nx; i++, x+=deltax) 00202 for(int j=0; j<ny; j++, y+=deltay) 00203 { 00204 input[0] = x; 00205 input[1] = y; 00206 learner->computeOutput(input,output); 00207 // cerr << "in: " << input << " out: " << output << endl; 00208 for(int k=0; k<noutputs; k++) 00209 fields[k](i,j) = output[k]; 00210 pb.update(i*nx+j); 00211 } 00212 00213 return fields; 00214 } 00215 00216 // Finds appropriate x0, y0, deltax, deltay from the dataset range, computes the fields and returns them 00217 // extraspace of .10 means we'll look 10% beyond the data range on every side 00218 TVec<Mat> computeOutputFieldsAutoRange(PP<PLearner> learner, VMat dataset, int nx, int ny, 00219 real& x0, real& y0, real& deltax, real& deltay, real extraspace=.10) 00220 { 00221 Vec minv(2); 00222 Vec maxv(2); 00223 computeRange(dataset.subMatColumns(0,2), minv, maxv); 00224 real extrax = (maxv[0]-minv[0])*extraspace; 00225 x0 = minv[0]-extrax; 00226 deltax = (maxv[0]+extrax-x0)/nx; 00227 real extray = (maxv[1]-minv[1])*extraspace; 00228 y0 = minv[1]-extray; 00229 deltay = (maxv[1]+extray-y0)/ny; 00230 return computeOutputFields(learner, nx, ny, x0, y0, deltax, deltay); 00231 } 00232 00233 00234 void computeXYPositions(VMat dataset, int nx, int ny, Vec& X, Vec& Y, real extraspace=.10) 00235 { 00236 Vec minv(2); 00237 Vec maxv(2); 00238 computeRange(dataset.subMatColumns(0,2), minv, maxv); 00239 real extrax = (maxv[0]-minv[0])*extraspace; 00240 real x0 = minv[0]-extrax; 00241 real deltax = (maxv[0]+extrax-x0)/nx; 00242 real extray = (maxv[1]-minv[1])*extraspace; 00243 real y0 = minv[1]-extray; 00244 real deltay = (maxv[1]+extray-y0)/ny; 00245 00246 set<real> xpos; 00247 set<real> ypos; 00248 int l = dataset.length(); 00249 Vec datapoint(2); 00250 for(int i=0; i<l; i++) 00251 { 00252 dataset->getRow(i,datapoint); 00253 xpos.insert(datapoint[0]); 00254 ypos.insert(datapoint[1]); 00255 } 00256 real x = x0; 00257 for(int i=0; i<nx; i++, x+=deltax) 00258 xpos.insert(x); 00259 real y = y0; 00260 for(int j=0; j<ny; j++, y+=deltay) 00261 ypos.insert(y); 00262 set<real>::iterator it; 00263 X.resize(xpos.size()); 00264 real* xptr = X.data(); 00265 it = xpos.begin(); 00266 while(it!=xpos.end()) 00267 *xptr++ = *it++; 00268 Y.resize(ypos.size()); 00269 real* yptr = Y.data(); 00270 it = ypos.begin(); 00271 while(it!=ypos.end()) 00272 *yptr++ = *it++; 00273 } 00274 00275 00276 00279 void DX_create_dataset_outputs_file(const string& filename, PP<PLearner> learner, VMat dataset) 00280 { 00281 ofstream out(filename.c_str()); 00282 00283 int l = dataset.length(); 00284 int inputsize = learner->inputsize(); 00285 int targetsize = learner->targetsize(); 00286 int outputsize = learner->outputsize(); 00287 00288 // First write data points (input -> target, output) 00289 Vec input(inputsize); 00290 Vec target(targetsize); 00291 real weight; 00292 Vec output(outputsize); 00293 00294 // write 2D positions 00295 out << "object \"dset_pos\" class array type float rank 1 shape " << inputsize << " items " << l << " data follows \n"; 00296 for(int i=0; i<l; i++) 00297 { 00298 dataset->getExample(i,input,target,weight); 00299 for(int j=0; j<inputsize; j++) 00300 out << input[j] << " "; 00301 out << "\n"; 00302 } 00303 out << "\n\n\n"; 00304 00305 // Now write data for those positions (target and output) 00306 if(targetsize+outputsize>0) 00307 { 00308 ProgressBar pb("Computing outputs for dataset points",l); 00309 out << "object \"dset_value\" class array type float rank 1 shape " << targetsize+outputsize << " items " << l << " data follows \n"; 00310 for(int i=0; i<l; i++) 00311 { 00312 dataset->getExample(i,input,target,weight); 00313 for(int j=0; j<targetsize; j++) 00314 out << target[j] << " "; 00315 learner->computeOutput(input, output); 00316 for(int j=0; j<outputsize; j++) 00317 out << output[j] << " "; 00318 out << "\n"; 00319 pb.update(i); 00320 } 00321 out << "attribute \"dep\" string \"positions\" \n\n\n"; 00322 } 00323 00324 // Field is created with two components: "positions" and "data" 00325 out << "object \"dset\" class field \n" 00326 << "component \"positions\" \"dset_pos\" \n"; 00327 if(targetsize+outputsize>0) 00328 out << "component \"data\" \"dset_value\" \n"; 00329 out << "\n\n\n"; 00330 00331 00332 00333 out << "end" << endl; 00334 } 00335 00336 00343 00344 void DX_create_grid_outputs_file(const string& filename, PP<PLearner> learner, VMat dataset, 00345 int nx, int ny, bool include_datapoint_grid=false, 00346 real xmin=MISSING_VALUE, real xmax=MISSING_VALUE, 00347 real ymin=MISSING_VALUE, real ymax=MISSING_VALUE, 00348 real extraspace=.10) 00349 { 00350 ofstream out(filename.c_str()); 00351 00352 double logsum = -FLT_MAX; 00353 00354 int l = dataset.length(); 00355 int inputsize = learner->inputsize(); 00356 int targetsize = learner->targetsize(); 00357 int outputsize = learner->outputsize(); 00358 00359 Vec input(inputsize); 00360 Vec target(targetsize); 00361 real weight; 00362 Vec output(outputsize); 00363 00364 // Create the grid field 00365 00366 set<real> xpos; 00367 set<real> ypos; 00368 00369 // First the regular grid coordinates 00370 Vec minv(2); 00371 Vec maxv(2); 00372 computeRange(dataset.subMatColumns(0,2), minv, maxv); 00373 real extrax = (maxv[0]-minv[0])*extraspace; 00374 real extray = (maxv[1]-minv[1])*extraspace; 00375 if(is_missing(xmin)) 00376 xmin = minv[0]-extrax; 00377 if(is_missing(xmax)) 00378 xmax = maxv[0]+extrax; 00379 if(is_missing(ymin)) 00380 ymin = minv[1]-extray; 00381 if(is_missing(ymax)) 00382 ymax = maxv[1]+extray; 00383 real deltax = (xmax-xmin)/nx; 00384 real deltay = (ymax-ymin)/ny; 00385 00386 real x = xmin; 00387 for(int i=0; i<nx; i++, x+=deltax) 00388 xpos.insert(x); 00389 real y = ymin; 00390 for(int j=0; j<ny; j++, y+=deltay) 00391 ypos.insert(y); 00392 00393 // also include irregular grid coordinates based on coordinates of dataset points? 00394 if(include_datapoint_grid) 00395 { 00396 for(int i=0; i<l; i++) 00397 { 00398 dataset->getExample(i,input,target,weight); 00399 x = input[0]; 00400 y = input[1]; 00401 if(x>xmin && x<xmax) 00402 xpos.insert(x); 00403 if(y>ymin && y<ymax) 00404 ypos.insert(y); 00405 } 00406 } 00407 00408 nx = xpos.size(); 00409 ny = ypos.size(); 00410 set<real>::iterator itx; 00411 set<real>::iterator ity; 00412 00413 out << "object \"outputs_gridpos\" class array type float rank 1 shape 2 items " << nx*ny << " data follows\n"; 00414 for(itx=xpos.begin(); itx!=xpos.end(); ++itx) 00415 for(ity=ypos.begin(); ity!=ypos.end(); ++ity) 00416 out << *itx << " " << *ity << "\n"; 00417 out << "\n\n"; 00418 00419 out << "object \"outputs_gridcon\" class gridconnections counts " << nx << " " << ny << "\n" 00420 // << "attribute \"element type\" string \"cubes\" \n" 00421 << "attribute \"ref\" string \"positions\" \n\n\n"; 00422 00423 out << "object \"outputs_values\" class array type float rank 1 shape " << outputsize << " items " << nx*ny << " data follows \n"; 00424 00425 ProgressBar pb("Computing outputs for grid positions: " + tostring(nx)+"x"+tostring(ny), nx*ny); 00426 int n = 0; 00427 for(itx=xpos.begin(); itx!=xpos.end(); ++itx) 00428 { 00429 input[0] = *itx; 00430 for(ity=ypos.begin(); ity!=ypos.end(); ++ity) 00431 { 00432 input[1] = *ity; 00433 learner->computeOutput(input, output); 00434 for(int j=0; j<outputsize; j++) 00435 out << output[j] << " "; 00436 out << "\n"; 00437 if(logsum==-FLT_MAX) 00438 logsum = output[0]; 00439 else 00440 logsum = logadd(logsum, output[0]); 00441 pb.update(n++); 00442 } 00443 } 00444 pb.close(); 00445 out << "attribute \"dep\" string \"positions\" \n\n\n"; 00446 00447 out << "object \"outputs\" class field \n" 00448 << "component \"positions\" \"outputs_gridpos\" \n" 00449 << "component \"connections\" \"outputs_gridcon\" \n" 00450 << "component \"data\" \"outputs_values\" \n\n\n"; 00451 00452 out << "end" << endl; 00453 00454 double surfelem = deltax*deltay; 00455 double surfintegral = exp(logsum)*surfelem; 00456 cerr << "Estimated integral over sampled domain: " << surfintegral << endl; 00457 } 00458 00459 00460 GenerateDecisionPlot::GenerateDecisionPlot() 00461 :basename("dxplot"), 00462 nx(10), ny(10), 00463 include_datapoint_grid(false), 00464 xmin(MISSING_VALUE), 00465 xmax(MISSING_VALUE), 00466 ymin(MISSING_VALUE), 00467 ymax(MISSING_VALUE) 00468 { 00469 } 00470 00471 PLEARN_IMPLEMENT_OBJECT(GenerateDecisionPlot, "ONE LINE DESCR", "NO HELP"); 00472 00473 void GenerateDecisionPlot::declareOptions(OptionList& ol) 00474 { 00475 // ### Declare all of this object's options here 00476 // ### For the "flags" of each option, you should typically specify 00477 // ### one of OptionBase::buildoption, OptionBase::learntoption or 00478 // ### OptionBase::tuningoption. Another possible flag to be combined with 00479 // ### is OptionBase::nosave 00480 00481 declareOption(ol, "basename", &GenerateDecisionPlot::basename, OptionBase::buildoption, 00482 "Base name of the .dx data file to generate. Running this class will generate\n" 00483 "files basename_dset.dx containing targets and outputs for the given dataset positions\n" 00484 "and basename_outputs.dx containing outputs computed at grid positions\n"); 00485 declareOption(ol, "learner", &GenerateDecisionPlot::learner, OptionBase::buildoption, 00486 "The learner to train/test. Its train_set will be used as the dataset on\n" 00487 "which to base this decision plot (ranges are inferred from it, and decisions\n" 00488 "on the training points are written in basename_dset.dx"); 00489 declareOption(ol, "nx", &GenerateDecisionPlot::nx, OptionBase::buildoption, 00490 "Number of x sample coordinates (grid)"); 00491 declareOption(ol, "ny", &GenerateDecisionPlot::ny, OptionBase::buildoption, 00492 "Number of y sample coordinates (grid)"); 00493 declareOption(ol, "include_datapoint_grid", &GenerateDecisionPlot::include_datapoint_grid, OptionBase::buildoption, 00494 ""); 00495 declareOption(ol, "xmin", &GenerateDecisionPlot::xmin, OptionBase::buildoption, 00496 ""); 00497 declareOption(ol, "xmax", &GenerateDecisionPlot::xmax, OptionBase::buildoption, 00498 ""); 00499 declareOption(ol, "ymin", &GenerateDecisionPlot::ymin, OptionBase::buildoption, 00500 ""); 00501 declareOption(ol, "ymax", &GenerateDecisionPlot::ymax, OptionBase::buildoption, 00502 ""); 00503 declareOption(ol, "save_learner_as", &GenerateDecisionPlot::save_learner_as, OptionBase::buildoption, 00504 "(Optionally) save trained learner in this file (.psave)"); 00505 00506 // Now call the parent class' declareOptions 00507 inherited::declareOptions(ol); 00508 } 00509 00510 void GenerateDecisionPlot::build_() 00511 { 00512 // ### This method should do the real building of the object, 00513 // ### according to set 'options', in *any* situation. 00514 // ### Typical situations include: 00515 // ### - Initial building of an object from a few user-specified options 00516 // ### - Building of a "reloaded" object: i.e. from the complete set of all serialised options. 00517 // ### - Updating or "re-building" of an object after a few "tuning" options have been modified. 00518 // ### You should assume that the parent class' build_() has already been called. 00519 } 00520 00522 void GenerateDecisionPlot::run() 00523 { 00524 VMat dataset = learner->getTrainingSet(); 00525 learner->setTrainStatsCollector(new VecStatsCollector()); 00526 learner->train(); 00527 00528 if(save_learner_as!="") 00529 { 00530 cerr << "Saving trained learner in file " << save_learner_as << endl; 00531 PLearn::save(save_learner_as, *learner); 00532 } 00533 00534 string dset_fname = basename+"_dset.dx"; 00535 cerr << "Computing and writing dataset output field to file " << dset_fname << endl; 00536 DX_create_dataset_outputs_file(dset_fname, learner, dataset); 00537 00538 string outputs_fname = basename+"_outputs.dx"; 00539 cerr << "Computing and writing grid output field to file " << outputs_fname << endl; 00540 DX_create_grid_outputs_file(outputs_fname, learner, dataset, nx, ny, 00541 include_datapoint_grid, 00542 xmin, xmax, ymin, ymax); 00543 cerr << "You can now view those files with OpenDX." << endl; 00544 } 00545 00546 00547 // ### Nothing to add here, simply calls build_ 00548 void GenerateDecisionPlot::build() 00549 { 00550 inherited::build(); 00551 build_(); 00552 } 00553 00554 00555 void GenerateDecisionPlot::makeDeepCopyFromShallowCopy(CopiesMap& copies) 00556 { 00557 inherited::makeDeepCopyFromShallowCopy(copies); 00558 } 00559 00560 } // end of namespace PLearn 00561 00562 00563 /* 00564 Local Variables: 00565 mode:c++ 00566 c-basic-offset:4 00567 c-file-style:"stroustrup" 00568 c-file-offsets:((innamespace . 0)(inline-open . 0)) 00569 indent-tabs-mode:nil 00570 fill-column:79 00571 End: 00572 */ 00573 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=79 :