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 
78  if ( mapIndex.count( output ) == 0 ) {
79  return;
80  }
81  int index = mapIndex[ output ];
82  subtract( 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].zeroing();
100  cluster_deltas_vec[i].last_deltas_inputs.zeroing();
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.copyValues( 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 *= 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  amul( 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.zeroing();
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  DoubleVector minus_ones( cluster_deltas_vec[i].cluster->outputs().size( ), -1.0f );
144  deltarule( cluster_deltas_vec[i].cluster->biases(), -learn_rate, minus_ones, cluster_deltas_vec[i].deltas_inputs );
145  }
146 
147  for ( int j=0; j<cluster_deltas_vec[i].incoming_linkers_vec.size(); ++j ) {
148  if ( cluster_deltas_vec[i].incoming_linkers_vec[j] != NULL ) {
149  deltarule(
150  cluster_deltas_vec[i].incoming_linkers_vec[j]->matrix(),
151  -learn_rate,
152  cluster_deltas_vec[i].incoming_linkers_vec[j]->from()->outputs(),
153  cluster_deltas_vec[i].deltas_inputs
154  );
155  if ( !useMomentum ) continue;
156  // --- add the momentum
157  deltarule(
158  cluster_deltas_vec[i].incoming_linkers_vec[j]->matrix(),
159  -learn_rate*momentumv,
160  cluster_deltas_vec[i].incoming_last_outputs[j],
161  cluster_deltas_vec[i].last_deltas_inputs
162  );
163  // --- save datas for momentum on the next step
164  cluster_deltas_vec[i].incoming_last_outputs[j].copyValues( cluster_deltas_vec[i].incoming_linkers_vec[j]->from()->outputs() );
165  cluster_deltas_vec[i].last_deltas_inputs.copyValues( cluster_deltas_vec[i].deltas_inputs );
166  }
167  }
168  }
169  return;
170 }
171 
173  // --- set the inputs of the net
174  ClusterList clins = neuralNet()->inputClusters();
175  for( int i=0; i<clins.size(); i++ ) {
176  clins[i]->inputs().copyValues( pat.inputsOf( clins[i] ) );
177  }
178  // --- spread the net
179  neuralNet()->step();
180  // --- set the teaching input
181  ClusterList clout = neuralNet()->outputClusters();
182  for( int i=0; i<clout.size(); i++ ) {
183  setTeachingInput( clout[i], pat.outputsOf( clout[i] ) );
184  }
185  learn();
186 }
187 
189  // --- set the inputs of the net
190  ClusterList clins = neuralNet()->inputClusters();
191  for( int i=0; i<clins.size(); i++ ) {
192  clins[i]->inputs().copyValues( pat.inputsOf( clins[i] ) );
193  }
194  // --- spread the net
195  neuralNet()->step();
196  // --- calculate the MSE
197  ClusterList clout = neuralNet()->outputClusters();
198  double mseacc = 0.0;
199  int dim = (int)clout.size();
200  for( int i=0; i<dim; i++ ) {
201  mseacc += mse( clout[i]->outputs(), pat.outputsOf( clout[i] ) );
202  }
203  return mseacc/dim;
204 }
205 
206 void BackPropagationAlgo::addCluster( Cluster* cl, bool isOut ) {
207  if( mapIndex.count( cl ) == 0 ) {
208  cluster_deltas temp;
209  int size = cl->numNeurons();
210  temp.cluster = dynamic_cast<BiasedCluster*>(cl);
211  temp.isOutput = isOut;
212  temp.deltas_outputs.resize( size );
213  temp.deltas_inputs.resize( size );
214  temp.last_deltas_inputs.resize( size );
215  cluster_deltas_vec.push_back( temp );
216  mapIndex[cl] = cluster_deltas_vec.size()-1;
217  }
218 }
219 
220 void BackPropagationAlgo::addLinker( Linker* link ) {
221  if ( mapIndex.count( link->to() ) == 0 ) {
222  cluster_deltas temp;
223  int size = link->to()->numNeurons();
224  temp.cluster = dynamic_cast<BiasedCluster*>(link->to());
225  temp.isOutput = false;
226  temp.deltas_outputs.resize( size );
227  temp.deltas_inputs.resize( size );
228  temp.last_deltas_inputs.resize( size );
229  temp.incoming_linkers_vec.push_back( dynamic_cast<MatrixLinker*>(link) );
230  temp.incoming_last_outputs.push_back( DoubleVector( link->from()->numNeurons() ) );
231  cluster_deltas_vec.push_back( temp );
232  mapIndex[temp.cluster] = cluster_deltas_vec.size()-1;
233  }
234  else {
235  int tmp = mapIndex[link->to()];
236  cluster_deltas_vec[ tmp ].incoming_linkers_vec.push_back( dynamic_cast<MatrixLinker*>(link) );
237  cluster_deltas_vec[ tmp ].incoming_last_outputs.push_back( DoubleVector( link->from()->numNeurons() ) );
238  }
239 }
240 
243  learn_rate = params.getValue( prefix + "rate" ).toDouble();
244  momentumv = params.getValue( prefix + "momentum" ).toDouble();
245  if ( momentumv == 0.0 ) {
246  useMomentum = false;
247  } else {
248  useMomentum = true;
249  }
250  QString str = params.getValue( prefix + "order" );
251  update_order.clear();
252  if ( !str.isEmpty() ) {
253  QStringList list = str.split(QRegExp("\\s+"), QString::SkipEmptyParts);
254  foreach( QString upl, list ) {
255  Updatable* up = params.getObjectFromGroup<Updatable>( upl, true );
256  update_order << up;
257  }
258  }
259  NeuralNet* net = params.getObjectFromParameter<NeuralNet>( prefix+"neuralnet", false, true );
260  setNeuralNet( net );
262 }
263 
264 void BackPropagationAlgo::save(ConfigurationParameters& params, QString prefix) {
265  params.startObjectParameters(prefix, "BackPropagationAlgo", this);
266  params.createParameter( prefix, "neuralnet", neuralNet() );
267  params.createParameter( prefix, "rate", QString::number(learn_rate) );
268  if ( useMomentum ) {
269  params.createParameter( prefix, "momentum", QString::number(momentumv) );
270  }
271  QStringList list;
272  foreach( Updatable* up, update_order ) {
273  list << up->name();
274  }
275  params.createParameter( prefix, "order", list.join(" ") );
276  //--- save the neuralnet in the group corresponding to its name
277  neuralNet()->save( params, neuralNet()->name() );
278 }
279 
280 void BackPropagationAlgo::describe( QString type ) {
281  Descriptor d = addTypeDescription( type, "Backpropagation Learning Algorithm" );
282  d.describeObject( "neuralnet" ).type( "NeuralNet" ).props( IsMandatory ).help( "The neural network to learn by backpropagation" );
283  d.describeReal( "rate" ).limits( 0.0, 1.0 ).def( 0.2 ).help( "The learning rate" );
284  d.describeReal( "momentum" ).limits( 0.0, 1.0 ).help( "The momentum rate; if zero momentum will be disabled" );
285 }
286 
287 }