PLearn 0.1
BackConvolution2DModule.cc
Go to the documentation of this file.
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 :
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines