PLearn 0.1
|
00001 // -*- C++ -*- 00002 00003 // RBMDistribution.cc 00004 // 00005 // Copyright (C) 2008 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 // Authors: Olivier Delalleau 00036 00040 #include "RBMDistribution.h" 00041 00042 namespace PLearn { 00043 using namespace std; 00044 00045 PLEARN_IMPLEMENT_OBJECT( 00046 RBMDistribution, 00047 "Distribution learnt by a Restricted Boltzmann Machine.", 00048 "The RBM is train by standard Contrastive Divergence in online mode." 00049 ); 00050 00052 // RBMDistribution // 00054 RBMDistribution::RBMDistribution(): 00055 n_gibbs_chains(-1), 00056 unnormalized_density(false) 00057 {} 00058 00060 // declareOptions // 00062 void RBMDistribution::declareOptions(OptionList& ol) 00063 { 00064 // ### Declare all of this object's options here. 00065 // ### For the "flags" of each option, you should typically specify 00066 // ### one of OptionBase::buildoption, OptionBase::learntoption or 00067 // ### OptionBase::tuningoption. If you don't provide one of these three, 00068 // ### this option will be ignored when loading values from a script. 00069 // ### You can also combine flags, for example with OptionBase::nosave: 00070 // ### (OptionBase::buildoption | OptionBase::nosave) 00071 00072 declareOption(ol, "rbm", &RBMDistribution::rbm, 00073 OptionBase::buildoption, 00074 "Underlying RBM modeling the distribution."); 00075 00076 declareOption(ol, "n_gibbs_chains", &RBMDistribution::n_gibbs_chains, 00077 OptionBase::buildoption, 00078 "Number of Gibbs chains ran in parallel when generating multiple\n" 00079 "samples with generateN(). If <0, then there are as many chains as\n" 00080 "samples. If in the (0,1) interval, then it is the given fraction of\n" 00081 "the number of generated samples. If an integer >= 1, it is the\n" 00082 "absolute number of chains that are run simultaneously. Each chain\n" 00083 "will sample about N/n_chains samples, so as to obtain N samples."); 00084 00085 declareOption(ol, "unnormalized_density", 00086 &RBMDistribution::unnormalized_density, 00087 OptionBase::buildoption, 00088 "If set to True, then the density will not be normalized (so the\n" 00089 "partition function does not need to be computed). This means the\n" 00090 "value returned by the 'log_density' method will instead be the\n" 00091 "negative free energy of the visible input."); 00092 00093 declareOption(ol, "sample_data", 00094 &RBMDistribution::sample_data, 00095 OptionBase::buildoption, 00096 "If provided, this data will be used to initialize the Gibbs\n" 00097 "chains when generating samples."); 00098 00099 // Now call the parent class' declareOptions(). 00100 inherited::declareOptions(ol); 00101 } 00102 00104 // build // 00106 void RBMDistribution::build() 00107 { 00108 inherited::build(); 00109 build_(); 00110 } 00111 00113 // build_ // 00115 void RBMDistribution::build_() 00116 { 00117 if (!rbm) 00118 return; 00119 int n_ports = rbm->nPorts(); 00120 ports_val.resize(n_ports); 00121 predicted_size = rbm->visible_layer->size; 00122 // Rebuild the PDistribution object to take size into account. 00123 inherited::build(); 00124 } 00125 00127 // cdf // 00129 real RBMDistribution::cdf(const Vec& y) const 00130 { 00131 PLERROR("cdf not implemented for RBMDistribution"); return 0; 00132 } 00133 00135 // expectation // 00137 void RBMDistribution::expectation(Vec& mu) const 00138 { 00139 PLERROR("In RBMDistribution::expectation - Not implemeted"); 00140 } 00141 00143 // forget // 00145 void RBMDistribution::forget() 00146 { 00147 rbm->forget(); 00148 learner = NULL; 00149 inherited::forget(); 00150 n_predicted = rbm->visible_layer->size; 00151 } 00152 00154 // generate // 00156 void RBMDistribution::generate(Vec& y) const 00157 { 00158 work1.resize(0, 0); 00159 ports_val.fill(NULL); 00160 ports_val[rbm->getPortIndex("visible_sample")] = &work1; 00161 if (sample_data) { 00162 // Pick a random sample to initialize the Gibbs chain. 00163 int init_i = 00164 random_gen->uniform_multinomial_sample(sample_data->length()); 00165 real dummy_weight; 00166 work3.resize(1, sample_data->inputsize()); 00167 Vec w3 = work3.toVec(); 00168 sample_data->getExample(init_i, w3, workv1, dummy_weight); 00169 ports_val[rbm->getPortIndex("visible")] = &work2; 00170 } 00171 rbm->fprop(ports_val); 00172 y.resize(work1.width()); 00173 y << work1(0); 00174 } 00175 00177 // generateN // 00179 void RBMDistribution::generateN(const Mat& Y) const 00180 { 00181 int n = Y.length(); // Number of samples to obtain. 00182 int n_chains = Y.length(); 00183 if (n_gibbs_chains > 0 && n_gibbs_chains < 1) { 00184 // Fraction. 00185 n_chains = min(1, int(round(n_gibbs_chains * n))); 00186 } else if (n_gibbs_chains > 0) { 00187 n_chains = int(round(n_gibbs_chains)); 00188 PLCHECK( is_equal(real(n_chains), n_gibbs_chains) ); 00189 } 00190 int n_gibbs_samples = n / n_chains; 00191 if (n % n_chains > 0) 00192 n_gibbs_samples += 1; 00193 work2.resize(n_chains * n_gibbs_samples, Y.width()); 00194 PP<ProgressBar> pb = verbosity && work2.length() > 10 00195 ? new ProgressBar("Gibbs sampling", work2.length()) 00196 : NULL; 00197 int idx = 0; 00198 for (int j = 0; j < n_chains; j++) { 00199 ports_val.fill(NULL); 00200 if (sample_data) { 00201 // Pick a sample to initialize the Gibbs chain. 00202 int init_i; 00203 if (n_chains == sample_data->length()) 00204 // We use each sample once and only once. 00205 init_i = j; 00206 else 00207 // Pick the sample randomly. 00208 init_i = random_gen->uniform_multinomial_sample(sample_data->length()); 00209 real dummy_weight; 00210 work3.resize(1, sample_data->inputsize()); 00211 Vec w3 = work3.toVec(); 00212 sample_data->getExample(init_i, w3, workv1, dummy_weight); 00213 ports_val[rbm->getPortIndex("visible")] = &work3; 00214 } 00215 // Crash if not in the specific case where we have sample data and we 00216 // compute only 1 sample in each chain. This is because otherwise I 00217 // (Olivier D.) am not sure the chain is properly (i) restarted for 00218 // each new chain, and (ii) kept intact when continuing the same chain. 00219 PLCHECK(sample_data && n_gibbs_samples == 1); 00220 for (int i = 0; i < n_gibbs_samples; i++) { 00221 work1.resize(0, 0); 00222 ports_val[rbm->getPortIndex("visible_sample")] = &work1; 00223 rbm->fprop(ports_val); 00224 work2(idx) << work1; 00225 idx++; 00226 if (pb) 00227 pb->update(idx); 00228 } 00229 } 00230 if (n_gibbs_samples > 1) 00231 // We shuffle rows to add more "randomness" since consecutive samples 00232 // in the same Gibbs chain may be similar. 00233 random_gen->shuffleRows(work2); 00234 Y << work2.subMatRows(0, Y.length()); 00235 } 00236 00238 // log_density // 00240 real RBMDistribution::log_density(const Vec& y) const 00241 { 00242 ports_val.fill(NULL); 00243 work1.resize(1, 0); 00244 work2.resize(1, y.length()); 00245 work2 << y; 00246 if (unnormalized_density) 00247 ports_val[rbm->getPortIndex("energy")] = &work1; 00248 else 00249 ports_val[rbm->getPortIndex("neg_log_likelihood")] = &work1; 00250 ports_val[rbm->getPortIndex("visible")] = &work2; 00251 rbm->fprop(ports_val); 00252 return -work1(0, 0); 00253 } 00254 00256 // makeDeepCopyFromShallowCopy // 00258 void RBMDistribution::makeDeepCopyFromShallowCopy(CopiesMap& copies) 00259 { 00260 inherited::makeDeepCopyFromShallowCopy(copies); 00261 00262 // ### Call deepCopyField on all "pointer-like" fields 00263 // ### that you wish to be deepCopied rather than 00264 // ### shallow-copied. 00265 // ### ex: 00266 // deepCopyField(trainvec, copies); 00267 00268 // ### Remove this line when you have fully implemented this method. 00269 PLERROR("RBMDistribution::makeDeepCopyFromShallowCopy not fully (correctly) implemented yet!"); 00270 } 00271 00273 // resetGenerator // 00275 void RBMDistribution::resetGenerator(long g_seed) 00276 { 00277 if (!rbm->random_gen) 00278 PLERROR("In RBMDistribution::resetGenerator - The underlying RBM " 00279 "must have a random number generator"); 00280 if (g_seed != 0) 00281 rbm->random_gen->manual_seed(g_seed); 00282 inherited::resetGenerator(g_seed); 00283 } 00284 00286 // survival_fn // 00288 real RBMDistribution::survival_fn(const Vec& y) const 00289 { 00290 PLERROR("survival_fn not implemented for RBMDistribution"); return 0; 00291 } 00292 00294 // train // 00296 void RBMDistribution::train() 00297 { 00298 if (!learner) { 00299 // First build the learner that will train a RBM. 00300 learner = new ModuleLearner(); 00301 learner->module = rbm; 00302 learner->seed_ = this->seed_; 00303 learner->use_a_separate_random_generator_for_testing = 00304 this->use_a_separate_random_generator_for_testing; 00305 learner->input_ports = TVec<string>(1, "visible"); 00306 learner->target_ports.resize(0); 00307 learner->cost_ports.resize(0); 00308 learner->build(); 00309 learner->setTrainingSet(this->train_set); 00310 } 00311 learner->nstages = this->nstages; 00312 learner->train(); 00313 this->stage = learner->stage; 00314 } 00315 00317 // variance // 00319 void RBMDistribution::variance(Mat& covar) const 00320 { 00321 PLERROR("variance not implemented for RBMDistribution"); 00322 } 00323 00324 } // end of namespace PLearn 00325 00326 00327 /* 00328 Local Variables: 00329 mode:c++ 00330 c-basic-offset:4 00331 c-file-style:"stroustrup" 00332 c-file-offsets:((innamespace . 0)(inline-open . 0)) 00333 indent-tabs-mode:nil 00334 fill-column:79 00335 End: 00336 */ 00337 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=79 :