Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members

KernelProjection.cc

Go to the documentation of this file.
00001 // -*- C++ -*- 00002 00003 // KernelProjection.cc 00004 // 00005 // Copyright (C) 2004 Olivier Delalleau 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 /* ******************************************************* 00036 * $Id: KernelProjection.cc,v 1.18 2004/07/21 20:28:13 tihocan Exp $ 00037 ******************************************************* */ 00038 00039 // Authors: Olivier Delalleau 00040 00043 #include <time.h> 00044 #include "KernelProjection.h" 00045 #include <plearn/math/plapack.h> 00046 00047 namespace PLearn { 00048 using namespace std; 00049 00051 // KernelProjection // 00053 KernelProjection::KernelProjection() 00054 : n_comp_kept(-1), 00055 first_output(true), 00056 compute_costs(false), 00057 free_extra_components(true), 00058 ignore_n_first(0), 00059 min_eigenvalue(-REAL_MAX), 00060 n_comp(1), 00061 n_comp_for_cost(-1), 00062 normalize(0) 00063 00064 { 00065 } 00066 00067 PLEARN_IMPLEMENT_OBJECT(KernelProjection, 00068 "Performs dimensionality reduction by learning eigenfunctions of a kernel.", 00069 "" 00070 ); 00071 00073 // declareOptions // 00075 void KernelProjection::declareOptions(OptionList& ol) 00076 { 00077 00078 // Build options. 00079 00080 declareOption(ol, "kernel", &KernelProjection::kernel, OptionBase::buildoption, 00081 "The kernel used to compute the Gram matrix."); 00082 00083 declareOption(ol, "n_comp", &KernelProjection::n_comp, OptionBase::buildoption, 00084 "Number of components computed."); 00085 00086 declareOption(ol, "normalize", &KernelProjection::normalize, OptionBase::buildoption, 00087 "The kind of normalization performed when computing the output\n" 00088 " - 0: none (classical projection on the eigenvectors)\n" 00089 " - 1: normalization to get unit variance on each coordinate\n" 00090 " - 2: ignore the eigenvalues and do as if they were all 1\n"); 00091 00092 declareOption(ol, "min_eigenvalue", &KernelProjection::min_eigenvalue, OptionBase::buildoption, 00093 "Any component associated with an eigenvalue <= min_eigenvalue will be discarded."); 00094 00095 declareOption(ol, "compute_costs", &KernelProjection::compute_costs, OptionBase::buildoption, 00096 "Whether we should compute costs or not."); 00097 00098 declareOption(ol, "n_comp_for_cost", &KernelProjection::n_comp_for_cost, OptionBase::buildoption, 00099 "The number of components considered when computing a cost (default = -1 means n_comp)."); 00100 00101 declareOption(ol, "free_extra_components", &KernelProjection::free_extra_components, OptionBase::buildoption, 00102 "If set to 1, components computed but not kept won't be available after training."); 00103 00104 declareOption(ol, "ignore_n_first", &KernelProjection::ignore_n_first, OptionBase::buildoption, 00105 "Will ignore the first 'ignore_n_first' eigenvectors, if this option is > 0."); 00106 00107 // Learnt options. 00108 00109 declareOption(ol, "eigenvalues", &KernelProjection::eigenvalues, OptionBase::learntoption, 00110 "The eigenvalues of the Gram matrix."); 00111 00112 declareOption(ol, "eigenvectors", &KernelProjection::eigenvectors, OptionBase::learntoption, 00113 "The eigenvectors of the Gram matrix."); 00114 00115 declareOption(ol, "n_comp_kept", &KernelProjection::n_comp_kept, OptionBase::learntoption, 00116 "The actual number of components actually kept in the output (we may discard\n" 00117 "some because of low eigenvalues)."); 00118 00119 declareOption(ol, "n_examples", &KernelProjection::n_examples, OptionBase::learntoption, 00120 "The number of points in the training set."); 00121 00122 // Now call the parent class' declareOptions 00123 inherited::declareOptions(ol); 00124 } 00125 00127 // build // 00129 void KernelProjection::build() 00130 { 00131 inherited::build(); 00132 build_(); 00133 } 00134 00136 // build_ // 00138 void KernelProjection::build_() 00139 { 00140 if (n_comp_kept == -1) { 00141 n_comp_kept = n_comp; 00142 } 00143 // Ensure 'normalize' has a correct value. 00144 switch(normalize) { 00145 case 0: case 1: case 2: 00146 break; 00147 default: 00148 PLERROR("In KernelProjection::build_ - Wrong value for 'normalize')"); 00149 } 00150 first_output = true; // Safer. 00151 last_input.resize(0); 00152 } 00153 00155 // computeCostsFromOutputs // 00157 void KernelProjection::computeCostsFromOutputs(const Vec& input, const Vec& output, 00158 const Vec& target, Vec& costs) const 00159 { 00160 if (!compute_costs) 00161 return; 00162 // fs_squared_norm_reconstruction_error (see getTestCostNames). 00163 real k_x_x = kernel->evaluate(input, input); 00164 real fs_norm; 00165 if (n_comp_for_cost > 0) { 00166 // Only take the 'n_comp_for_cost' first components. 00167 fs_norm = pownorm(output.subVec(0, n_comp_for_cost)); 00168 } else { 00169 fs_norm = pownorm(output); 00170 } 00171 costs.resize(2); 00172 if (last_input.length() == 0) { 00173 last_input.resize(input.length()); 00174 last_output.resize(output.length()); 00175 last_input << input; 00176 last_output << output; 00177 costs[1] = MISSING_VALUE; 00178 } else { 00179 real k_x_y = kernel->evaluate(input, last_input); 00180 real fs_dotp; 00181 if (n_comp_for_cost > 0) { 00182 // Only take the 'n_comp_for_cost' first components. 00183 fs_dotp = dot(output.subVec(0, n_comp_for_cost), last_output.subVec(0, n_comp_for_cost)); 00184 } else { 00185 fs_dotp = dot(output, last_output); 00186 } 00187 last_input.resize(0); 00188 real diff = k_x_y - fs_dotp; 00189 costs[1] = diff * diff; 00190 } 00191 costs[0] = abs(k_x_x - fs_norm); 00192 if (k_x_x - fs_norm < -1e-5) { 00193 // TODO Remove this later after making sure it didn't happen. 00194 cout << "Negative error: " << k_x_x - fs_norm << " (k_x_x = " << k_x_x << ", fs_norm = " << fs_norm << ")" << endl; 00195 } 00196 } 00197 00199 // computeOutput // 00201 void KernelProjection::computeOutput(const Vec& input, Vec& output) const 00202 { 00203 static real* result_ptr; 00204 if (first_output) { 00205 // Initialize k_x_xi, used_eigenvectors and result correctly. 00206 k_x_xi.resize(n_examples); 00207 used_eigenvectors = eigenvectors.subMatRows(0, n_comp_kept); 00208 result.resize(n_comp_kept,1); 00209 first_output = false; 00210 } 00211 // Compute the K(x,x_i). 00212 kernel->evaluate_all_i_x(input, k_x_xi); 00213 // Compute the output. 00214 rowSum(used_eigenvectors * k_x_xi, result); 00215 output.resize(n_comp_kept); 00216 result_ptr = result[0]; 00217 if (normalize == 1) { 00218 real norm_coeff = sqrt(real(n_examples)); 00219 for (int i = 0; i < n_comp_kept; i++) { 00220 output[i] = *(result_ptr++) / eigenvalues[i] * norm_coeff; 00221 } 00222 } else if (normalize == 0) { 00223 for (int i = 0; i < n_comp_kept; i++) { 00224 output[i] = *(result_ptr++) / sqrt(eigenvalues[i]); 00225 } 00226 } else if (normalize == 2) { 00227 output << result; 00228 output *= sqrt(real(n_examples)); 00229 } else { 00230 PLERROR("In KernelProjection::computeOutput - Wrong value for 'normalize')"); 00231 } 00232 } 00233 00235 // forget // 00237 void KernelProjection::forget() 00238 { 00239 stage = 0; 00240 n_comp_kept = n_comp; 00241 n_examples = 0; 00242 first_output = true; 00243 last_input.resize(0); 00244 // Free memory. 00245 eigenvectors = Mat(); 00246 eigenvalues = Vec(); 00247 } 00248 00250 // getTestCostNames // 00252 TVec<string> KernelProjection::getTestCostNames() const 00253 { 00254 TVec<string> t; 00255 if (!compute_costs) 00256 return t; 00257 // Feature space squared norm reconstruction error: 00258 // | K(x,x) - ||output||^2 | 00259 t.append("fs_squared_norm_reconstruction_error"); 00260 // Feature space dot product reconstruction squared error: 00261 // ( K(x,y) - <output_x,output_y> )^2 00262 t.append("fs_dotp_reconstruction_squared_error"); 00263 return t; 00264 } 00265 00267 // getTrainCostNames // 00269 TVec<string> KernelProjection::getTrainCostNames() const 00270 { 00271 return getTestCostNames(); 00272 } 00273 00275 // makeDeepCopyFromShallowCopy // 00277 void KernelProjection::makeDeepCopyFromShallowCopy(map<const void*, void*>& copies) 00278 { 00279 inherited::makeDeepCopyFromShallowCopy(copies); 00280 00281 // ### Call deepCopyField on all "pointer-like" fields 00282 // ### that you wish to be deepCopied rather than 00283 // ### shallow-copied. 00284 // ### ex: 00285 // deepCopyField(trainvec, copies); 00286 00287 // ### Remove this line when you have fully implemented this method. 00288 PLERROR("KernelProjection::makeDeepCopyFromShallowCopy not fully (correctly) implemented yet!"); 00289 } 00290 00291 00293 // outputsize // 00295 int KernelProjection::outputsize() const 00296 { 00297 return n_comp_kept; 00298 } 00299 00301 // setTrainingSet // 00303 void KernelProjection::setTrainingSet(VMat training_set, bool call_forget) { 00304 inherited::setTrainingSet(training_set, call_forget); 00305 n_examples = training_set->length(); 00306 // Save the dataset in the kernel, because it may be needed after we reload 00307 // the learner. 00308 if (kernel) 00309 { 00310 kernel->specify_dataset = training_set; 00311 kernel->build(); 00312 } 00313 else 00314 PLERROR("KernelProjection::setTrainingSet: You cannot use setTrainingSet without a kernel set"); 00315 } 00316 00318 // train // 00320 void KernelProjection::train() 00321 { 00322 if (stage == 1) { 00323 PLWARNING("In KernelProjection::train - Learner has already been trained"); 00324 return; 00325 } 00326 Mat gram(n_examples,n_examples); 00327 // (1) Compute the Gram matrix. 00328 if (report_progress) { 00329 kernel->report_progress = true; 00330 } 00331 clock_t time_for_gram = clock(); 00332 kernel->computeGramMatrix(gram); 00333 time_for_gram = clock() - time_for_gram; 00334 if (verbosity >= 3) { 00335 cout << flush; 00336 cout << "Time to compute the Gram matrix: " << real(time_for_gram) / real(CLOCKS_PER_SEC) << endl; 00337 } 00338 // (2) Compute its eigenvectors and eigenvalues. 00339 eigenVecOfSymmMat(gram, n_comp + ignore_n_first, eigenvalues, eigenvectors); 00340 if (ignore_n_first > 0) { 00341 eigenvalues = eigenvalues.subVec(ignore_n_first, eigenvalues.length() - ignore_n_first); 00342 eigenvectors = eigenvectors.subMatRows(ignore_n_first, eigenvectors.length() - ignore_n_first); 00343 } 00344 n_comp_kept = eigenvalues.length(); // Could be different of n_comp. 00345 // (3) Discard low eigenvalues. 00346 int p = 0; 00347 while (p < n_comp_kept && eigenvalues[p] > min_eigenvalue) 00348 p++; 00349 n_comp_kept = p; 00350 // (4) Optionally remove the discarded components. 00351 if (free_extra_components) { 00352 eigenvalues.resize(n_comp_kept); 00353 eigenvectors.resize(n_comp_kept, eigenvectors.width()); 00354 } 00355 // All done! 00356 first_output = true; 00357 stage = 1; 00358 } 00359 00360 } // end of namespace PLearn

Generated on Tue Aug 17 15:56:35 2004 for PLearn by doxygen 1.3.7