PLearn 0.1
RBMDistribution.cc
Go to the documentation of this file.
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 :
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines