PLearn 0.1
|
00001 // -*- C++ -*- 00002 00003 // SplitModule.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 "SplitModule.h" 00042 00043 namespace PLearn { 00044 using namespace std; 00045 00046 PLEARN_IMPLEMENT_OBJECT( 00047 SplitModule, 00048 "Split a single down-port into multiple up-ports or concatenate the up-ports into the down-port", 00049 "There is a single down-port and multiple up-ports.\n" 00050 "After an fprop, we always have that the down-port\n" 00051 "is the horizontal concatenation of the up-ports.\n" 00052 "When the down-port is input and the up-ports are output\n" 00053 "the module SPLITS the input into its output. When the\n" 00054 "up-ports are input and the down-port is output, the module\n" 00055 "CONCATENATES the up-ports into the down-port.\n" 00056 "The user specifies names and sizes of each port.\n" 00057 "The port values are expected to be matrices of the\n" 00058 "same length. The splitting or concatenation operations\n" 00059 "can be seen as performed on each row of these matrices.\n" 00060 ); 00061 00063 // SplitModule // 00065 SplitModule::SplitModule() 00066 : down_port_name("down_port") 00067 { 00068 } 00069 00071 // declareOptions // 00073 void SplitModule::declareOptions(OptionList& ol) 00074 { 00075 declareOption(ol, "down_port_name", &SplitModule::down_port_name, 00076 OptionBase::buildoption, 00077 "Name of the down-port. Default = 'down_port'."); 00078 00079 declareOption(ol, "up_port_sizes", &SplitModule::up_port_sizes, 00080 OptionBase::buildoption, 00081 "Sizes of the up-ports. Mandatory.\n"); 00082 00083 declareOption(ol, "up_port_names", &SplitModule::up_port_names, 00084 OptionBase::buildoption, 00085 "Names of the up-ports. Default = ['up_port_1','up_port_2',...]."); 00086 00087 // Now call the parent class' declareOptions 00088 inherited::declareOptions(ol); 00089 00090 redeclareOption(ol, "input_size", &SplitModule::input_size, 00091 OptionBase::learntoption, 00092 "input_size = down_port's size = sum(up_ports_size) but is set automatically."); 00093 00094 redeclareOption(ol, "output_size", &SplitModule::output_size, 00095 OptionBase::learntoption, 00096 "output_size = sum(up_ports_size) but is set automatically."); 00097 } 00098 00100 // build_ // 00102 void SplitModule::build_() 00103 { 00104 int n_up_ports = up_port_sizes.length(); 00105 if (n_up_ports>0) // build cannot be completed until then 00106 { 00107 if (up_port_names.length()==0) // set default up_port_names 00108 { 00109 up_port_names.resize(n_up_ports); 00110 for (int i=0;i<n_up_ports;i++) 00111 up_port_names[i] = "up_port_" + tostring(i+1); 00112 } 00113 input_size = output_size = sum(up_port_sizes); 00114 } 00115 } 00116 00117 // ### Nothing to add here, simply calls build_ 00118 void SplitModule::build() 00119 { 00120 inherited::build(); 00121 build_(); 00122 } 00123 00124 00125 void SplitModule::makeDeepCopyFromShallowCopy(CopiesMap& copies) 00126 { 00127 inherited::makeDeepCopyFromShallowCopy(copies); 00128 00129 deepCopyField(up_port_sizes, copies); 00130 deepCopyField(up_port_names, copies); 00131 } 00132 00134 // fprop // 00136 void SplitModule::fprop(const TVec<Mat*>& ports_value) 00137 { 00138 PLASSERT( ports_value.length() == nPorts() ); 00139 PLASSERT_MSG( up_port_sizes.length()>0, "SplitModule should have up_port_sizes specified with length>0"); 00140 PLASSERT_MSG( ports_value[0], "SplitModule's down_port should always be connected.\n"); 00141 if (!ports_value[0]->isEmpty()) // the down_port is an input, do a SPLIT 00142 { 00143 int start=0; 00144 Mat& input = *ports_value[0]; 00145 for (int i=0;i<up_port_sizes.length();i++) 00146 { 00147 int width = up_port_sizes[i]; 00148 if (ports_value[i+1]) 00149 { 00150 PLASSERT_MSG(ports_value[i+1]->isEmpty(),"In SplitModule, when the down_port is an input, the up_ports should either be outputs or not connected.\n"); 00151 ports_value[i+1]->resize(input.length(),width); 00152 *ports_value[i+1] << input.subMatColumns(start,width); 00153 } 00154 start += width; 00155 } 00156 return; 00157 } else // the down_port is an output, do a CONCATENATE 00158 { 00159 int start=0; 00160 Mat& output = *ports_value[0]; 00161 // hacky check so that the resize does not fail 00162 PLASSERT_MSG(ports_value[1] && !ports_value[1]->isEmpty(), "In SplitModule, when the down_port is an output, all up_ports should be connected.\n"); 00163 output.resize(ports_value[1]->length(),input_size); 00164 for (int i=0;i<up_port_sizes.length();i++) 00165 { 00166 int width = up_port_sizes[i]; 00167 PLASSERT_MSG(ports_value[i+1] && !ports_value[i+1]->isEmpty(), "In SplitModule, when the down_port is an output, all up_ports should be connected.\n"); 00168 output.subMatColumns(start,width) << *ports_value[i+1]; 00169 start += width; 00170 } 00171 return; 00172 } 00173 PLERROR("In SplitModule::fprop - Not implemented for class " 00174 "'%s'", classname().c_str()); 00175 } 00176 00178 // bpropUpdate // 00180 00181 00182 void SplitModule::bpropAccUpdate(const TVec<Mat*>& ports_value, 00183 const TVec<Mat*>& ports_gradient) 00184 { 00185 PLASSERT( ports_value.length() == nPorts() && ports_gradient.length() == nPorts()); 00186 PLASSERT_MSG( up_port_sizes.length()>0, "SplitModule should have up_port_sizes specified with length>0"); 00187 if (ports_gradient[0] && ports_gradient[0]->isEmpty()) // the down_port is an input, do a backprop on SPLIT 00188 { 00189 int start=0; 00190 Mat& input_gradient = *ports_gradient[0]; 00191 for (int i=0;i<up_port_sizes.length();i++) 00192 { 00193 int width = up_port_sizes[i]; 00194 PLASSERT_MSG( ports_gradient[i+1] && 00195 !ports_gradient[i+1]->isEmpty(), 00196 "In SplitModule::bpropAccUpdate - When the down_port" 00197 " is an input and its gradient is required, one must" 00198 " provide a gradient on the up_ports" ); 00199 input_gradient.resize(ports_gradient[i+1]->length(), 00200 input_gradient.width()); 00201 input_gradient.subMatColumns(start, width) += *ports_gradient[i+1]; 00202 start += width; 00203 } 00204 return; 00205 } 00206 bool one_of_the_up_ports_wants_a_gradient = false; 00207 for (int i=0;i<up_port_sizes.length();i++) 00208 if (ports_gradient[i+1] && ports_gradient[i+1]->isEmpty()) 00209 { 00210 one_of_the_up_ports_wants_a_gradient = true; 00211 break; 00212 } 00213 if (one_of_the_up_ports_wants_a_gradient) 00214 // the down_port is an output, do a CONCATENATE 00215 { 00216 int start=0; 00217 Mat& output_gradient = *ports_gradient[0]; 00218 00219 for (int i=0;i<up_port_sizes.length();i++) 00220 { 00221 int width = up_port_sizes[i]; 00222 if (ports_gradient[i+1] && ports_gradient[i+1]->isEmpty()) 00223 { 00224 ports_gradient[i+1]->resize(output_gradient.length(),width); 00225 (*ports_gradient[i+1]) += output_gradient.subMatColumns(start,width); 00226 } 00227 start += width; 00228 } 00229 return; 00230 } 00231 //PLERROR("In SplitModule::fprop - This configuration of ports not implemented for class " 00232 // "'%s'", classname().c_str()); 00233 } 00234 00236 // getPorts // 00238 const TVec<string>& SplitModule::getPorts() { 00239 if (port_names.isEmpty()) 00240 { 00241 port_names.resize(nPorts()); 00242 port_names[0] = down_port_name; 00243 port_names.subVec(1,nPorts()-1) << up_port_names; 00244 } 00245 return port_names; 00246 } 00247 00249 // getPortSizes // 00251 const TMat<int>& SplitModule::getPortSizes() { 00252 if (sizes.isEmpty()) 00253 { 00254 sizes.resize(nPorts(),2); 00255 sizes.column(0).fill(-1); 00256 sizes(0,1) = input_size; 00257 sizes.subMat(1,1,nPorts()-1,1) << up_port_sizes.toMat(nPorts()-1,1); 00258 } 00259 return sizes; 00260 } 00261 00263 // nPorts // 00265 int SplitModule::nPorts() 00266 { 00267 return 1+up_port_sizes.length(); 00268 } 00269 00270 00271 } 00272 // end of namespace PLearn 00273 00274 00275 /* 00276 Local Variables: 00277 mode:c++ 00278 c-basic-offset:4 00279 c-file-style:"stroustrup" 00280 c-file-offsets:((innamespace . 0)(inline-open . 0)) 00281 indent-tabs-mode:nil 00282 fill-column:79 00283 End: 00284 */ 00285 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=79 :