nnfw/src/backpropagationalgo.cpp

00001 /********************************************************************************
00002  *  Neural Network Framework.                                                   *
00003  *  Copyright (C) 2005-2011 Gianluca Massera <emmegian@yahoo.it>                *
00004  *                                                                              *
00005  *  This program is free software; you can redistribute it and/or modify        *
00006  *  it under the terms of the GNU General Public License as published by        *
00007  *  the Free Software Foundation; either version 2 of the License, or           *
00008  *  (at your option) any later version.                                         *
00009  *                                                                              *
00010  *  This program is distributed in the hope that it will be useful,             *
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of              *
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               *
00013  *  GNU General Public License for more details.                                *
00014  *                                                                              *
00015  *  You should have received a copy of the GNU General Public License           *
00016  *  along with this program; if not, write to the Free Software                 *
00017  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA  *
00018  ********************************************************************************/
00019 
00020 #include "neuralnet.h"
00021 #include "matrixlinker.h"
00022 #include "biasedcluster.h"
00023 #include "backpropagationalgo.h"
00024 
00025 namespace farsa {
00026 
00027 BackPropagationAlgo::BackPropagationAlgo( NeuralNet *n_n, UpdatableList up_order, double l_r )
00028     : LearningAlgorithm(n_n), learn_rate(l_r), update_order(up_order) {
00029     useMomentum = false;
00030     momentumv = 0.0f;
00031     neuralNetChanged();
00032 }
00033 
00034 BackPropagationAlgo::BackPropagationAlgo()
00035     : LearningAlgorithm(), learn_rate(0.0) {
00036     useMomentum = false;
00037     momentumv = 0.0f;
00038     neuralNetChanged();
00039 }
00040 
00041 BackPropagationAlgo::~BackPropagationAlgo( ) {
00042     /* nothing to do ?!?! */
00043 }
00044 
00045 void BackPropagationAlgo::neuralNetChanged() {
00046     NeuralNet* net = neuralNet();
00047     if ( !net ) return;
00048     //--- clear all data
00049     mapIndex.clear();
00050     cluster_deltas_vec.clear();
00051     //--- insert new data for the new NeuralNet
00052     Cluster *cluster_temp;
00053     // pushing the info for output cluster
00054     ClusterList outs = net->outputClusters();
00055     for( int i=0; i<(int)outs.size(); i++ ) {
00056         addCluster( outs[i], true );
00057     }
00058     // --- generate information for backpropagation of deltas
00059     for( int i=0; i<(int)update_order.size(); i++ ) {
00060         cluster_temp = dynamic_cast<Cluster*>(update_order[i]);
00061         if ( cluster_temp ) {
00062             addCluster( cluster_temp, false );
00063             continue;
00064         }
00065         MatrixLinker* linker_temp = dynamic_cast<MatrixLinker*>(update_order[i]);
00066         if ( linker_temp ) {    //Dot linker subclass of Matrixlinker
00067             addLinker( linker_temp );
00068         }
00069     }
00070 }
00071 
00072 void BackPropagationAlgo::setUpdateOrder( const UpdatableList& update_order ) {
00073     this->update_order = update_order;
00074     this->neuralNetChanged();
00075 }
00076 
00077 void BackPropagationAlgo::setTeachingInput( Cluster* output, const DoubleVector& ti ) {
00078     if ( mapIndex.count( output ) == 0 ) { 
00079         return;
00080     }
00081     int index = mapIndex[ output ];
00082     subtract( cluster_deltas_vec[index].deltas_outputs, output->outputs(), ti );
00083     return;
00084 }
00085 
00086 DoubleVector BackPropagationAlgo::getError( Cluster* cl ) {
00087     if ( mapIndex.count( cl ) == 0 ) {
00088         qWarning() << "Cluster not present in BackPropagationAlgo";
00089         return DoubleVector();
00090     }
00091     int index = mapIndex[ cl ];
00092     return cluster_deltas_vec[index].deltas_outputs;
00093 }
00094 
00095 void BackPropagationAlgo::enableMomentum() {
00096     for ( int i=0; i<cluster_deltas_vec.size(); ++i ) {
00097         for ( int j=0;  j<cluster_deltas_vec[i].incoming_linkers_vec.size(); ++j ) {
00098             // --- zeroing data
00099             cluster_deltas_vec[i].incoming_last_outputs[j].zeroing();
00100             cluster_deltas_vec[i].last_deltas_inputs.zeroing();
00101         }
00102     }
00103     useMomentum = true;
00104 }
00105 
00106 void BackPropagationAlgo::propagDeltas() {
00107     DoubleVector diff_vec;
00108     for( int i=0; i<(int)cluster_deltas_vec.size(); i++ ) {
00109         cluster_deltas_vec[i].incoming_linkers_vec;
00110         // --- propagate DeltaOutput to DeltaInputs
00111         cluster_deltas_vec[i].deltas_inputs.copyValues( cluster_deltas_vec[i].deltas_outputs );
00112         Cluster* cl = cluster_deltas_vec[i].cluster;
00113         OutputFunction* func = cl->outFunction();
00114         diff_vec.resize( cluster_deltas_vec[i].deltas_inputs.size() );
00115         if ( func->derivate( cl->inputs(), cl->outputs(), diff_vec ) ) {
00116             cluster_deltas_vec[i].deltas_inputs *= diff_vec;
00117         }
00118         // --- propagate DeltaInputs to DeltaOutput through MatrixLinker
00119         for( int k=0; k<cluster_deltas_vec[i].incoming_linkers_vec.size( ); ++k ) {
00120             MatrixLinker* link = dynamic_cast<MatrixLinker*>(cluster_deltas_vec[i].incoming_linkers_vec[k]);
00121             if ( mapIndex.count(link->from()) == 0 ) {
00122                 // --- the from() cluster is not in Learning
00123                 continue;
00124             }
00125             int from_index = mapIndex[ link->from() ];
00126             amul( cluster_deltas_vec[from_index].deltas_outputs, link->matrix(), cluster_deltas_vec[i].deltas_inputs );
00127         }
00128     }
00129     return;
00130 }
00131 
00132 void BackPropagationAlgo::learn() {
00133     // --- zeroing previous step delta information
00134     for ( int i=0; i<cluster_deltas_vec.size(); ++i ) {
00135         if ( cluster_deltas_vec[i].isOutput ) continue;
00136         cluster_deltas_vec[i].deltas_outputs.zeroing();
00137     }
00138     // --- propagating the error through the net
00139     propagDeltas();
00140     // --- make the learn !!
00141     for ( int i=0; i<cluster_deltas_vec.size(); ++i ) {
00142         if ( cluster_deltas_vec[i].cluster != NULL) {
00143             DoubleVector minus_ones( cluster_deltas_vec[i].cluster->outputs().size( ), -1.0f );
00144             deltarule( cluster_deltas_vec[i].cluster->biases(), -learn_rate, minus_ones, cluster_deltas_vec[i].deltas_inputs );
00145         }
00146 
00147         for ( int j=0;  j<cluster_deltas_vec[i].incoming_linkers_vec.size(); ++j ) {
00148             if ( cluster_deltas_vec[i].incoming_linkers_vec[j] != NULL ) {
00149                 deltarule(
00150                     cluster_deltas_vec[i].incoming_linkers_vec[j]->matrix(),
00151                     -learn_rate,
00152                     cluster_deltas_vec[i].incoming_linkers_vec[j]->from()->outputs(),
00153                     cluster_deltas_vec[i].deltas_inputs
00154                 );
00155                 if ( !useMomentum ) continue;
00156                 // --- add the momentum
00157                 deltarule(
00158                     cluster_deltas_vec[i].incoming_linkers_vec[j]->matrix(),
00159                     -learn_rate*momentumv,
00160                     cluster_deltas_vec[i].incoming_last_outputs[j],
00161                     cluster_deltas_vec[i].last_deltas_inputs
00162                 );
00163                 // --- save datas for momentum on the next step
00164                 cluster_deltas_vec[i].incoming_last_outputs[j].copyValues( cluster_deltas_vec[i].incoming_linkers_vec[j]->from()->outputs() );
00165                 cluster_deltas_vec[i].last_deltas_inputs.copyValues( cluster_deltas_vec[i].deltas_inputs );
00166             }
00167         }
00168     }
00169     return;
00170 }
00171 
00172 void BackPropagationAlgo::learn( const Pattern& pat ) {
00173     // --- set the inputs of the net
00174     ClusterList clins = neuralNet()->inputClusters();
00175     for( int i=0; i<clins.size(); i++ ) {
00176         clins[i]->inputs().copyValues( pat.inputsOf( clins[i] ) );
00177     }
00178     // --- spread the net
00179     neuralNet()->step();
00180     // --- set the teaching input
00181     ClusterList clout = neuralNet()->outputClusters();
00182     for( int i=0; i<clout.size(); i++ ) {
00183         setTeachingInput( clout[i], pat.outputsOf( clout[i] ) );
00184     }
00185     learn();
00186 }
00187 
00188 double BackPropagationAlgo::calculateMSE( const Pattern& pat ) {
00189     // --- set the inputs of the net
00190     ClusterList clins = neuralNet()->inputClusters();
00191     for( int i=0; i<clins.size(); i++ ) {
00192         clins[i]->inputs().copyValues( pat.inputsOf( clins[i] ) );
00193     }
00194     // --- spread the net
00195     neuralNet()->step();
00196     // --- calculate the MSE
00197     ClusterList clout = neuralNet()->outputClusters();
00198     double mseacc = 0.0;
00199     int dim = (int)clout.size();
00200     for( int i=0; i<dim; i++ ) {
00201         mseacc += mse( clout[i]->outputs(), pat.outputsOf( clout[i] ) );
00202     }
00203     return mseacc/dim;
00204 }
00205 
00206 void BackPropagationAlgo::addCluster( Cluster* cl, bool isOut ) {
00207     if( mapIndex.count( cl ) == 0 ) {
00208         cluster_deltas temp;
00209         int size = cl->numNeurons();
00210         temp.cluster = dynamic_cast<BiasedCluster*>(cl);
00211         temp.isOutput = isOut;
00212         temp.deltas_outputs.resize( size );
00213         temp.deltas_inputs.resize( size );
00214         temp.last_deltas_inputs.resize( size );
00215         cluster_deltas_vec.push_back( temp );
00216         mapIndex[cl] = cluster_deltas_vec.size()-1;
00217     }
00218 }
00219 
00220 void BackPropagationAlgo::addLinker( Linker* link ) {
00221     if ( mapIndex.count( link->to() ) == 0 ) {
00222         cluster_deltas temp;
00223         int size = link->to()->numNeurons();
00224         temp.cluster = dynamic_cast<BiasedCluster*>(link->to());
00225         temp.isOutput = false;
00226         temp.deltas_outputs.resize( size );
00227         temp.deltas_inputs.resize( size );
00228         temp.last_deltas_inputs.resize( size );
00229         temp.incoming_linkers_vec.push_back( dynamic_cast<MatrixLinker*>(link) );
00230         temp.incoming_last_outputs.push_back( DoubleVector( link->from()->numNeurons() ) );
00231         cluster_deltas_vec.push_back( temp );
00232         mapIndex[temp.cluster] = cluster_deltas_vec.size()-1;
00233     }
00234     else {
00235         int tmp = mapIndex[link->to()];
00236         cluster_deltas_vec[ tmp ].incoming_linkers_vec.push_back( dynamic_cast<MatrixLinker*>(link) );
00237         cluster_deltas_vec[ tmp ].incoming_last_outputs.push_back( DoubleVector( link->from()->numNeurons() ) );
00238     }
00239 }
00240 
00241 void BackPropagationAlgo::configure(ConfigurationParameters& params, QString prefix) {
00242     params.startRememberingGroupObjectAssociations();
00243     learn_rate = params.getValue( prefix + "rate" ).toDouble();
00244     momentumv = params.getValue( prefix + "momentum" ).toDouble();
00245     if ( momentumv == 0.0 ) {
00246         useMomentum = false;
00247     } else {
00248         useMomentum = true;
00249     }
00250     QString str = params.getValue( prefix + "order" );
00251     update_order.clear();
00252     if ( !str.isEmpty() ) {
00253         QStringList list = str.split(QRegExp("\\s+"), QString::SkipEmptyParts);
00254         foreach( QString upl, list ) {
00255             Updatable* up = params.getObjectFromGroup<Updatable>( upl, true );
00256             update_order << up;
00257         }
00258     }
00259     NeuralNet* net = params.getObjectFromParameter<NeuralNet>( prefix+"neuralnet", false, true );
00260     setNeuralNet( net );
00261     params.stopRememberingGroupObjectAssociations();
00262 }
00263 
00264 void BackPropagationAlgo::save(ConfigurationParameters& params, QString prefix) {
00265     params.startObjectParameters(prefix, "BackPropagationAlgo", this);
00266     params.createParameter( prefix, "neuralnet", neuralNet() );
00267     params.createParameter( prefix, "rate", QString::number(learn_rate) );
00268     if ( useMomentum ) {
00269         params.createParameter( prefix, "momentum", QString::number(momentumv) );
00270     }
00271     QStringList list;
00272     foreach( Updatable* up, update_order ) {
00273         list << up->name();
00274     }
00275     params.createParameter( prefix, "order", list.join(" ") );
00276     //--- save the neuralnet in the group corresponding to its name
00277     neuralNet()->save( params, neuralNet()->name() );
00278 }
00279 
00280 void BackPropagationAlgo::describe( QString type ) {
00281     Descriptor d = addTypeDescription( type, "Backpropagation Learning Algorithm" );
00282     d.describeObject( "neuralnet" ).type( "NeuralNet" ).props( IsMandatory ).help( "The neural network to learn by backpropagation" );
00283     d.describeReal( "rate" ).limits( 0.0, 1.0 ).def( 0.2 ).help( "The learning rate" );
00284     d.describeReal( "momentum" ).limits( 0.0, 1.0 ).help( "The momentum rate; if zero momentum will be disabled" );
00285 }
00286 
00287 }