PLearn 0.1
|
00001 // -*- C++ -*- 00002 00003 // BackConvolution2DModule.cc 00004 // 00005 // Copyright (C) 2006 Pascal Lamblin 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: Pascal Lamblin 00036 00040 #define PL_LOG_MODULE_NAME "BackConvolution2DModule" 00041 00042 #include "BackConvolution2DModule.h" 00043 #include <plearn/math/convolutions.h> 00044 #include <plearn/math/TMat_maths.h> 00045 #include <plearn/io/pl_log.h> 00046 00047 namespace PLearn { 00048 using namespace std; 00049 00050 PLEARN_IMPLEMENT_OBJECT( 00051 BackConvolution2DModule, 00052 "Transpose of a Convolution2DModule", 00053 ""); 00054 00055 BackConvolution2DModule::BackConvolution2DModule() : 00056 n_input_images(1), 00057 input_images_length(-1), 00058 input_images_width(-1), 00059 n_output_images(1), 00060 kernel_length(-1), 00061 kernel_width(-1), 00062 kernel_step1(1), 00063 kernel_step2(1), 00064 start_learning_rate(0.), 00065 decrease_constant(0.), 00066 output_images_length(-1), 00067 output_images_width(-1), 00068 input_images_size(-1), 00069 output_images_size(-1), 00070 kernel_size(-1), 00071 learning_rate(0.), 00072 step_number(0) 00073 { 00074 } 00075 00076 void BackConvolution2DModule::declareOptions(OptionList& ol) 00077 { 00078 // declareOption(ol, "myoption", &BackConvolution2DModule::myoption, 00079 // OptionBase::buildoption, 00080 // "Help text describing this option"); 00081 00082 declareOption(ol, "n_input_images", 00083 &BackConvolution2DModule::n_input_images, 00084 OptionBase::buildoption, 00085 "Number of input images present at the same time in the" 00086 " input vector"); 00087 00088 declareOption(ol, "input_images_length", 00089 &BackConvolution2DModule::input_images_length, 00090 OptionBase::buildoption, 00091 "Length of each of the input images"); 00092 00093 declareOption(ol, "input_images_width", 00094 &BackConvolution2DModule::input_images_width, 00095 OptionBase::buildoption, 00096 "Width of each of the input images"); 00097 00098 declareOption(ol, "n_output_images", 00099 &BackConvolution2DModule::n_output_images, 00100 OptionBase::buildoption, 00101 "Number of output images to put in the output vector"); 00102 00103 declareOption(ol, "kernel_length", 00104 &BackConvolution2DModule::kernel_length, 00105 OptionBase::buildoption, 00106 "Length of each filter (or kernel) applied on an input image" 00107 ); 00108 00109 declareOption(ol, "kernel_width", &BackConvolution2DModule::kernel_width, 00110 OptionBase::buildoption, 00111 "Width of each filter (or kernel) applied on an input image" 00112 ); 00113 00114 declareOption(ol, "kernel_step1", &BackConvolution2DModule::kernel_step1, 00115 OptionBase::buildoption, 00116 "Horizontal step of the kernels"); 00117 00118 declareOption(ol, "kernel_step2", &BackConvolution2DModule::kernel_step2, 00119 OptionBase::buildoption, 00120 "Vertical step of the kernels"); 00121 00122 declareOption(ol, "connection_matrix", 00123 &BackConvolution2DModule::connection_matrix, 00124 OptionBase::buildoption, 00125 "Matrix of connections:\n" 00126 "it has n_input_images rows and n_output_images columns,\n" 00127 "each output image will only be connected to a subset of" 00128 " the\n" 00129 "input images, where a non-zero value is present in this" 00130 " matrix.\n" 00131 "If this matrix is not provided, it will be fully" 00132 " connected.\n" 00133 ); 00134 00135 declareOption(ol, "start_learning_rate", 00136 &BackConvolution2DModule::start_learning_rate, 00137 OptionBase::buildoption, 00138 "Starting learning-rate, by which we multiply the gradient" 00139 " step" 00140 ); 00141 00142 declareOption(ol, "decrease_constant", 00143 &BackConvolution2DModule::decrease_constant, 00144 OptionBase::buildoption, 00145 "learning_rate = start_learning_rate / (1 +" 00146 " decrease_constant*t),\n" 00147 "where t is the number of updates since the beginning\n" 00148 ); 00149 00150 declareOption(ol, "output_images_length", 00151 &BackConvolution2DModule::output_images_length, 00152 OptionBase::learntoption, 00153 "Length of the output images"); 00154 00155 declareOption(ol, "output_images_width", 00156 &BackConvolution2DModule::output_images_width, 00157 OptionBase::learntoption, 00158 "Width of the output images"); 00159 00160 declareOption(ol, "kernels", &BackConvolution2DModule::kernels, 00161 OptionBase::learntoption, 00162 "Contains the kernels between input and output images"); 00163 00164 declareOption(ol, "bias", &BackConvolution2DModule::bias, 00165 OptionBase::learntoption, 00166 "Contains the bias of the output images"); 00167 00168 00169 // Now call the parent class' declareOptions 00170 inherited::declareOptions(ol); 00171 00172 // Redeclare some of the parent's options as learntoptions 00173 redeclareOption(ol, "input_size", &BackConvolution2DModule::input_size, 00174 OptionBase::learntoption, 00175 "Size of the input, computed from n_input_images,\n" 00176 "n_input_length and n_input_width.\n"); 00177 00178 redeclareOption(ol, "output_size", &BackConvolution2DModule::output_size, 00179 OptionBase::learntoption, 00180 "Size of the output, computed from n_output_images,\n" 00181 "n_output_length and n_output_width.\n"); 00182 } 00183 00184 void BackConvolution2DModule::build_() 00185 { 00186 MODULE_LOG << "build_() called" << endl; 00187 00188 // Verify the parameters 00189 if( n_input_images < 1 ) 00190 PLERROR("BackConvolution2DModule::build_: 'n_input_images'<1 (%i).\n", 00191 n_input_images); 00192 00193 if( input_images_length < 0 ) 00194 PLERROR("BackConvolution2DModule::build_: 'input_images_length'<0" 00195 " (%i).\n", 00196 input_images_length); 00197 00198 if( input_images_width < 0 ) 00199 PLERROR("BackConvolution2DModule::build_: 'input_images_width'<0" 00200 " (%i).\n", 00201 input_images_width); 00202 00203 if( n_output_images < 1 ) 00204 PLERROR("BackConvolution2DModule::build_: 'n_output_images'<1 (%i).\n", 00205 n_input_images); 00206 00207 if( kernel_length < 0 ) 00208 PLERROR("BackConvolution2DModule::build_: 'kernel_length'<0 (%i).\n", 00209 kernel_length); 00210 00211 if( kernel_width < 0 ) 00212 PLERROR("BackConvolution2DModule::build_: 'kernel_width'<0 (%i).\n", 00213 kernel_width); 00214 00215 if( kernel_step1 < 0 ) 00216 PLERROR("BackConvolution2DModule::build_: 'kernel_step1'<0 (%i).\n", 00217 kernel_step1); 00218 00219 if( kernel_step2 < 0 ) 00220 PLERROR("BackConvolution2DModule::build_: 'kernel_step2'<0 (%i).\n", 00221 kernel_step2); 00222 00223 // Build the learntoptions from the buildoptions 00224 input_images_size = input_images_length * input_images_width; 00225 input_size = n_input_images * input_size; 00226 00227 output_images_length = kernel_step1*(input_images_length-1)+kernel_length; 00228 output_images_width = kernel_step2*(input_images_width - 1) + kernel_width; 00229 output_images_size = output_images_length * output_images_width; 00230 00231 kernel_size = kernel_length * kernel_width; 00232 00233 bias.resize(n_output_images); 00234 00235 // If connection_matrix was not specified, or inconsistently, 00236 // make it a matrix full of ones. 00237 if( connection_matrix.length() != n_input_images 00238 || connection_matrix.width() != n_output_images ) 00239 { 00240 connection_matrix.resize(n_input_images, n_output_images); 00241 connection_matrix.fill(1); 00242 } 00243 00244 build_kernels(); 00245 00246 input_images.resize(n_input_images); 00247 output_images.resize(n_output_images); 00248 input_gradients.resize(n_input_images); 00249 output_gradients.resize(n_output_images); 00250 input_diag_hessians.resize(n_input_images); 00251 output_diag_hessians.resize(n_output_images); 00252 } 00253 00254 void BackConvolution2DModule::build_kernels() 00255 { 00256 // If kernels has the right size, for all i and j kernel(i,j) exists iff 00257 // connection_matrix(i,j) !=0, and has the appropriate size, then we don't 00258 // want to forget them. 00259 bool need_rebuild = false; 00260 if( kernels.length() != n_input_images 00261 || kernels.width() != n_output_images ) 00262 { 00263 need_rebuild = true; 00264 } 00265 else 00266 { 00267 for( int i=0 ; i<n_input_images ; i++ ) 00268 for( int j=0 ; j<n_output_images ; j++ ) 00269 { 00270 if( connection_matrix(i,j) == 0 ) 00271 { 00272 if( kernels(i,j).size() != 0 ) 00273 { 00274 need_rebuild = true; 00275 break; 00276 } 00277 } 00278 else if( kernels(i,j).length() != kernel_length 00279 || kernels(i,j).width() != kernel_width ) 00280 { 00281 need_rebuild = true; 00282 break; 00283 } 00284 } 00285 } 00286 00287 if( need_rebuild ) 00288 forget(); 00289 00290 kernel_gradient.resize(kernel_length, kernel_width); 00291 squared_kernel.resize(kernel_length, kernel_width); 00292 } 00293 00294 void BackConvolution2DModule::build() 00295 { 00296 inherited::build(); 00297 build_(); 00298 } 00299 00300 00301 void BackConvolution2DModule::makeDeepCopyFromShallowCopy(CopiesMap& copies) 00302 { 00303 inherited::makeDeepCopyFromShallowCopy(copies); 00304 00305 deepCopyField(connection_matrix, copies); 00306 deepCopyField(kernels, copies); 00307 deepCopyField(bias, copies); 00308 deepCopyField(input_images, copies); 00309 deepCopyField(output_images, copies); 00310 deepCopyField(input_gradients, copies); 00311 deepCopyField(output_gradients, copies); 00312 deepCopyField(input_diag_hessians, copies); 00313 deepCopyField(output_diag_hessians, copies); 00314 deepCopyField(kernel_gradient, copies); 00315 deepCopyField(squared_kernel, copies); 00316 00317 } 00318 00320 void BackConvolution2DModule::fprop(const Vec& input, Vec& output) const 00321 { 00322 // Check size 00323 if( input.size() != input_size ) 00324 PLERROR("BackConvolution2DModule::fprop: input.size() should be equal" 00325 " to\n" 00326 "input_size (%i != %i).\n", input.size(), input_size); 00327 output.resize(output_size); 00328 00329 // Make input_images and output_images point to the right places 00330 for( int i=0 ; i<n_input_images ; i++ ) 00331 input_images[i] = 00332 input.subVec(i*input_images_size, input_images_size) 00333 .toMat( input_images_length, input_images_width ); 00334 00335 for( int j=0 ; j<n_output_images ; j++ ) 00336 output_images[j] = 00337 output.subVec(j*output_images_size, output_images_size) 00338 .toMat( output_images_length, output_images_width ); 00339 00340 // Compute the values of the output_images 00341 for( int j=0 ; j<n_output_images ; j++ ) 00342 { 00343 output_images[j].fill( bias[j] ); 00344 for( int i=0 ; i<n_input_images ; i++ ) 00345 { 00346 if( connection_matrix(i,j) != 0 ) 00347 backConvolve2D(input_images[i], kernels(i,j), output_images[j], 00348 kernel_step1, kernel_step2, true ); 00349 } 00350 } 00351 } 00352 00353 /* THIS METHOD IS OPTIONAL 00364 void BackConvolution2DModule::bpropUpdate(const Vec& input, const Vec& output, 00365 const Vec& output_gradient) 00366 { 00367 } 00368 */ 00369 00371 void BackConvolution2DModule::bpropUpdate(const Vec& input, const Vec& output, 00372 Vec& input_gradient, 00373 const Vec& output_gradient, 00374 bool accumulate) 00375 { 00376 // Check size 00377 if( input.size() != input_size ) 00378 PLERROR("BackConvolution2DModule::bpropUpdate: input.size() should" 00379 " be\n" 00380 "equal to input_size (%i != %i).\n", input.size(), input_size); 00381 if( output.size() != output_size ) 00382 PLERROR("BackConvolution2DModule::bpropUpdate: output.size() should" 00383 " be\n" 00384 "equal to output_size (%i != %i).\n", 00385 output.size(), output_size); 00386 if( output_gradient.size() != output_size ) 00387 PLERROR("BackConvolution2DModule::bpropUpdate: output_gradient.size()" 00388 " should be\n" 00389 "equal to output_size (%i != %i).\n", 00390 output_gradient.size(), output_size); 00391 00392 if( accumulate ) 00393 { 00394 PLASSERT_MSG( input_gradient.size() == input_size, 00395 "Cannot resize input_gradient AND accumulate into it" ); 00396 } 00397 else 00398 input_gradient.resize(input_size); 00399 00400 // Since fprop() has just been called, we assume that input_images and 00401 // output_images are up-to-date 00402 // Make input_gradients and output_gradients point to the right places 00403 for( int i=0 ; i<n_input_images ; i++ ) 00404 input_gradients[i] = 00405 input_gradient.subVec(i*input_images_size, input_images_size) 00406 .toMat( input_images_length, input_images_width ); 00407 00408 for( int j=0 ; j<n_output_images ; j++ ) 00409 output_gradients[j] = 00410 output_gradient.subVec(j*output_images_size, output_images_size) 00411 .toMat( output_images_length, output_images_width ); 00412 00413 // Do the actual bprop and update 00414 learning_rate = start_learning_rate / (1+decrease_constant*step_number); 00415 for( int j=0 ; j<n_output_images ; j++ ) 00416 { 00417 for( int i=0 ; i<n_input_images ; j++ ) 00418 if( connection_matrix(i,j) != 0 ) 00419 { 00420 backConvolve2Dbackprop( kernels(i,j), input_images[i], 00421 input_gradients[i], 00422 output_gradients[j], kernel_gradient, 00423 kernel_step1, kernel_step2, 00424 accumulate ); 00425 00426 // kernel(i,j) -= learning_rate * kernel_gradient 00427 multiplyAcc( kernels(i,j), kernel_gradient, -learning_rate ); 00428 } 00429 bias[j] -= learning_rate * sum( output_gradients[j] ); 00430 } 00431 00432 } 00433 00436 void BackConvolution2DModule::forget() 00437 { 00438 bias.clear(); 00439 if( !random_gen ) 00440 { 00441 PLWARNING( "BackConvolution2DModule: cannot forget() without" 00442 " random_gen" ); 00443 return; 00444 } 00445 00446 real scale_factor = 1./(kernel_length*kernel_width); 00447 kernels.resize( n_input_images, n_output_images ); 00448 for( int i=0 ; i<n_input_images ; i++ ) 00449 for( int j=0 ; j<n_output_images ; j++ ) 00450 { 00451 if( connection_matrix(i,j) == 0 ) 00452 kernels(i,j).resize(0,0); 00453 else 00454 { 00455 kernels(i,j).resize(kernel_length, kernel_width); 00456 random_gen->fill_random_uniform( kernels(i,j), 00457 -scale_factor, 00458 scale_factor ); 00459 } 00460 } 00461 } 00462 00463 /* THIS METHOD IS OPTIONAL 00468 void BackConvolution2DModule::finalize() 00469 { 00470 } 00471 */ 00472 00473 /* THIS METHOD IS OPTIONAL 00476 bool BackConvolution2DModule::bpropDoesNothing() 00477 { 00478 } 00479 */ 00480 00481 /* THIS METHOD IS OPTIONAL 00491 void BackConvolution2DModule::bbpropUpdate(const Vec& input, const Vec& output, 00492 const Vec& output_gradient, 00493 const Vec& output_diag_hessian) 00494 { 00495 } 00496 */ 00497 00502 void BackConvolution2DModule::bbpropUpdate(const Vec& input, const Vec& output, 00503 Vec& input_gradient, 00504 const Vec& output_gradient, 00505 Vec& input_diag_hessian, 00506 const Vec& output_diag_hessian, 00507 bool accumulate) 00508 { 00509 // This version forwards the second order information, but does not 00510 // actually use it for the update. 00511 00512 // Check size 00513 if( output_diag_hessian.size() != output_size ) 00514 PLERROR("BackConvolution2DModule::bbpropUpdate:" 00515 " output_diag_hessian.size()\n" 00516 "should be equal to output_size (%i != %i).\n", 00517 output_diag_hessian.size(), output_size); 00518 00519 if( accumulate ) 00520 { 00521 PLASSERT_MSG( input_diag_hessian.size() == input_size, 00522 "Cannot resize input_diag_hessian AND accumulate into it" 00523 ); 00524 } 00525 else 00526 input_diag_hessian.resize(input_size); 00527 00528 // Make input_diag_hessians and output_diag_hessians point to the right 00529 // places 00530 for( int i=0 ; i<n_input_images ; i++ ) 00531 input_diag_hessians[i] = 00532 input_diag_hessian.subVec(i*input_images_size, input_images_size) 00533 .toMat( input_images_length, input_images_width ); 00534 00535 for( int j=0 ; j<n_output_images ; j++ ) 00536 output_diag_hessians[j] = 00537 output_diag_hessian.subVec(j*output_images_size,output_images_size) 00538 .toMat( output_images_length, output_images_width ); 00539 00540 // Propagates to input_diag_hessian 00541 for( int j=0 ; j<n_output_images ; j++ ) 00542 for( int i=0 ; j<n_input_images ; i++ ) 00543 if( connection_matrix(i,j) != 0 ) 00544 { 00545 squared_kernel << kernels(i,j); 00546 squared_kernel *= kernels(i,j); // term-to-term product 00547 00548 convolve2D( output_diag_hessians[j], squared_kernel, 00549 input_diag_hessians[i], 00550 kernel_step1, kernel_step2, accumulate ); 00551 } 00552 00553 // Call bpropUpdate() 00554 bpropUpdate( input, output, input_gradient, output_gradient, accumulate ); 00555 } 00556 00557 00558 } // end of namespace PLearn 00559 00560 00561 /* 00562 Local Variables: 00563 mode:c++ 00564 c-basic-offset:4 00565 c-file-style:"stroustrup" 00566 c-file-offsets:((innamespace . 0)(inline-open . 0)) 00567 indent-tabs-mode:nil 00568 fill-column:79 00569 End: 00570 */ 00571 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=79 :