backpropagationalgo.cpp
1 /********************************************************************************
2  * Neural Network Framework. *
3  * Copyright (C) 2005-2011 Gianluca Massera <emmegian@yahoo.it> *
4  * *
5  * This program is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program; if not, write to the Free Software *
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
18  ********************************************************************************/
19 
20 #include "neuralnet.h"
21 #include "matrixlinker.h"
22 #include "biasedcluster.h"
23 #include "backpropagationalgo.h"
24 
25 namespace farsa {
26 
27 BackPropagationAlgo::BackPropagationAlgo( NeuralNet *n_n, UpdatableList up_order, double l_r )
28  : LearningAlgorithm(n_n), learn_rate(l_r), update_order(up_order) {
29  useMomentum = false;
30  momentumv = 0.0f;
32 }
33 
35  : LearningAlgorithm(), learn_rate(0.0) {
36  useMomentum = false;
37  momentumv = 0.0f;
39 }
40 
42  /* nothing to do ?!?! */
43 }
44 
46  NeuralNet* net = neuralNet();
47  if ( !net ) return;
48  //--- clear all data
49  mapIndex.clear();
50  cluster_deltas_vec.clear();
51  //--- insert new data for the new NeuralNet
52  Cluster *cluster_temp;
53  // pushing the info for output cluster
54  ClusterList outs = net->outputClusters();
55  for( int i=0; i<(int)outs.size(); i++ ) {
56  addCluster( outs[i], true );
57  }
58  // --- generate information for backpropagation of deltas
59  for( int i=0; i<(int)update_order.size(); i++ ) {
60  cluster_temp = dynamic_cast<Cluster*>(update_order[i]);
61  if ( cluster_temp ) {
62  addCluster( cluster_temp, false );
63  continue;
64  }
65  MatrixLinker* linker_temp = dynamic_cast<MatrixLinker*>(update_order[i]);
66  if ( linker_temp ) { //Dot linker subclass of Matrixlinker
67  addLinker( linker_temp );
68  }
69  }
70 }
71 
72 void BackPropagationAlgo::setUpdateOrder( const UpdatableList& update_order ) {
73  this->update_order = update_order;
74  this->neuralNetChanged();
75 }
76 
77 void BackPropagationAlgo::setTeachingInput( Cluster* output, const DoubleVector& ti ) {
78  if ( mapIndex.count( output ) == 0 ) {
79  return;
80  }
81  int index = mapIndex[ output ];
82  cluster_deltas_vec[index].deltas_outputs = output->outputs() - ti;
83  return;
84 }
85 
87  if ( mapIndex.count( cl ) == 0 ) {
88  qWarning() << "Cluster not present in BackPropagationAlgo";
89  return DoubleVector();
90  }
91  int index = mapIndex[ cl ];
92  return cluster_deltas_vec[index].deltas_outputs;
93 }
94 
96  for ( int i=0; i<cluster_deltas_vec.size(); ++i ) {
97  for ( int j=0; j<cluster_deltas_vec[i].incoming_linkers_vec.size(); ++j ) {
98  // --- zeroing data
99  cluster_deltas_vec[i].incoming_last_outputs[j].setZero();
100  cluster_deltas_vec[i].last_deltas_inputs.setZero();
101  }
102  }
103  useMomentum = true;
104 }
105 
106 void BackPropagationAlgo::propagDeltas() {
107  DoubleVector diff_vec;
108  for( int i=0; i<(int)cluster_deltas_vec.size(); i++ ) {
109  cluster_deltas_vec[i].incoming_linkers_vec;
110  // --- propagate DeltaOutput to DeltaInputs
111  cluster_deltas_vec[i].deltas_inputs = cluster_deltas_vec[i].deltas_outputs;
112  Cluster* cl = cluster_deltas_vec[i].cluster;
113  OutputFunction* func = cl->outFunction();
114  diff_vec.resize( cluster_deltas_vec[i].deltas_inputs.size() );
115  if ( func->derivate( cl->inputs(), cl->outputs(), diff_vec ) ) {
116  cluster_deltas_vec[i].deltas_inputs = cluster_deltas_vec[i].deltas_inputs.cwiseProduct(diff_vec);
117  }
118  // --- propagate DeltaInputs to DeltaOutput through MatrixLinker
119  for( int k=0; k<cluster_deltas_vec[i].incoming_linkers_vec.size( ); ++k ) {
120  MatrixLinker* link = dynamic_cast<MatrixLinker*>(cluster_deltas_vec[i].incoming_linkers_vec[k]);
121  if ( mapIndex.count(link->from()) == 0 ) {
122  // --- the from() cluster is not in Learning
123  continue;
124  }
125  int from_index = mapIndex[ link->from() ];
126  cluster_deltas_vec[from_index].deltas_outputs = link->matrix()*cluster_deltas_vec[i].deltas_inputs;
127  }
128  }
129  return;
130 }
131 
133  // --- zeroing previous step delta information
134  for ( int i=0; i<cluster_deltas_vec.size(); ++i ) {
135  if ( cluster_deltas_vec[i].isOutput ) continue;
136  cluster_deltas_vec[i].deltas_outputs.setZero();
137  }
138  // --- propagating the error through the net
139  propagDeltas();
140  // --- make the learn !!
141  for ( int i=0; i<cluster_deltas_vec.size(); ++i ) {
142  if ( cluster_deltas_vec[i].cluster != NULL) {
143  for( unsigned int b=0; b<cluster_deltas_vec[i].cluster->numNeurons(); b++ ) {
144  cluster_deltas_vec[i].cluster->biases()[b] += -learn_rate*-cluster_deltas_vec[i].deltas_inputs[b];
145  }
146  }
147 
148  for ( int j=0; j<cluster_deltas_vec[i].incoming_linkers_vec.size(); ++j ) {
149  if ( cluster_deltas_vec[i].incoming_linkers_vec[j] != NULL ) {
150  DoubleVector& outputs = cluster_deltas_vec[i].incoming_linkers_vec[j]->from()->outputs();
151  DoubleVector& inputs = cluster_deltas_vec[i].deltas_inputs;
152  DoubleMatrix& matrix = cluster_deltas_vec[i].incoming_linkers_vec[j]->matrix();
153  for ( int r=0; r<outputs.size(); r++ ) {
154  for ( int c=0; c<inputs.size(); c++ ) {
155  matrix(r,c) += -learn_rate * outputs[r] * inputs[c];
156  }
157  }
158  if ( !useMomentum ) continue;
159  // --- add the momentum
160  outputs = cluster_deltas_vec[i].incoming_last_outputs[j];
161  inputs = cluster_deltas_vec[i].last_deltas_inputs;
162  matrix = cluster_deltas_vec[i].incoming_linkers_vec[j]->matrix();
163  for ( int r=0; r<outputs.size(); r++ ) {
164  for ( int c=0; c<inputs.size(); c++ ) {
165  matrix(r,c) += -learn_rate*momentumv * outputs[r] * inputs[c];
166  }
167  }
168  // --- save datas for momentum on the next step
169  cluster_deltas_vec[i].incoming_last_outputs[j] = cluster_deltas_vec[i].incoming_linkers_vec[j]->from()->outputs();
170  cluster_deltas_vec[i].last_deltas_inputs = cluster_deltas_vec[i].deltas_inputs;
171  }
172  }
173  }
174  return;
175 }
176 
178  // --- set the inputs of the net
179  ClusterList clins = neuralNet()->inputClusters();
180  for( int i=0; i<clins.size(); i++ ) {
181  clins[i]->inputs() = pat.inputsOf( clins[i] );
182  }
183  // --- spread the net
184  neuralNet()->step();
185  // --- set the teaching input
186  ClusterList clout = neuralNet()->outputClusters();
187  for( int i=0; i<clout.size(); i++ ) {
188  setTeachingInput( clout[i], pat.outputsOf( clout[i] ) );
189  }
190  learn();
191 }
192 
194  // --- set the inputs of the net
195  ClusterList clins = neuralNet()->inputClusters();
196  for( int i=0; i<clins.size(); i++ ) {
197  clins[i]->inputs() = pat.inputsOf( clins[i] );
198  }
199  // --- spread the net
200  neuralNet()->step();
201  // --- calculate the MSE
202  ClusterList clout = neuralNet()->outputClusters();
203  double mseacc = 0.0;
204  int dim = (int)clout.size();
205  for( int i=0; i<dim; i++ ) {
206  DoubleVector diff = clout[i]->outputs()-pat.outputsOf( clout[i] );
207  mseacc += diff.dot(diff)/diff.size();
208  }
209  return mseacc/dim;
210 }
211 
212 void BackPropagationAlgo::addCluster( Cluster* cl, bool isOut ) {
213  if( mapIndex.count( cl ) == 0 ) {
214  cluster_deltas temp;
215  int size = cl->numNeurons();
216  temp.cluster = dynamic_cast<BiasedCluster*>(cl);
217  temp.isOutput = isOut;
218  temp.deltas_outputs.resize( size );
219  temp.deltas_inputs.resize( size );
220  temp.last_deltas_inputs.resize( size );
221  cluster_deltas_vec.push_back( temp );
222  mapIndex[cl] = cluster_deltas_vec.size()-1;
223  }
224 }
225 
226 void BackPropagationAlgo::addLinker( Linker* link ) {
227  if ( mapIndex.count( link->to() ) == 0 ) {
228  cluster_deltas temp;
229  int size = link->to()->numNeurons();
230  temp.cluster = dynamic_cast<BiasedCluster*>(link->to());
231  temp.isOutput = false;
232  temp.deltas_outputs.resize( size );
233  temp.deltas_inputs.resize( size );
234  temp.last_deltas_inputs.resize( size );
235  temp.incoming_linkers_vec.push_back( dynamic_cast<MatrixLinker*>(link) );
236  temp.incoming_last_outputs.push_back( DoubleVector( link->from()->numNeurons() ) );
237  cluster_deltas_vec.push_back( temp );
238  mapIndex[temp.cluster] = cluster_deltas_vec.size()-1;
239  }
240  else {
241  int tmp = mapIndex[link->to()];
242  cluster_deltas_vec[ tmp ].incoming_linkers_vec.push_back( dynamic_cast<MatrixLinker*>(link) );
243  cluster_deltas_vec[ tmp ].incoming_last_outputs.push_back( DoubleVector( link->from()->numNeurons() ) );
244  }
245 }
246 
249  learn_rate = params.getValue( prefix + "rate" ).toDouble();
250  momentumv = params.getValue( prefix + "momentum" ).toDouble();
251  if ( momentumv == 0.0 ) {
252  useMomentum = false;
253  } else {
254  useMomentum = true;
255  }
256  QString str = params.getValue( prefix + "order" );
257  update_order.clear();
258  if ( !str.isEmpty() ) {
259  QStringList list = str.split(QRegExp("\\s+"), QString::SkipEmptyParts);
260  foreach( QString upl, list ) {
261  Updatable* up = params.getObjectFromGroup<Updatable>( upl, true );
262  update_order << up;
263  }
264  }
265  NeuralNet* net = params.getObjectFromParameter<NeuralNet>( prefix+"neuralnet", false, true );
266  setNeuralNet( net );
268 }
269 
270 void BackPropagationAlgo::save(ConfigurationParameters& params, QString prefix) {
271  params.startObjectParameters(prefix, "BackPropagationAlgo", this);
272  params.createParameter( prefix, "neuralnet", neuralNet() );
273  params.createParameter( prefix, "rate", QString::number(learn_rate) );
274  if ( useMomentum ) {
275  params.createParameter( prefix, "momentum", QString::number(momentumv) );
276  }
277  QStringList list;
278  foreach( Updatable* up, update_order ) {
279  list << up->name();
280  }
281  params.createParameter( prefix, "order", list.join(" ") );
282  //--- save the neuralnet in the group corresponding to its name
283  neuralNet()->save( params, neuralNet()->name() );
284 }
285 
286 void BackPropagationAlgo::describe( QString type ) {
287  Descriptor d = addTypeDescription( type, "Backpropagation Learning Algorithm" );
288  d.describeObject( "neuralnet" ).type( "NeuralNet" ).props( IsMandatory ).help( "The neural network to learn by backpropagation" );
289  d.describeReal( "rate" ).limits( 0.0, 1.0 ).def( 0.2 ).help( "The learning rate" );
290  d.describeReal( "momentum" ).limits( 0.0, 1.0 ).help( "The momentum rate; if zero momentum will be disabled" );
291 }
292 
293 }
TypeToCreate * getObjectFromParameter(QString param, bool alsoMatchParents=false, bool configure=true, bool forceObjectCreation=false)
void enableMomentum()
Enable the momentum.
OutputFunction Class.
const DoubleVector & outputsOf(Cluster *) const
return stored information if exists, otherwise it return a zero vector
virtual double calculateMSE(const Pattern &)
Calculate the Mean Square Error respect to Pattern passed.
virtual void learn()
a single step of learning algorithm
void setTeachingInput(Cluster *output, const DoubleVector &ti)
Set the teaching input for Cluster passed.
In a BiasedCluster each neuron has an input, an output and a bias value.
Definition: biasedcluster.h:41
virtual void save(ConfigurationParameters &params, QString prefix)
Save the actual status of parameters into the ConfigurationParameters object passed.
BackPropagationAlgo()
Default Constructor.
virtual void save(ConfigurationParameters &params, QString prefix)
Save the actual status of parameters into the ConfigurationParameters object passed.
Definition: neuralnet.cpp:436
DoubleVector & inputs()
Get the array of inputs.
Definition: cluster.h:122
const DoubleVector & inputsOf(Cluster *) const
return stored information if exists, otherwise it return a zero vector
ClusterList outputClusters() const
Returns the vector of Output Clusters contained.
Definition: neuralnet.cpp:186
Updatables objects.
Definition: updatable.h:37
virtual void neuralNetChanged()
Configure internal data for backpropagation on NeuralNet setted.
void step()
Step.
Definition: neuralnet.h:296
virtual void configure(ConfigurationParameters &params, QString prefix)
Configures the object using a ConfigurationParameters object.
MatrixLinker Class define a full connection between two cluster.
Definition: matrixlinker.h:39
void setUpdateOrder(const UpdatableList &update_order)
Set the order on which the error is backpropagated through the NeuralNet.
Define the common interface among Clusters.
Definition: cluster.h:73
static void describe(QString type)
Add to Factory::typeDescriptions() the descriptions of all parameters and subgroups.
void setNeuralNet(NeuralNet *net)
Set the NeuralNet to learn.
bool startObjectParameters(QString groupPath, QString typeName, ParameterSettable *object)
LearningAlgorithm object.
NeuralNet * neuralNet()
Return the NeuralNet setted.
The Neural Network Class.
Definition: neuralnet.h:221
static Descriptor addTypeDescription(QString type, QString shortHelp, QString longHelp=QString(""))
QString getValue(QString path, bool alsoMatchParents=false) const
OutputFunction * outFunction() const
Get the Output function.
Definition: cluster.h:149
TypeToCreate * getObjectFromGroup(QString group, bool configure=true, bool forceObjectCreation=false)
DoubleVector & outputs()
Get the array of outputs.
Definition: cluster.h:136
Pattern object.
virtual bool derivate(const DoubleVector &inputs, const DoubleVector &outputs, DoubleVector &derivates) const
Compute the derivate of the function represent Given the input of neurons and the corresponding outp...
DoubleVector getError(Cluster *)
This method returns the deltas calculated by the Back-propagation Algorithm.
ClusterList inputClusters() const
Returns the vector of Input Clusters contained.
Definition: neuralnet.cpp:182
QString name() const
Return its name.
Definition: updatable.cpp:52
unsigned int numNeurons() const
Return the number of neurons (the length of input and output arrays)
Definition: cluster.h:82
void createParameter(QString groupPath, QString parameter)
This file contains the declaration of Neural Network Class.
This file contains the declaration of BiasedCluster class.