UESMANN CPP  1.0
Reference implementation of UESMANN
/home/travis/build/jimfinnis/uesmanncpp/testTrainBasic.cpp
Go to the documentation of this file.
1 
7 #include <iostream>
8 #include <boost/test/unit_test.hpp>
9 
10 #include "test.hpp"
11 
12 BOOST_AUTO_TEST_SUITE(basictrain)
13 
14 
25 BOOST_AUTO_TEST_CASE(trainparams) {
26  const int NUMEXAMPLES=1000;
27  ExampleSet e(NUMEXAMPLES,1,1 ,1); // 100 examples at 1 input, 1 output, 1 mod level
28 
29  double recipNE = 1.0/(double)NUMEXAMPLES;
30  // generate examples of the identity function y=x, from 0 to 1.
31  // This will train but will be a bit iffy at the ends!
32 
33  for(double i=0;i<NUMEXAMPLES;i++){
34  double v = i*recipNE;
35  *(e.getInputs(i)) = v;
36  *(e.getOutputs(i)) = v;
37  e.setH(i,0);
38  }
39 
40  // set up a net which conforms to those examples with 3 hidden nodes.
42 
43  // eta=1, lots of iterations
44  Net::SGDParams params(1,10000000);
45 
46  // use half of the data as CV examples, 1000 CV cycles, 10 slices.
47  // Don't shuffle the CV examples on epoch. Also, store the best net
48  // and make sure we end up with that.
49  params.crossValidation(e,0.5,1000,10,false).storeBest().setSeed(0);
50 
51  // do the training and get the MSE of the best net.
52  double mse = net->trainSGD(e,params);
53  printf("%f\n",mse);
54  // assert that it's a sensible value
55  BOOST_REQUIRE(mse>0);
56  BOOST_REQUIRE(mse<0.005);
57 #if 0
58  for(double i=0;i<NUMEXAMPLES;i++){
59  double v = i*recipNE;
60  double o = *(net->run(&v));
61  printf(" %f,%f\n",v,o);
62  }
63 #endif
64  delete net;
65 }
66 
72 BOOST_AUTO_TEST_CASE(trainparams2) {
73  const int NUMEXAMPLES=100;
74  ExampleSet e(NUMEXAMPLES*2,1,1,1); // 100 examples at 1 input, 1 output, 1 modulator level
75 
76  double recipNE = 1.0/(double)NUMEXAMPLES;
77 
78  for(double i=0;i<NUMEXAMPLES*2;i+=2){
79  double v = (i/2)*recipNE;
80  *(e.getInputs(i)) = v;
81  *(e.getOutputs(i)) = v;
82  *(e.getInputs(i+1)) = v;
83  *(e.getOutputs(i+1)) = v;
84  e.setH(i,0);
85  e.setH(i+1,1);
86  }
87 
89 
90  // eta=1, 10000000 iterations. No CV.
91  Net::SGDParams params(1,10000000);
92  params.storeBest();
93 
94  // do the training and get the MSE of the best net.
95  double mse = net->trainSGD(e,params);
96  printf("%f\n",mse);
97 #if 0
98  for(double i=0;i<NUMEXAMPLES;i++){
99  double v = i*recipNE;
100  double o = *(net->run(&v));
101  printf("%f -> %f\n",v,o);
102  }
103 #endif
104  // assert that it's a sensible value
105  BOOST_REQUIRE(mse>0);
106  BOOST_REQUIRE(mse<0.005);
107 
108  delete net;
109 }
110 
112 
119  // 1000 examples, 2 inputs, 1 output, 1 modulator level (i.e. no modulation)
120  ExampleSet e(1000,2,1,1);
121 
122  // initialise a PRNG
123  drand48_data rd;
124  srand48_r(10,&rd);
125 
126  // create the examples
127  for(int i=0;i<1000;i++){
128  // get a pointer to the inputs for this example
129  double *ins = e.getInputs(i);
130  // and a pointer to the outputs (only one of them in this case)
131  double *out = e.getOutputs(i);
132 
133  // use the PRNG to generate the operands in the range [0,0.5) to ensure
134  // that the result is <1.
135  double a,b;
136  drand48_r(&rd,&a);a*=0.5;
137  drand48_r(&rd,&b);b*=0.5;
138 
139  // write the inputs and the output
140  ins[0] = a;
141  ins[1] = b;
142  *out = a+b;
143  }
144 
145  // create a plain backprop network
146  // which conforms to those examples with 2 hidden nodes.
148 
149  // set up training parameters:
150  // eta=1, lots of iterations
151  Net::SGDParams params(1,10000000);
152  // cross validation etc.:
153  // use half of the data as CV examples. This is divided into 10 slices,
154  // and we do cross-validation 1000 times during the run.
155  // Don't shuffle the CV examples on epoch. Also, store the best net
156  // and make sure we end up with that. We also set a PRNG seed to
157  // ensure reproducibility.
158  params.crossValidation(e, // example set
159  0.5, // proportion of set to hold back for CV
160  1000, // number of CV cycles
161  10, // number of CV slices
162  false // don't shuffle the entire CV set on completing an epoch
163  )
164  .storeBest() // store the best net inside this param block
165  .setSeed(0); // initialise PRNG for net, used for initial weights and shuffling.
166 
167  // do the training and get the MSE of the best net, found by cross-validation.
168  double mse = net->trainSGD(e,params);
169  printf("%f\n",mse);
170  // check the MSE
171  BOOST_REQUIRE(mse<0.03);
172 
173  // test the actual performance - loop through lots of pairs
174  // of numbers <0.5 (the only numbers we can do given the range of the function)
175  for(double a=0;a<0.5;a+=0.02){
176  for(double b=0;b<0.5;b+=0.02){
177  // set up an array holding the inputs
178  double runIns[2];
179  runIns[0]=a;
180  runIns[1]=b;
181  // run the net and get the output
182  double out = *(net->run(runIns));
183  // check the difference (with a line commented out to print it)
184  double diff = fabs(out-(a+b));
185 // printf("%f+%f=%f (%f)\n",a,b,out,diff);
186  BOOST_REQUIRE(diff<0.05);
187  }
188  }
189  delete net;
190 }
191 
193 
195 
201 BOOST_AUTO_TEST_CASE(additionmod) {
202  // 2000 examples, 2 inputs, 1 output, 2 modulator levels. We need to know
203  // the number of modulator levels so that example shuffling will shuffle in
204  // blocks of that count, or will shuffle normally and then fix up to ensure that
205  // example modulator levels alternate in the net (ExampleSet::STRIDE and
206  // ExampleNet::ALTERNATE respectively).
207 
208  ExampleSet e(2000,2,1,2);
209 
210  // initialise a PRNG
211  drand48_data rd;
212  srand48_r(10,&rd);
213 
214  // create the examples
215  int idx=0; // example index
216  for(int i=0;i<1000;i++){
217  // use the PRNG to generate the operands in the range [0,0.5) to ensure
218  // that the result is <1.
219  double a,b;
220  drand48_r(&rd,&a);a*=0.5;
221  drand48_r(&rd,&b);b*=0.5;
222 
223  // get a pointer to the inputs for the h=0 example and write to it
224  double *ins = e.getInputs(idx);
225  double *out = e.getOutputs(idx);
226  ins[0] = a;
227  ins[1] = b;
228  *out = a+b;
229  e.setH(idx,0); // set modulator for this example
230  idx++; // increment the example index
231 
232  // Do the same for the h=1 example, but here we're writing 0.3(a+b),
233  // and the modulator is 1.
234  ins = e.getInputs(idx);
235  out = e.getOutputs(idx);
236  ins[0] = a;
237  ins[1] = b;
238  *out = (a+b)*0.3;
239  e.setH(idx,1);
240  idx++;
241 
242  }
243 
244  // create a UESMANN network which conforms to those examples with 2 hidden nodes.
246 
247  // set up training parameters:
248  // eta=1, lots of iterations
249  Net::SGDParams params(1,1000000);
250  // cross validation etc.:
251  // use half of the data as CV examples. This is divided into 10 slices,
252  // and we do cross-validation 1000 times during the run.
253  // DO shuffle the CV examples on epoch. Also, store the best net
254  // and make sure we end up with that. We also set a PRNG seed to
255  // ensure reproducibility.
256  params.crossValidation(e, // example set
257  0.5, // proportion of set to hold back for CV
258  1000, // number of CV cycles
259  10, // number of CV slices
260  true // shuffle the entire CV set on completing an epoch
261  )
262  .storeBest() // store the best net inside this param block
263  .setSeed(0); // initialise PRNG for net, used for initial weights and shuffling.
264 
265  // do the training and get the MSE of the best net, found by cross-validation.
266  double mse = net->trainSGD(e,params);
267  printf("%f\n",mse);
268  // check the MSE
269  BOOST_REQUIRE(mse<0.03);
270 
271  // test the actual performance - loop through lots of pairs
272  // of numbers. We limit the range here; performance is known to fall
273  // off at the ends due to node saturation (probably)
274  for(double a=0.1;a<0.4;a+=0.02){
275  for(double b=0.1;b<0.4;b+=0.02){
276  // set up an array holding the inputs
277  double runIns[2];
278  runIns[0]=a;
279  runIns[1]=b;
280  // check for H=0
281  net->setH(0);
282  double out = *(net->run(runIns));
283  // check the difference (with a line commented out to print it)
284  double diff = fabs(out-(a+b));
285  printf("%f+%f=%f (%f)\n",a,b,out,diff);
286  BOOST_REQUIRE(diff<0.07);
287 
288  // check for H=1
289  net->setH(1);
290  out = *(net->run(runIns));
291  diff = fabs(out-(a+b)*0.3);
292  printf("%f+%f=%f (%f)\n",a,b,out,diff);
293  BOOST_REQUIRE(diff<0.07);
294  }
295  }
296  delete net;
297 }
299 
300 
302 
307  // Create an MNIST object, which consists of labelled data in the standard MNIST
308  // format (http://yann.lecun.com/exdb/mnist/). This is in two files, one containing
309  // the images and one containing the data.
310 
311  MNIST m("../testdata/train-labels-idx1-ubyte","../testdata/train-images-idx3-ubyte");
312 
313  // This ExampleSet constructor builds the examples directly from the MNIST data,
314  // with a large number of inputs (28x28) and a number of outputs equal to the maximum
315  // label value + 1. The outputs of the examples are in a one-hot format: for handwritten
316  // digits, there will be 10 in which the output corresponding to the label will
317  // be 1 with the others 0.
318  ExampleSet e(m);
319 
320  // create a plain MLP network conforming to the examples' input and output counts
321  // with 16 hidden nodes
323 
324  // set up the parameters
325  Net::SGDParams params(0.1,10000); // eta,iterations
326 
327  // use half of the data as CV examples, 1000 CV cycles, 10 slices.
328  // Shuffle the CV examples on epoch. Also, store the best net
329  // and make sure we end up with that. Set the seed to 10.
330  // Shuffle mode is stride, the default (not that it matters here)
331 
332  params.crossValidation(e,0.5,1000,10,true)
333  .storeBest()
334  .setSeed(10);
335 
336  // train and get the MSE, and test it is low.
337  double mse = n->trainSGD(e,params);
338  BOOST_REQUIRE(mse<0.03);
339 
340  // now load the test set of 10000 images and labels and construct
341  // examples in a similar way.
342  MNIST mtest("../testdata/t10k-labels-idx1-ubyte","../testdata/t10k-images-idx3-ubyte");
343  ExampleSet testSet(mtest);
344 
345  // and test against the test set, recording how many are good.
346  int correct=0;
347  for(int i=0;i<testSet.getCount();i++){
348  // for each test, get the inputs
349  double *ins = testSet.getInputs(i);
350  // run them through the network and get the outputs
351  double *o = n->run(ins);
352  // find the correct label by getting the highest output in the example
353  int correctLabel = getHighest(testSet.getOutputs(i),testSet.getOutputCount());
354  // find the network's result by getting its highest output
355  int netLabel = getHighest(o,testSet.getOutputCount());
356  // and increment the count if they agree
357  if(correctLabel==netLabel)correct++;
358  }
359 
360  // get the ratio of correct answers -
361  // we've not trained for long so this isn't going to be brilliant performance.
362  double ratio = ((double)correct)/(double)testSet.getCount();
363  printf("MSE=%f, correct=%d/%d=%f\n",mse,correct,testSet.getCount(),ratio);
364  // assert that it's at least 85%
365  BOOST_REQUIRE(ratio>0.85);
366  delete n;
367 }
369 
375 BOOST_AUTO_TEST_SUITE_END()
int getCount() const
get the number of examples
Definition: data.hpp:327
void setH(int example, double h)
Set the h (modulator) for a given example.
Definition: data.hpp:378
Training parameters for trainSGD(). This structure holds the parameters for the trainSGD() method...
Definition: net.hpp:173
SGDParams & crossValidation(const ExampleSet &examples, double propCV, int cvCount, int cvSlices, bool cvShuf=true)
Set up the cross-validation parameters given the full training set, the proportion to be used for CV...
Definition: net.hpp:356
This class encapsulates and loads data in the standard MNIST format. The data resides in two files...
Definition: mnist.hpp:24
Useful stuff for testing.
double trainSGD(ExampleSet &examples, SGDParams &params)
Train using stochastic gradient descent. Note that cross-validation parameters are slightly different...
Definition: net.hpp:428
virtual void setH(double h)=0
Set the modulator level for subsequent runs and training of this network.
double * getOutputs(int example)
Get a pointer to the outputs for a given example, for reading or writing.
Definition: data.hpp:349
SGDParams & storeBest()
set up a "best net buffer" to store the best network found, to which the network will be set on compl...
Definition: net.hpp:394
SGDParams & setSeed(long v)
fluent setter for seed
Definition: net.hpp:275
double * run(double *in)
Run the network on some data.
Definition: net.hpp:107
static Net * makeNet(NetType t, ExampleSet &e, int hnodes)
Construct a single hidden layer network of a given type which conforms to the example set...
Definition: netFactory.hpp:32
int getOutputCount() const
get the number of outputs in all examples
Definition: data.hpp:319
h-as-input
BOOST_AUTO_TEST_CASE(trainparams)
Test training. This just checks that the network trains.
The abstract network type upon which all others are based. It&#39;s not pure virtual, in that it encapsul...
Definition: net.hpp:39
double ins[][2]
possible inputs to boolean functions
Definition: genBoolMap.cpp:32
double * getInputs(int example)
Get a pointer to the inputs for a given example, for reading or writing.
Definition: data.hpp:338
A set of example data. Each datum consists of hormone (i.e. modulator value), inputs and outputs...
Definition: data.hpp:57