PLearn 0.1
LinearCombinationModule.cc
Go to the documentation of this file.
00001 // -*- C++ -*-
00002 
00003 // LinearCombinationModule.cc
00004 //
00005 // Copyright (C) 2007 Yoshua Bengio
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: Yoshua Bengio
00036 
00041 #include "LinearCombinationModule.h"
00042 
00043 namespace PLearn {
00044 using namespace std;
00045 
00046 PLEARN_IMPLEMENT_OBJECT(
00047     LinearCombinationModule,
00048     "Outputs a linear combination of the input ports\n",
00049     "   output = sum_i weights[i] input_i\n"
00050     "where input_i is the provided matrix for the i-th input port\n"
00051     "and output is the resulting matrix for the output port.\n"
00052     "Hence all the ports should have the same dimensions.\n"
00053     "The weights of the linear combination could either be learned or user-defined.\n"
00054     "The input ports are names 'in_1', 'in_2', ... and the output port is named 'output'.\n"
00055     );
00056 
00057 LinearCombinationModule::LinearCombinationModule()
00058     : adaptive(false), learning_rate(0)
00059 {
00060 }
00061 
00062 void LinearCombinationModule::declareOptions(OptionList& ol)
00063 {
00064     declareOption(ol, "weights", &LinearCombinationModule::weights,
00065                   OptionBase::buildoption,
00066                   "the weights of the linear combination: a vector with one element per input port\n");
00067 
00068     declareOption(ol, "adaptive", &LinearCombinationModule::adaptive,
00069                   OptionBase::buildoption,
00070                   "whether to adapt the weights, if true they are cleared upon initialization (forget()).\n");
00071 
00072     declareOption(ol, "learning_rate", &LinearCombinationModule::learning_rate,
00073                   OptionBase::buildoption,
00074                   "Learning rate to adapt the weights by online gradient descent.\n");
00075 
00076     // Now call the parent class' declareOptions
00077     inherited::declareOptions(ol);
00078 }
00079 
00081 // build_ //
00083 void LinearCombinationModule::build_()
00084 {
00085     PLASSERT(weights.length()==0 || port_names.length()==0 ||
00086              weights.length() + 1 ==port_names.length());
00087     int n_ports=0;
00088     if (weights.length()!=0 && port_names.length()==0)
00089         // weights provided but not ports: give default port names
00090     {
00091         n_ports = weights.length() + 1;
00092         port_names.resize(n_ports);
00093         for (int i=0;i<n_ports-1;i++)
00094             port_names[i]="in_" + tostring(i+1);
00095         port_names[n_ports-1]="output";
00096     }
00097     if (weights.length()==0 && port_names.length()!=0)
00098         // ports provided but not weights: initialize weights to 0
00099     {
00100         n_ports = port_names.length();
00101         weights.resize(n_ports - 1);
00102         weights.fill(0);
00103         if (!adaptive)
00104             PLWARNING("LinearCombinationModule::build: non-adaptive weights set to 0! the module will always output 0.");
00105     }
00106 
00107     PLCHECK( learning_rate >= 0 );
00108 }
00109 
00111 // build //
00113 void LinearCombinationModule::build()
00114 {
00115     inherited::build();
00116     build_();
00117 }
00118 
00119 
00121 // makeDeepCopyFromShallowCopy //
00123 void LinearCombinationModule::makeDeepCopyFromShallowCopy(CopiesMap& copies)
00124 {
00125     inherited::makeDeepCopyFromShallowCopy(copies);
00126 
00127     deepCopyField(weights,    copies);
00128     deepCopyField(port_names, copies);
00129 }
00130 
00132 // fprop //
00134 void LinearCombinationModule::fprop(const TVec<Mat*>& ports_value)
00135 {
00136     int n_ports = weights.length() + 1;
00137     if ( n_ports < 2 )
00138         // has build completed? there should be at least one input port + the output port
00139         PLERROR("LinearCombinationModule should have at least 2 ports (one input port and one output port)\n");
00140     PLASSERT( ports_value.length() == n_ports ); // is the input coherent with expected nPorts
00141 
00142     const TVec<Mat*>& inputs = ports_value;
00143     Mat* output = ports_value[n_ports-1];
00144     if (output) {
00145         PLASSERT( output->isEmpty() );
00146         PLASSERT( inputs[0] );
00147         int mbs = inputs[0]->length();
00148         int width = inputs[0]->width();
00149         output->resize(mbs, width);
00150         output->clear();
00151         for (int i=0;i<n_ports-1;i++) {
00152             Mat* input_i = inputs[i];
00153             if (!input_i || input_i->isEmpty())
00154                 PLERROR("In LinearCombinationModule::fprop - The %d-th input "
00155                         "port is missing or empty", i);
00156             multiplyAcc(*output, *input_i, weights[i]);
00157         }
00158     }
00159 
00160     // Ensure all required ports have been computed.
00161     checkProp(ports_value);
00162 }
00163 
00165 // bpropAccUpdate //
00167 void LinearCombinationModule::bpropAccUpdate(const TVec<Mat*>& ports_value,
00168                                              const TVec<Mat*>& ports_gradient)
00169 {
00170     int n_ports = weights.length() + 1;
00171     PLASSERT( ports_value.length() == n_ports && ports_gradient.length() == n_ports);
00172 
00173     const TVec<Mat*>& input_grad = ports_gradient;
00174     Mat* output_grad = ports_gradient[n_ports-1];
00175     if (output_grad && !output_grad->isEmpty())
00176     {
00177         int mbs = output_grad->length();
00178         int width = output_grad->width();
00179         for (int i=0;i<n_ports-1;i++)
00180         {
00181             if (input_grad[i])
00182             {
00183                 PLASSERT(input_grad[i]->isEmpty() &&
00184                          input_grad[i]->width() == width);
00185                 input_grad[i]->resize(mbs,width);
00186                 multiplyAcc(*input_grad[i],*output_grad,weights[i]);
00187             }
00188             if (adaptive && learning_rate > 0)
00189             {
00190                 Mat* input_i = ports_value[i];
00191                 PLASSERT(input_i);
00192                 weights[i] -= learning_rate * dot(*output_grad,*input_i);
00193             }
00194         }
00195     }
00196 
00197     // Ensure all required gradients have been computed.
00198     checkProp(ports_gradient);
00199 }
00200 
00202 // forget //
00204 void LinearCombinationModule::forget()
00205 {
00206     if (adaptive)
00207         weights.clear();
00208 }
00209 
00211 // finalize //
00213 /* THIS METHOD IS OPTIONAL
00214 void LinearCombinationModule::finalize()
00215 {
00216 }
00217 */
00218 
00220 // bpropDoesNothing //
00222 /* THIS METHOD IS OPTIONAL
00223 // the default implementation returns false
00224 bool LinearCombinationModule::bpropDoesNothing()
00225 {
00226 }
00227 */
00228 
00230 // setLearningRate //
00232 /* OPTIONAL
00233 // The default implementation raises a warning and does not do anything.
00234 void LinearCombinationModule::setLearningRate(real dynamic_learning_rate)
00235 {
00236 }
00237 */
00238 
00240 // getPorts //
00242 const TVec<string>& LinearCombinationModule::getPorts() {
00243     return port_names;
00244 }
00245 
00247 // getPortSizes //
00249 /* Optional
00250 const TMat<int>& LinearCombinationModule::getPortSizes() {
00251 }
00252 */
00253 
00254 }
00255 // end of namespace PLearn
00256 
00257 
00258 /*
00259   Local Variables:
00260   mode:c++
00261   c-basic-offset:4
00262   c-file-style:"stroustrup"
00263   c-file-offsets:((innamespace . 0)(inline-open . 0))
00264   indent-tabs-mode:nil
00265   fill-column:79
00266   End:
00267 */
00268 // 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