PLearn 0.1
|
00001 // -*- C++ -*- 00002 00003 // NetworkModule.cc 00004 // 00005 // Copyright (C) 2007 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 00041 #include "NetworkModule.h" 00042 #include <plearn_learners/online/NullModule.h> 00043 00044 #define PL_LOG_MODULE_NAME "NetworkModule" 00045 #include <plearn/io/pl_log.h> 00046 00047 namespace PLearn { 00048 using namespace std; 00049 00050 PLEARN_IMPLEMENT_OBJECT( 00051 NetworkModule, 00052 "A module that encapsulates a whole network of underlying modules.", 00053 "The network is defined by the list of modules, and the list of\n" 00054 "connections between these modules.\n" 00055 "The network's ports are given through the 'ports' option. A typical\n" 00056 "value for 'ports' would be for instance something like:\n" 00057 " [ \"input\":\"rbm.visible\",\n" 00058 " \"target\":\"nll.target\",\n" 00059 " \"output\":\"rbm.hidden\",\n" 00060 " \"NLL\":\"nll.cost\"\n" 00061 " ]\n" 00062 "which means this module has four ports (input, target, output and NLL)\n" 00063 "which redirect to specific ports of modules in the network.\n" 00064 ); 00065 00067 // NetworkModule // 00069 NetworkModule::NetworkModule(): 00070 save_states(true) 00071 {} 00072 00074 // declareOptions // 00076 void NetworkModule::declareOptions(OptionList& ol) 00077 { 00078 declareOption(ol, "modules", &NetworkModule::modules, 00079 OptionBase::buildoption, 00080 "List of modules contained in the network."); 00081 00082 declareOption(ol, "connections", &NetworkModule::connections, 00083 OptionBase::buildoption, 00084 "List of connections between modules."); 00085 00086 declareOption(ol, "ports", &NetworkModule::ports, 00087 OptionBase::buildoption, 00088 "A sequence of pairs of strings, where each pair is of the form\n" 00089 "\"P\":\"M.N\" with 'M' the name of an underlying module, 'N' one of\n" 00090 "its ports, and 'P' the name under which the NetworkModule sees this\n" 00091 "port. See the class help for an example. If 'P' is an empty string,\n" 00092 "then the port name will be 'M.N'."); 00093 00094 declareOption(ol, "save_states", &NetworkModule::save_states, 00095 OptionBase::buildoption, 00096 "If set to 1, then any port of an underlying module whose name ends\n" 00097 "with 'state' will be automatically saved, even if it has no incoming\n" 00098 "or outgoing connection."); 00099 00100 // Now call the parent class' declareOptions 00101 inherited::declareOptions(ol); 00102 00103 // Hide unused parent options. 00104 00105 redeclareOption(ol, "input_size", &NetworkModule::input_size, 00106 OptionBase::nosave, 00107 "Not used."); 00108 00109 redeclareOption(ol, "output_size", &NetworkModule::output_size, 00110 OptionBase::nosave, 00111 "Not used."); 00112 00113 } 00114 00116 // bpropAccUpdate // 00118 void NetworkModule::bpropAccUpdate(const TVec<Mat*>& ports_value, 00119 const TVec<Mat*>& ports_gradient) 00120 { 00121 00122 // Reset internal gradients, either: 00123 // - to zero if no additional gradient is provided in 'ports_gradient' 00124 // - to the gradient provided in 'ports_gradient' otherwise 00125 PLASSERT( fprop_path.length() == bprop_path.length() ); 00126 for (int i = 0; i < bprop_path.length(); i++) { 00127 const TVec<int>& toresize = bprop_toresize[i]; 00128 int fprop_idx = fprop_data.length() - 1 - i; 00129 const TVec<Mat*>& f_data = fprop_data[fprop_idx]; 00130 for (int j = 0; j < toresize.length(); j++) { 00131 if (j == 0) { 00132 DBG_MODULE_LOG << "CLEAR: " << 00133 all_modules[bprop_path[i]]->classname() << endl; 00134 } 00135 int mat_idx = toresize[j]; 00136 DBG_MODULE_LOG << " grad = " << 00137 all_modules[bprop_path[i]]->getPortName(mat_idx) << endl; 00138 Mat* mat_toresize = bprop_data[i][mat_idx]; 00139 Mat* mat_tpl = f_data[mat_idx]; 00140 mat_toresize->resize(mat_tpl->length(), mat_tpl->width()); 00141 mat_toresize->clear(); 00142 } 00143 const TMat<int>& f_toplug = fprop_toplug[fprop_idx]; 00144 for (int j = 0; j < f_toplug.length(); j++) { 00145 int mod_idx = f_toplug(j, 1); 00146 Mat* current = bprop_data[i][mod_idx]; 00147 if (!current) 00148 // There is no gradient currently computed for this port, thus 00149 // nothing to initialize. 00150 continue; 00151 int this_idx = f_toplug(j, 0); 00152 Mat* provided = ports_gradient[this_idx]; 00153 if (provided) { 00154 // We are provided with some additional gradient, either as 00155 // input or output. If it is an output, there is nothing more 00156 // to do. However, if it is an input, we must use it to 00157 // initialize the gradient. 00158 if (!provided->isEmpty()) { 00159 PLASSERT( current->length() == provided->length() && 00160 current->width() == provided->width() ); 00161 PLASSERT( (*current)(0, 0) == 0 ); 00162 *current << *provided; 00163 } else { 00164 // Just for safety reason, crash here. It seems weird that 00165 // we may want to accumulate the gradient w.r.t. a port for 00166 // which a gradient is already computed internally (i.e. is 00167 // used in the bprop). If we want to allow this, care 00168 // should be taken as to what to do with the current value 00169 // of the gradient in 'provided' (in particular, do we use 00170 // it to update the internal paremeters?) 00171 PLASSERT( false ); 00172 } 00173 } 00174 } 00175 } 00176 00177 // Backpropagate gradient and update parameters. 00178 for (int i = 0; i < bprop_path.length(); i++) { 00179 PP<OnlineLearningModule> module = all_modules[bprop_path[i]]; 00180 DBG_MODULE_LOG << "BPROP: " << module->name << endl; 00181 // First resize some gradient matrices, so that the gradient is 00182 // properly computed. 00183 const TVec<int>& toresize = bprop_toresize[i]; 00184 const TVec<Mat*> b_data = bprop_data[i]; 00185 for (int j = 0; j < toresize.length(); j++) { 00186 int mat_idx = toresize[j]; 00187 DBG_MODULE_LOG << " grad = " << module->getPortName(mat_idx) 00188 << endl; 00189 Mat* mat_toresize = b_data[mat_idx]; 00190 PLASSERT( mat_toresize->width() > 0 ); 00191 mat_toresize->resize(0, mat_toresize->width()); 00192 } 00193 00194 // Plug in the matrices provided as parameters of this method. 00195 int fprop_idx = fprop_data.length() - 1 - i; 00196 const TVec<Mat*> f_data = fprop_data[fprop_idx]; 00197 const TMat<int>& f_toplug = fprop_toplug[fprop_idx]; 00198 for (int j = 0; j < f_toplug.length(); j++) { 00199 int this_idx = f_toplug(j, 0); 00200 int mod_idx = f_toplug(j, 1); 00201 Mat* provided = ports_gradient[this_idx]; 00202 if (provided) { 00203 Mat* current = b_data[mod_idx]; 00204 if (!current) { 00205 // We can directly plug in the provided matrix. 00206 b_data[mod_idx] = provided; 00207 } else { 00208 if (!provided->isEmpty()) { 00209 PLASSERT( !current->isEmpty() ); 00210 // This gradient is some external gradient we are 00211 // provided with. It has already been added in the step 00212 // where we reset (see above), thus there is nothing 00213 // more to do. TODO Note that it may be cleaner to 00214 // actually add it here to simplify the code above. 00215 } else { 00216 PLASSERT( current->isEmpty() ); 00217 // This gradient must be computed, but is already 00218 // computed somewhere here. We will copy it after the 00219 // update step. 00220 PLASSERT( false); // This should not happen (cf. above) 00221 } 00222 } 00223 } 00224 // Same for the fprop data since it is also given in argument. 00225 Mat* f_provided = ports_value[this_idx]; 00226 if (f_provided) { 00227 Mat* f_current = f_data[mod_idx]; 00228 if (!f_current) 00229 f_data[mod_idx] = f_provided; 00230 else { 00231 // 'f_provided' is thus the result of the computation, that 00232 // was copied from the value of 'f_current' at fprop time. 00233 // We must fill in 'f_current' with this value (once again, 00234 // this is not best for efficiency). 00235 f_current->resize(f_provided->length(), 00236 f_provided->width()); 00237 *f_current << *f_provided; 00238 } 00239 } 00240 } 00241 00242 // Then perform the bpropUpdate step. 00243 module->bpropAccUpdate( 00244 fprop_data[fprop_data.length() - 1 - i], bprop_data[i]); 00245 00246 // Restore the 'bprop_data' and 'fprop_data' parameters. 00247 for (int j = 0; j < f_toplug.length(); j++) { 00248 int this_idx = f_toplug(j, 0); 00249 int mod_idx = f_toplug(j, 1); 00250 Mat* provided = ports_gradient[this_idx]; 00251 if (provided) { 00252 Mat* current = b_data[mod_idx]; 00253 PLASSERT_MSG(current, "This should not happen: if we are " 00254 "provided with a matrix, it should have been plugged " 00255 "in here"); 00256 if (provided == current) 00257 // They are the same matrices: this means there used to be 00258 // a NULL matrix before. 00259 b_data[mod_idx] = NULL; 00260 else { 00261 // This should never happen. 00262 PLASSERT( false ); 00263 } 00264 } 00265 Mat* f_provided = ports_value[this_idx]; 00266 if (f_provided) { 00267 Mat* f_current = f_data[mod_idx]; 00268 if (f_current == f_provided) 00269 // This used to be a null pointer, that must be restored. 00270 f_data[mod_idx] = NULL; 00271 } 00272 } 00273 } 00274 } 00275 00276 00278 // build // 00280 void NetworkModule::build() 00281 { 00282 inherited::build(); 00283 build_(); 00284 } 00285 00287 // build_ // 00289 void NetworkModule::build_() 00290 { 00291 // Initialize the 'all_modules' and 'all_connections' lists. 00292 all_modules.resize(modules.length()); 00293 all_modules << modules; 00294 all_connections.resize(connections.length()); 00295 all_connections << connections; 00296 if (save_states) { 00297 // Ensure all ports which are states are correctly saved. 00298 // This is done by adding connections to a NullModule in order to force 00299 // them being saved. 00300 int count_null = 0; 00301 for (int i = 0; i < modules.length(); i++) { 00302 const TVec<string>& mod_ports = modules[i]->getPorts(); 00303 for (int j = 0; j < mod_ports.length(); j++) { 00304 const string& port = mod_ports[j]; 00305 if (port.size() >= 5 && 00306 port.substr(port.size() - 5) == "state") 00307 { 00308 // Look for either an incoming or outgoing connection. 00309 string port_name = modules[i]->name + "." + port; 00310 bool has_a_connection = false; 00311 for (int k = 0; k < connections.length(); k++) 00312 if (connections[k]->destination == port_name || 00313 connections[k]->source == port_name) 00314 { 00315 has_a_connection = true; 00316 break; 00317 } 00318 if (!has_a_connection) { 00319 // Currently we create one NullModule / state, since 00320 // multiple incoming connections are not correctly 00321 // handled. 00322 PP<OnlineLearningModule> null; 00323 /* 00324 if (all_modules.length() == modules.length()) { 00325 // No null module added yet. 00326 */ 00327 null = new NullModule("_null_" + 00328 tostring(count_null++)); 00329 all_modules.append(null); 00330 /*} else { 00331 PLASSERT( all_modules.length() == 00332 modules.length() + 1 ); 00333 null = all_modules.lastElement(); 00334 }*/ 00335 all_connections.append( new NetworkConnection( 00336 modules[i], port, null, "null", false)); 00337 } 00338 } 00339 } 00340 } 00341 } 00342 00343 // Forward random number generator to all underlying modules. 00344 if (random_gen) { 00345 for (int i = 0; i < all_modules.length(); i++) { 00346 if (!all_modules[i]->random_gen) { 00347 all_modules[i]->random_gen = random_gen; 00348 all_modules[i]->build(); 00349 all_modules[i]->forget(); 00350 } 00351 } 00352 } 00353 00354 // Construct fprop and bprop paths from the list of modules and 00355 // connections. 00356 00357 // First create a dictionary of modules. 00358 map<string, PP<OnlineLearningModule> > name_to_module; 00359 for (int i = 0; i < all_modules.length(); i++) { 00360 string module = all_modules[i]->name; 00361 if (name_to_module.find(module) != name_to_module.end()) { 00362 // There is already a module with the same name. For safety 00363 // reasons, we make it point to a NULL pointer to ensure we do not 00364 // accidentally use the wrong module. 00365 PLWARNING("In NetworkModule::build_ - There are two modules with " 00366 "the same name '%s': any connection attempting to use " 00367 "one of these modules will probably crash", 00368 module.c_str()); 00369 name_to_module[module] = NULL; 00370 } else 00371 name_to_module[module] = all_modules[i]; 00372 } 00373 00374 // Initialize connections. 00375 for (int i = 0; i < all_connections.length(); i++) 00376 all_connections[i]->initialize(name_to_module); 00377 00378 // Preprocess some convenience data structures from the list of 00379 // connections. 00380 00381 // 'module_to_index' maps module pointers to their corresponding index in 00382 // the 'all_modules' list. 00383 map<const OnlineLearningModule*, int> module_to_index; 00384 for (int i = 0; i < all_modules.length(); i++) 00385 module_to_index[all_modules[i]] = i; 00386 00387 // Analyze the list of ports. 00388 // The 'port_correspondances' lists, for each module, the correspondances 00389 // between the modules' ports and the ports of the NetworkModule. 00390 TVec< TMat<int> > port_correspondances(all_modules.length()); 00391 for (int i = 0; i < all_modules.length(); i++) 00392 port_correspondances[i].resize(0, 2); 00393 TVec<int> new_row(2); 00394 all_ports.resize(0); 00395 port_sizes.resize(0, 2); 00396 int n_ports = ports.length(); 00397 for (int i = 0; i < n_ports; i++) { 00398 const pair<string, string>& new_old = ports[i]; 00399 string new_name = new_old.first; 00400 const string& old_name = new_old.second; 00401 if (new_name.empty()) 00402 // Empty string: we use the full name of the form 'module.port'. 00403 new_name = old_name; 00404 all_ports.append(new_name); 00405 size_t dot_pos = old_name.find('.'); 00406 PLASSERT( dot_pos != string::npos ); 00407 string old_module_name = old_name.substr(0, dot_pos); 00408 string old_module_port = old_name.substr(dot_pos + 1); 00409 if ( name_to_module.find(old_module_name) == name_to_module.end()) 00410 PLERROR("NetworkModule::Can't find module with name %s\n",old_module_name.c_str()); 00411 00412 new_row[0] = i; 00413 PP<OnlineLearningModule> old_module = name_to_module[old_module_name]; 00414 new_row[1] = old_module->getPortIndex(old_module_port); 00415 port_correspondances[module_to_index[old_module]].appendRow(new_row); 00416 new_row[0] = old_module->getPortLength(old_module_port); 00417 new_row[1] = old_module->getPortWidth(old_module_port); 00418 port_sizes.appendRow(new_row); 00419 } 00420 00421 // The i-th element of 'in_connections' maps each port in the i-th module 00422 // to the connection that has it as destination (there may be only one). 00423 TVec< map<string, PP<NetworkConnection> > > in_connections; 00424 in_connections.resize(all_modules.length()); 00425 // The i-th element of 'out_connections' maps each port in the i-th module 00426 // to the connections that have it as source (there may be many). 00427 TVec< map<string, TVec< PP<NetworkConnection> > > > out_connections; 00428 out_connections.resize(all_modules.length()); 00429 // The 'inputs_needed' vector contains the number of inputs that must be 00430 // fed to a module before it can compute a fprop. 00431 TVec<int> inputs_needed(all_modules.length(), 0); 00432 // The 'compute_input_of' list gives, for each module M, the indices of 00433 // other modules that take an output of M as input. 00434 TVec< TVec<int> > compute_input_of(all_modules.length()); 00435 for (int i = 0; i < all_connections.length(); i++) { 00436 PP<NetworkConnection> connection = all_connections[i]; 00437 int src = module_to_index[connection->getSourceModule()]; 00438 int dest = module_to_index[connection->getDestinationModule()]; 00439 inputs_needed[dest]++; 00440 compute_input_of[src].append(dest); 00441 map<string, PP<NetworkConnection> >& in_conn = in_connections[dest]; 00442 if (in_conn.find(connection->getDestinationPort()) != in_conn.end()) { 00443 /* 00444 // The port has more than one incoming connection. Currently, we 00445 // only allow this to happen if the module is a NullModule (this is 00446 // for safety). If in the future we want more modules to allow 00447 // multiple incoming connections, we may get rid of this error. 00448 NullModule* test = dynamic_cast<NullModule*>( 00449 get_pointer(connection->getDestinationModule())); 00450 */ 00451 // TODO ACTUALLY this does not work, as the fprop/bprop paths may 00452 // not be correctly computed in the case of multiple incoming 00453 // connections. Until this is fixed, multiple incoming connections 00454 // are not allowed. 00455 bool test = false; 00456 if (!test) 00457 PLERROR("In LearningNetwork::build_ - A port may have only one " 00458 "incoming connection"); 00459 } 00460 in_conn[connection->getDestinationPort()] = connection; 00461 out_connections[src][connection->getSourcePort()].append(connection); 00462 } 00463 // The fprop and bprop paths can now be computed. 00464 fprop_path.resize(0); 00465 bprop_path.resize(all_modules.length()); 00466 bprop_path.fill(-1); 00467 TVec<bool> is_done(all_modules.length(), false); 00468 fprop_data.resize(0); 00469 fprop_data_idx.resize(0); 00470 bprop_data.resize(all_modules.length()); 00471 bprop_data_idx.resize(all_modules.length()); 00472 fprop_toresize.resize(0); 00473 bprop_toresize.resize(all_modules.length()); 00474 fprop_toplug.resize(0); 00475 // This is getting a little hackish here... If not enough memory is 00476 // allocated to store the work matrices, then when appending a new Mat to 00477 // 'all_mats', we will have to create a new Storage and copy previous data. 00478 // The problem is then that the pointers in 'fprop_data' and 'bprop_data' 00479 // will become invalid. So an easy fix is to allocate enough memory to 00480 // ensure we never have to resize the Storage later. 00481 int max_n_mats = 1000; 00482 all_mats.resize(max_n_mats); 00483 all_mats.resize(0); 00484 // A vector that stores the index of a module in the fprop path. 00485 TVec<int> module_index_to_path_index(all_modules.length(), -1); 00486 while (is_done.find(false) >= 0) { 00487 bool at_least_one_done = false; 00488 for (int i = 0; i < all_modules.length(); i++) { 00489 if (!is_done[i] && inputs_needed[i] == 0) { 00490 for (int j = 0; j < compute_input_of[i].length(); j++) 00491 inputs_needed[compute_input_of[i][j]]--; 00492 // Save any correspondance between ports of this module and 00493 // ports of the encapsulating NetworkModule. 00494 fprop_toplug.append(port_correspondances[i]); 00495 // Compute the list of matrices that must be provided to this 00496 // module when doing a fprop and bprop. 00497 TVec<string> mod_ports = all_modules[i]->getPorts(); 00498 map<string, PP<NetworkConnection> >& in_conn = 00499 in_connections[i]; 00500 map<string, TVec< PP<NetworkConnection> > >& out_conn = 00501 out_connections[i]; 00502 TVec<int> fprop_tores; 00503 TVec<int> bprop_tores; 00504 TVec<Mat*> fprop_mats; 00505 TVec<Mat*> bprop_mats; 00506 TVec<int> fprop_mats_idx; 00507 TVec<int> bprop_mats_idx; 00508 int n_in_conn = in_conn.size(); 00509 int n_in_conn_found = 0; 00510 for (int j = 0; j < mod_ports.length(); j++) { 00511 int count_inc_conn = in_conn.count(mod_ports[j]); 00512 if (count_inc_conn > 0) { 00513 // This port has an incoming connection: it is thus an 00514 // input, and the corresponding matrices for storing 00515 // its value and gradient are found by looking at the 00516 // source port of the connection. 00517 n_in_conn_found += count_inc_conn; 00518 PP<NetworkConnection> conn = in_conn[mod_ports[j]]; 00519 int src_mod = module_to_index[conn->getSourceModule()]; 00520 int path_index = module_index_to_path_index[src_mod]; 00521 int port_index = conn->getSourceModule()->getPortIndex( 00522 conn->getSourcePort()); 00523 if (port_index < 0) 00524 PLERROR("In NetworkModule::build_ - no port named" 00525 " \"%s\"\n" 00526 "in module \"%s\". Valid port names are:\n" 00527 "%s\n", 00528 conn->getSourcePort().c_str(), 00529 conn->getSourceModule()->name.c_str(), 00530 tostring(conn->getSourceModule() 00531 ->getPorts()).c_str() 00532 ); 00533 fprop_mats.append(fprop_data[path_index][port_index]); 00534 fprop_mats_idx.append( 00535 fprop_data_idx[path_index][port_index]); 00536 if (!conn->propagate_gradient) { 00537 // This connection does not propagate the gradient, 00538 // and thus we do not want to accumulate it. 00539 bprop_mats.append(NULL); 00540 bprop_mats_idx.append(-1); 00541 } else { 00542 int b_idx = all_modules.length() - 1 - path_index; 00543 bprop_mats.append(bprop_data[b_idx][port_index]); 00544 bprop_mats_idx.append( 00545 bprop_data_idx[b_idx][port_index]); 00546 bprop_tores.append(j); 00547 } 00548 PLASSERT_MSG( 00549 out_conn.find(mod_ports[j]) == out_conn.end(), 00550 "A port with an incoming connection cannot " 00551 "also have an outgoing connection" ); 00552 } else if (out_conn.find(mod_ports[j]) != out_conn.end()) { 00553 // This port has (at least) one outgoing connection: it 00554 // is thus an output, and it must be provided with 00555 // matrices to store its value (and gradient if the 00556 // connection propagates it). 00557 all_mats.append(Mat()); 00558 if (all_mats.length() > max_n_mats) 00559 PLERROR("In NetworkModule::build_ - Will need to " 00560 "increase 'max_n_mats'"); 00561 Mat* new_mat = &all_mats.lastElement(); 00562 fprop_mats.append(new_mat); 00563 fprop_mats_idx.append(all_mats.length() - 1); 00564 fprop_tores.append(j); 00565 // Ensure there exists a connection propagating the 00566 // gradient to this port. 00567 bool must_store_grad = false; 00568 const TVec< PP<NetworkConnection> >& out_j = 00569 out_conn[mod_ports[j]]; 00570 for (int k = 0; k < out_j.length(); k++) 00571 if (out_j[k]->propagate_gradient) { 00572 must_store_grad = true; 00573 break; 00574 } 00575 if (must_store_grad) { 00576 all_mats.append(Mat()); 00577 if (all_mats.length() > max_n_mats) 00578 PLERROR("In NetworkModule::build_ - Will need " 00579 "to increase 'max_n_mats'"); 00580 new_mat = &all_mats.lastElement(); 00581 bprop_mats.append(new_mat); 00582 bprop_mats_idx.append(all_mats.length() - 1); 00583 } else { 00584 // No connection propagating gradient to this port. 00585 bprop_mats.append(NULL); 00586 bprop_mats_idx.append(-1); 00587 } 00588 } else { 00589 // This port is not used (we do not provide its value, 00590 // and we do not care about obtaining it). 00591 fprop_mats.append(NULL); 00592 bprop_mats.append(NULL); 00593 fprop_mats_idx.append(-1); 00594 bprop_mats_idx.append(-1); 00595 } 00596 } 00597 if (n_in_conn != n_in_conn_found) 00598 PLERROR("In NetworkModule::build_ - Not all incoming " 00599 "connections were found for module '%s', please " 00600 "ensure all destination ports do exist", 00601 all_modules[i]->name.c_str()); 00602 module_index_to_path_index[i] = fprop_path.length(); 00603 // Update fprop path. 00604 PLASSERT( fprop_mats.length() == fprop_mats_idx.length() ); 00605 fprop_data.append(fprop_mats); 00606 fprop_data_idx.append(fprop_mats_idx); 00607 fprop_toresize.append(fprop_tores); 00608 fprop_path.append(i); 00609 // Update bprop path. 00610 PLASSERT( bprop_mats_idx.length() == bprop_mats.length() ); 00611 int bprop_idx = bprop_path.length() - fprop_path.length(); 00612 bprop_data[bprop_idx] = bprop_mats; 00613 bprop_data_idx[bprop_idx] = bprop_mats_idx; 00614 bprop_toresize[bprop_idx] = bprop_tores; 00615 bprop_path[bprop_idx] = i; 00616 00617 is_done[i] = true; 00618 at_least_one_done = true; 00619 } 00620 } 00621 if (!at_least_one_done) { 00622 // Infinite loop: the algorithm cannot find a module that can be 00623 // propagated at this point. 00624 string err; 00625 for (int i = 0; i < all_modules.length(); i++) 00626 if (!is_done[i]) 00627 err += all_modules[i]->name + ", "; 00628 PLERROR("In NetworkModule::build_ - Cannot build propagation path " 00629 "due to the following modules waiting for incoming " 00630 "connections (there may be an infinite loop): %s", 00631 err.c_str()); 00632 } 00633 } 00634 PLASSERT( module_index_to_path_index.find(-1) == -1 ); 00635 } 00636 00638 // forget // 00640 void NetworkModule::forget() 00641 { 00642 // Forward forget to the underlying modules. 00643 for (int i = 0; i < all_modules.length(); i++) { 00644 PLASSERT( all_modules[i]->random_gen ); 00645 all_modules[i]->forget(); 00646 } 00647 } 00648 00650 // fprop // 00652 void NetworkModule::fprop(const Vec& input, Vec& output) const 00653 { 00654 PLERROR("In NetworkModule::fprop - Not implemented for single vector " 00655 "operations"); 00656 } 00657 00658 void NetworkModule::fprop(const TVec<Mat*>& ports_value) { 00659 for (int i = 0; i < fprop_path.length(); i++) { 00660 PP<OnlineLearningModule> module = all_modules[fprop_path[i]]; 00661 DBG_MODULE_LOG << "FPROP: " << module->name << endl; 00662 00663 // First resize some data matrices, so that the outputs are properly 00664 // computed. 00665 const TVec<int>& toresize = fprop_toresize[i]; 00666 const TVec<Mat*>& data = fprop_data[i]; 00667 for (int j = 0; j < toresize.length(); j++) { 00668 DBG_MODULE_LOG << " out = " << module->getPortName(toresize[j]) 00669 << endl; 00670 data[toresize[j]]->resize(0, 0); 00671 } 00672 00673 // Then plug in the extra data provided in this method's fprop. 00674 const TMat<int>& toplug = fprop_toplug[i]; 00675 for (int j = 0; j < toplug.length(); j++) { 00676 int swap_1 = toplug(j, 0); 00677 Mat* provided = ports_value[swap_1]; 00678 if (!provided) 00679 // There is no provided matrix for this port, thus no extra 00680 // information to use or compute. 00681 continue; 00682 int swap_2 = toplug(j, 1); 00683 Mat* current = data[swap_2]; 00684 if (!provided->isEmpty()) { 00685 // A full matrix is provided. This means the data is readily 00686 // available (= input), and thus should not have been computed 00687 // anywhere else. 00688 PLASSERT( !current ); 00689 data[swap_2] = provided; 00690 } else { 00691 // An empty matrix is provided. This means we want to compute 00692 // this data. If it is already computed in the network, we do a 00693 // copy (may not be the most efficient way to do it though). If 00694 // it is not already computed, we can directly plug the 00695 // provided matrix. 00696 if (!current) { 00697 data[swap_2] = provided; 00698 } else { 00699 // This data should be something we compute. 00700 PLASSERT( current->isEmpty() ); 00701 } 00702 } 00703 } 00704 00705 // Now perform fprop of the i-th module. 00706 module->fprop(data); 00707 00708 // And restore 'data', in addition to copy any computed value that 00709 // needs to be retrieved. 00710 for (int j = 0; j < toplug.length(); j++) { 00711 int swap_1 = toplug(j, 0); 00712 Mat* provided = ports_value[swap_1]; 00713 if (!provided) 00714 continue; 00715 int swap_2 = toplug(j, 1); 00716 Mat* current = data[swap_2]; 00717 if (!provided->isEmpty()) { 00718 // This is either a matrix given as input, or directly plugged 00719 // in the network. In both cases, the old value in 'data' was a 00720 // null pointer. 00721 PLASSERT( current == provided ); 00722 data[swap_2] = NULL; 00723 } else { 00724 // This is an output we need to copy. 00725 PLASSERT( current && !current->isEmpty() ); 00726 provided->resize(current->length(), current->width()); 00727 *provided << *current; 00728 } 00729 } 00730 } 00731 } 00732 00734 // getPorts // 00736 const TVec<string>& NetworkModule::getPorts() 00737 { 00738 return all_ports; 00739 } 00740 00742 // getPortSizes // 00744 const TMat<int>& NetworkModule::getPortSizes() 00745 { 00746 return port_sizes; 00747 } 00748 00750 // makeDeepCopyFromShallowCopy // 00752 void NetworkModule::makeDeepCopyFromShallowCopy(CopiesMap& copies) 00753 { 00754 inherited::makeDeepCopyFromShallowCopy(copies); 00755 00756 deepCopyField(modules, copies); 00757 deepCopyField(connections, copies); 00758 deepCopyField(ports, copies); 00759 deepCopyField(all_modules, copies); 00760 deepCopyField(all_connections, copies); 00761 deepCopyField(fprop_path, copies); 00762 deepCopyField(bprop_path, copies); 00763 deepCopyField(fprop_data_idx, copies); 00764 deepCopyField(fprop_toresize, copies); 00765 deepCopyField(fprop_toplug, copies); 00766 deepCopyField(bprop_data_idx, copies); 00767 deepCopyField(bprop_toresize, copies); 00768 deepCopyField(all_mats, copies); 00769 deepCopyField(all_ports, copies); 00770 00771 // Special code to handle the deep copy of 'fprop_data' and 'bprop_data'. 00772 // A better way may exist to do this! 00773 fprop_data = TVec< TVec<Mat*> >(fprop_data.length()); 00774 bprop_data = TVec< TVec<Mat*> >(bprop_data.length()); 00775 PLASSERT( fprop_data.length() == bprop_data.length() ); 00776 for (int i = 0; i < fprop_data.length(); i++) { 00777 TVec<Mat*>& fdata = fprop_data[i]; 00778 TVec<Mat*>& bdata = bprop_data[i]; 00779 const TVec<int>& fdata_idx = fprop_data_idx[i]; 00780 const TVec<int>& bdata_idx = bprop_data_idx[i]; 00781 fdata.resize(fdata_idx.length()); 00782 bdata.resize(bdata_idx.length()); 00783 for (int k = 0; k < fdata_idx.length(); k++) { 00784 int idx = fdata_idx[k]; 00785 if (idx == -1) 00786 fdata[k] = NULL; 00787 else 00788 fdata[k] = &all_mats[idx]; 00789 } 00790 for (int k = 0; k < bdata_idx.length(); k++) { 00791 int idx = bdata_idx[k]; 00792 if (idx == -1) 00793 bdata[k] = NULL; 00794 else 00795 bdata[k] = &all_mats[idx]; 00796 } 00797 } 00798 } 00799 00800 00801 } // end of namespace PLearn 00802 00803 00804 /* 00805 Local Variables: 00806 mode:c++ 00807 c-basic-offset:4 00808 c-file-style:"stroustrup" 00809 c-file-offsets:((innamespace . 0)(inline-open . 0)) 00810 indent-tabs-mode:nil 00811 fill-column:79 00812 End: 00813 */ 00814 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=79 :