iCub-main
neuralNetworks.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2013 iCub Facility - Istituto Italiano di Tecnologia
3  * Author: Ugo Pattacini
4  * email: ugo.pattacini@iit.it
5  * Permission is granted to copy, distribute, and/or modify this program
6  * under the terms of the GNU General Public License, version 2 or any
7  * later version published by the Free Software Foundation.
8  *
9  * A copy of the license can be found at
10  * http://www.robotcub.org/icub/license/gpl.txt
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15  * Public License for more details
16 */
17 
18 #include <algorithm>
19 #include <string>
20 
21 #include <yarp/math/Math.h>
22 #include <yarp/math/Rand.h>
23 #include <iCub/ctrl/math.h>
25 
26 #include <IpTNLP.hpp>
27 #include <IpIpoptApplication.hpp>
28 
29 #define CAST_IPOPTAPP(x) (static_cast<Ipopt::IpoptApplication*>(x))
30 
31 using namespace std;
32 using namespace yarp::os;
33 using namespace yarp::sig;
34 using namespace yarp::math;
35 using namespace iCub::ctrl;
36 using namespace iCub::optimization;
37 
38 
39 namespace iCub
40 {
41 
42 namespace optimization
43 {
44 
45 /****************************************************************/
46 class ff2LayNNTrainNLP : public Ipopt::TNLP
47 {
48 protected:
49  Property bounds;
50  bool randomInit;
51 
53  deque<Vector> &IW;
54  deque<Vector> &LW;
55  Vector &b1;
56  Vector &b2;
57 
58  const deque<Vector> &in;
59  const deque<Vector> &out;
60  deque<Vector> &pred;
61  double error;
62 
63  /****************************************************************/
64  bool getBounds(const string &tag, double &min, double &max)
65  {
66  min=-1.0; max=1.0;
67  if (Bottle *b=bounds.find(tag).asList())
68  {
69  if (b->size()>=2)
70  {
71  min=b->get(0).asFloat64();
72  max=b->get(1).asFloat64();
73  return true;
74  }
75  }
76 
77  return false;
78  }
79 
80  /****************************************************************/
81  void fillNet(const Ipopt::Number *x)
82  {
83  Ipopt::Index k=0;
84  for (size_t i=0; i<IW.size(); i++)
85  for (size_t j=0; j<IW.front().length(); j++, k++)
86  IW[i][j]=x[k];
87 
88  for (size_t i=0; i<LW.size(); i++)
89  for (size_t j=0; j<LW.front().length(); j++, k++)
90  LW[i][j]=x[k];
91 
92  for (size_t i=0; i<b1.length(); i++, k++)
93  b1[i]=x[k];
94 
95  for (size_t i=0; i<b2.length(); i++, k++)
96  b2[i]=x[k];
97  }
98 
99  /****************************************************************/
100  void fillVar(Ipopt::Number *x, const Ipopt::Number *x_l, const Ipopt::Number *x_u)
101  {
102  Ipopt::Index k=0;
103  for (size_t i=0; i<IW.size(); i++)
104  for (size_t j=0; j<IW.front().length(); j++, k++)
105  x[k]=std::min(x_u[k],std::max(IW[i][j],x_l[k]));
106 
107  for (size_t i=0; i<LW.size(); i++)
108  for (size_t j=0; j<LW.front().length(); j++, k++)
109  x[k]=std::min(x_u[k],std::max(LW[i][j],x_l[k]));
110 
111  for (size_t i=0; i<b1.length(); i++, k++)
112  x[k]=std::min(x_u[k],std::max(b1[i],x_l[k]));
113 
114  for (size_t i=0; i<b2.length(); i++, k++)
115  x[k]=std::min(x_u[k],std::max(b2[i],x_l[k]));
116  }
117 
118 public:
119  /****************************************************************/
120  ff2LayNNTrainNLP(ff2LayNNTrain &_net, const Property &_bounds,
121  const bool _randomInit, const deque<Vector> &_in,
122  const deque<Vector> &_out, deque<Vector> &_pred) :
123  net(_net), bounds(_bounds), randomInit(_randomInit),
124  in(_in), out(_out), pred(_pred),
125  IW(_net.get_IW()), LW(_net.get_LW()),
126  b1(_net.get_b1()), b2(_net.get_b2())
127  {
128  pred.clear();
129  error=0.0;
130  }
131 
132  /****************************************************************/
133  virtual double get_error() const
134  {
135  return error;
136  }
137 
138  /****************************************************************/
139  bool get_nlp_info(Ipopt::Index &n, Ipopt::Index &m, Ipopt::Index &nnz_jac_g,
140  Ipopt::Index &nnz_h_lag, IndexStyleEnum &index_style)
141  {
142  n=IW.size()*IW.front().length()+LW.size()*LW.front().length()+b1.length()+b2.length();
143  m=nnz_jac_g=nnz_h_lag=0;
144  index_style=TNLP::C_STYLE;
145 
146  return true;
147  }
148 
149  /****************************************************************/
150  bool get_bounds_info(Ipopt::Index n, Ipopt::Number *x_l, Ipopt::Number *x_u,
151  Ipopt::Index m, Ipopt::Number *g_l, Ipopt::Number *g_u)
152  {
153  double min_IW,max_IW; getBounds("IW",min_IW,max_IW);
154  double min_LW,max_LW; getBounds("LW",min_LW,max_LW);
155  double min_b1,max_b1; getBounds("b1",min_b1,max_b1);
156  double min_b2,max_b2; getBounds("b2",min_b2,max_b2);
157 
158  Ipopt::Index k=0;
159  for (size_t i=0; i<IW.size(); i++)
160  {
161  for (size_t j=0; j<IW.front().length(); j++, k++)
162  {
163  x_l[k]=min_IW;
164  x_u[k]=max_IW;
165  }
166  }
167 
168  for (size_t i=0; i<LW.size(); i++)
169  {
170  for (size_t j=0; j<LW.front().length(); j++, k++)
171  {
172  x_l[k]=min_LW;
173  x_u[k]=max_LW;
174  }
175  }
176 
177  for (size_t i=0; i<b1.size(); i++, k++)
178  {
179  x_l[k]=min_b1;
180  x_u[k]=max_b1;
181  }
182 
183  for (size_t i=0; i<b2.size(); i++, k++)
184  {
185  x_l[k]=min_b2;
186  x_u[k]=max_b2;
187  }
188 
189  return true;
190  }
191 
192  /****************************************************************/
193  bool get_starting_point(Ipopt::Index n, bool init_x, Ipopt::Number *x,
194  bool init_z, Ipopt::Number *z_L, Ipopt::Number *z_U,
195  Ipopt::Index m, bool init_lambda, Ipopt::Number *lambda)
196  {
197  Ipopt::Number *x_l=new Ipopt::Number[n];
198  Ipopt::Number *x_u=new Ipopt::Number[n];
199  Ipopt::Number *g_l=new Ipopt::Number[n];
200  Ipopt::Number *g_u=new Ipopt::Number[n];
201 
202  get_bounds_info(n,x_l,x_u,m,g_l,g_u);
203  if (randomInit)
204  {
205  Rand::init();
206  for (Ipopt::Index i=0; i<n; i++)
207  x[i]=Rand::scalar(x_l[i],x_u[i]);
208  }
209  else
210  fillVar(x,x_l,x_u);
211 
212  delete[] x_l;
213  delete[] x_u;
214  delete[] g_l;
215  delete[] g_u;
216 
217  return true;
218  }
219 
220  /****************************************************************/
221  bool eval_f(Ipopt::Index n, const Ipopt::Number *x, bool new_x,
222  Ipopt::Number &obj_value)
223  {
224  fillNet(x);
225 
226  obj_value=0.0;
227  for (size_t i=0; i<in.size(); i++)
228  {
229  Vector pred=net.predict(in[i]);
230  obj_value+=norm2(out[i]-pred);
231  }
232 
233  obj_value/=in.size();
234  return true;
235  }
236 
237  /****************************************************************/
238  bool eval_grad_f(Ipopt::Index n, const Ipopt::Number* x, bool new_x,
239  Ipopt::Number *grad_f)
240  {
241  return true;
242  }
243 
244  /****************************************************************/
245  bool eval_g(Ipopt::Index n, const Ipopt::Number *x, bool new_x,
246  Ipopt::Index m, Ipopt::Number *g)
247  {
248  return true;
249  }
250 
251  /****************************************************************/
252  bool eval_jac_g(Ipopt::Index n, const Ipopt::Number *x, bool new_x,
253  Ipopt::Index m, Ipopt::Index nele_jac, Ipopt::Index *iRow,
254  Ipopt::Index *jCol, Ipopt::Number *values)
255  {
256  return true;
257  }
258 
259  /****************************************************************/
260  bool eval_h(Ipopt::Index n, const Ipopt::Number *x, bool new_x,
261  Ipopt::Number obj_factor, Ipopt::Index m, const Ipopt::Number *lambda,
262  bool new_lambda, Ipopt::Index nele_hess, Ipopt::Index *iRow,
263  Ipopt::Index *jCol, Ipopt::Number *values)
264  {
265  return true;
266  }
267 
268  /****************************************************************/
269  void finalize_solution(Ipopt::SolverReturn status, Ipopt::Index n,
270  const Ipopt::Number *x, const Ipopt::Number *z_L,
271  const Ipopt::Number *z_U, Ipopt::Index m,
272  const Ipopt::Number *g, const Ipopt::Number *lambda,
273  Ipopt::Number obj_value, const Ipopt::IpoptData *ip_data,
274  Ipopt::IpoptCalculatedQuantities *ip_cq)
275  {
276  error=0.0;
277  pred.clear();
278  for (size_t i=0; i<in.size(); i++)
279  {
280  Vector pred=net.predict(in[i]);
281  error+=norm2(out[i]-pred);
282  this->pred.push_back(pred);
283  }
284  error/=in.size();
285  }
286 };
287 
288 }
289 
290 }
291 
292 
293 /****************************************************************/
294 ff2LayNNTrain::ff2LayNNTrain()
295 {
296  App=new Ipopt::IpoptApplication();
297  CAST_IPOPTAPP(App)->Options()->SetNumericValue("tol",1e-8);
298  CAST_IPOPTAPP(App)->Options()->SetIntegerValue("acceptable_iter",0);
299  CAST_IPOPTAPP(App)->Options()->SetStringValue("mu_strategy","adaptive");
300  CAST_IPOPTAPP(App)->Options()->SetIntegerValue("max_iter",300);
301  CAST_IPOPTAPP(App)->Options()->SetStringValue("nlp_scaling_method","gradient-based");
302  CAST_IPOPTAPP(App)->Options()->SetStringValue("hessian_approximation","limited-memory");
303  CAST_IPOPTAPP(App)->Options()->SetStringValue("jacobian_approximation","finite-difference-values");
304  CAST_IPOPTAPP(App)->Options()->SetIntegerValue("print_level",0);
305  CAST_IPOPTAPP(App)->Options()->SetStringValue("derivative_test","none");
306  CAST_IPOPTAPP(App)->Initialize();
307 }
308 
309 
310 /****************************************************************/
311 void ff2LayNNTrain::setBounds(const Property &bounds)
312 {
313  this->bounds=bounds;
314 }
315 
316 
317 /****************************************************************/
318 bool ff2LayNNTrain::train(const unsigned int numHiddenNodes,
319  const deque<Vector> &in, const deque<Vector> &out,
320  deque<Vector> &pred, double &error)
321 {
322  if ((in.size()==0) || (in.size()!=out.size()))
323  return false;
324 
325  IW.clear();
326  IW.assign(numHiddenNodes,Vector(in.front().length(),0.0));
327  b1.resize(numHiddenNodes,0.0);
328 
329  LW.clear();
330  LW.assign(out.front().length(),Vector(numHiddenNodes,0.0));
331  b2.resize(out.front().length(),0.0);
332 
333  inMinMaxX.clear();
334  inMinMaxY.clear();
335 
336  outMinMaxX.clear();
337  outMinMaxY.clear();
338 
339  // seek for min-max of input and scale it in [-1,1]
340  const Vector &in_front=in.front();
341  for (size_t i=0; i<in_front.length(); i++)
342  {
343  minmax range;
344  range.min=range.max=in_front[i];
345  inMinMaxX.push_back(range);
346  range.min=-1.0; range.max=1.0;
347  inMinMaxY.push_back(range);
348  }
349  for (size_t i=1; i<in.size(); i++)
350  {
351  for (size_t j=0; j<in_front.length(); j++)
352  {
353  inMinMaxX[j].min=std::min(inMinMaxX[j].min,in[i][j]);
354  inMinMaxX[j].max=std::max(inMinMaxX[j].max,in[i][j]);
355  }
356  }
357 
358  // enlarge of 10%
359  for (size_t i=0; i<inMinMaxX.size(); i++)
360  {
361  inMinMaxX[i].min-=0.1*fabs(inMinMaxX[i].min);
362  inMinMaxX[i].max+=0.1*fabs(inMinMaxX[i].max);
363  }
364 
365  // seek for min-max of output and scale it in [-1,1]
366  const Vector &out_front=out.front();
367  for (size_t i=0; i<out_front.length(); i++)
368  {
369  minmax range;
370  range.min=range.max=out_front[i];
371  outMinMaxX.push_back(range);
372  range.min=-1.0; range.max=1.0;
373  outMinMaxY.push_back(range);
374  }
375  for (size_t i=1; i<out.size(); i++)
376  {
377  for (size_t j=0; j<out_front.length(); j++)
378  {
379  outMinMaxX[j].min=std::min(outMinMaxX[j].min,out[i][j]);
380  outMinMaxX[j].max=std::max(outMinMaxX[j].max,out[i][j]);
381  }
382  }
383 
384  // enlarge of 10%
385  for (size_t i=0; i<outMinMaxX.size(); i++)
386  {
387  outMinMaxX[i].min-=0.1*fabs(outMinMaxX[i].min);
388  outMinMaxX[i].max+=0.1*fabs(outMinMaxX[i].max);
389  }
390 
391  prepare();
392  configured=true;
393 
394  Ipopt::SmartPtr<ff2LayNNTrainNLP> nlp=new ff2LayNNTrainNLP(*this,bounds,true,in,out,pred);
395  Ipopt::ApplicationReturnStatus status=CAST_IPOPTAPP(App)->OptimizeTNLP(GetRawPtr(nlp));
396 
397  error=nlp->get_error();
398  return (status==Ipopt::Solve_Succeeded);
399 }
400 
401 
402 /****************************************************************/
403 bool ff2LayNNTrain::retrain(const deque<Vector> &in, const deque<Vector> &out,
404  deque<Vector> &pred, double &error)
405 {
406  if ((in.size()==0) || (in.size()!=out.size()) || !configured)
407  return false;
408 
409  Ipopt::SmartPtr<ff2LayNNTrainNLP> nlp=new ff2LayNNTrainNLP(*this,bounds,false,in,out,pred);
410  Ipopt::ApplicationReturnStatus status=CAST_IPOPTAPP(App)->OptimizeTNLP(GetRawPtr(nlp));
411 
412  error=nlp->get_error();
413  return (status==Ipopt::Solve_Succeeded);
414 }
415 
416 
417 /****************************************************************/
418 ff2LayNNTrain::~ff2LayNNTrain()
419 {
420  delete CAST_IPOPTAPP(App);
421 }
422 
423 
virtual yarp::sig::Vector predict(const yarp::sig::Vector &x) const
Predict the output given a certain input to the network.
bool eval_g(Ipopt::Index n, const Ipopt::Number *x, bool new_x, Ipopt::Index m, Ipopt::Number *g)
bool getBounds(const string &tag, double &min, double &max)
bool get_nlp_info(Ipopt::Index &n, Ipopt::Index &m, Ipopt::Index &nnz_jac_g, Ipopt::Index &nnz_h_lag, IndexStyleEnum &index_style)
bool get_bounds_info(Ipopt::Index n, Ipopt::Number *x_l, Ipopt::Number *x_u, Ipopt::Index m, Ipopt::Number *g_l, Ipopt::Number *g_u)
ff2LayNNTrainNLP(ff2LayNNTrain &_net, const Property &_bounds, const bool _randomInit, const deque< Vector > &_in, const deque< Vector > &_out, deque< Vector > &_pred)
void finalize_solution(Ipopt::SolverReturn status, Ipopt::Index n, const Ipopt::Number *x, const Ipopt::Number *z_L, const Ipopt::Number *z_U, Ipopt::Index m, const Ipopt::Number *g, const Ipopt::Number *lambda, Ipopt::Number obj_value, const Ipopt::IpoptData *ip_data, Ipopt::IpoptCalculatedQuantities *ip_cq)
bool eval_h(Ipopt::Index n, const Ipopt::Number *x, bool new_x, Ipopt::Number obj_factor, Ipopt::Index m, const Ipopt::Number *lambda, bool new_lambda, Ipopt::Index nele_hess, Ipopt::Index *iRow, Ipopt::Index *jCol, Ipopt::Number *values)
void fillVar(Ipopt::Number *x, const Ipopt::Number *x_l, const Ipopt::Number *x_u)
bool eval_jac_g(Ipopt::Index n, const Ipopt::Number *x, bool new_x, Ipopt::Index m, Ipopt::Index nele_jac, Ipopt::Index *iRow, Ipopt::Index *jCol, Ipopt::Number *values)
bool eval_f(Ipopt::Index n, const Ipopt::Number *x, bool new_x, Ipopt::Number &obj_value)
bool get_starting_point(Ipopt::Index n, bool init_x, Ipopt::Number *x, bool init_z, Ipopt::Number *z_L, Ipopt::Number *z_U, Ipopt::Index m, bool init_lambda, Ipopt::Number *lambda)
bool eval_grad_f(Ipopt::Index n, const Ipopt::Number *x, bool new_x, Ipopt::Number *grad_f)
void fillNet(const Ipopt::Number *x)
Class to deal with training of Feed-Forward 2 layers Neural Network using IpOpt.
int n
double norm2(const yarp::sig::Matrix &M, int col)
Returns the squared norm of the vector given in the form: matrix(:,col).
Definition: math.h:91
This file contains the definition of unique IDs for the body parts and the skin parts of the robot.
const FSC max
Definition: strain.h:48
const FSC min
Definition: strain.h:49
#define CAST_IPOPTAPP(x)
out
Definition: sine.m:8