PLearn 0.1
|
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 :