-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtestBasic.cpp
353 lines (296 loc) · 9.91 KB
/
testBasic.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
/**
* @file testBasic.cpp
* @brief Basic tests of underlying functionality, or things which only take
* a short time to run!
*
*/
#include <iostream>
#define BOOST_TEST_MAIN
#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#include "test.hpp"
/**
* \brief Utility test class.
* Constructs a standard set: 10 examples, 5 ins, 2 outs:
*
* * input j of example i is i*100+j
* * output j of example i is i*200+j
* * h of example i is i*1000
*
* While the set says there are 2 h levels, this is
* untrue (however, a later test resets the H to match this)
*/
class TestExampleSet : public ExampleSet {
public:
TestExampleSet() : ExampleSet(10,5,2, 2){
for(int i=0;i<getCount();i++){
double *d = getInputs(i);
for(int j=0;j<getInputCount();j++)
d[j] = i*10+j;
d= getOutputs(i);
for(int j=0;j<getOutputCount();j++)
d[j] = i*20+j;
setH(i,i*1000);
}
}
};
/** \addtogroup basictests Core functionality tests
* \ingroup tests
* @{
*/
BOOST_AUTO_TEST_SUITE(basic)
/**
* \brief Test the basic example
*/
BOOST_AUTO_TEST_CASE(example) {
TestExampleSet e;
// just check the retrieved values are correct
for(int i=0;i<e.getCount();i++){
double *d = e.getInputs(i);
for(int j=0;j<e.getInputCount();j++)
BOOST_REQUIRE(d[j]== i*10+j);
d= e.getOutputs(i);
for(int j=0;j<e.getOutputCount();j++)
BOOST_REQUIRE(d[j]== i*20+j);
BOOST_REQUIRE(e.getH(i)==i*1000);
}
}
/**
* \brief Test that subsetting examples works
*/
BOOST_AUTO_TEST_CASE(subset) {
TestExampleSet parent;
// check that bad values throw
BOOST_REQUIRE_THROW(ExampleSet bad(parent,5,6),std::out_of_range);
BOOST_REQUIRE_THROW(ExampleSet bad(parent,-1,6),std::out_of_range);
BOOST_REQUIRE_THROW(ExampleSet bad(parent,5,6),std::out_of_range);
BOOST_REQUIRE_THROW(ExampleSet bad(parent,11,6),std::out_of_range);
ExampleSet e(parent,5,5);
BOOST_REQUIRE(e.getCount()==5);
for(int i=0;i<e.getCount();i++){
int parentIndex = i+5;
double *d = e.getInputs(i);
for(int j=0;j<e.getInputCount();j++)
BOOST_REQUIRE(d[j]== parentIndex*10+j);
d= e.getOutputs(i);
for(int j=0;j<e.getOutputCount();j++)
BOOST_REQUIRE(d[j]== parentIndex*20+j);
BOOST_REQUIRE(e.getH(i)==parentIndex*1000);
}
}
/**
* \brief simple shuffle for testing - performs a Fisher-Yates shuffle
* on an array of items of class T.
*/
template <class T> void sshuffle(T *x, int ct){
T tmp;
for(int i=ct-1;i>=1;i--){
long lr;
int j = rand()%(i+1);
tmp=x[i];
x[i]=x[j];
x[j]=tmp;
}
}
/**
* \brief Test the alternate() function
*/
BOOST_AUTO_TEST_CASE(alt) {
long t;
static const int NUMEXAMPLES = 100;
static const int CYCLE=5;
srand(time(&t));
// make a bunch of numbers and shuffle them
int arr[NUMEXAMPLES];
for(int i=0;i<NUMEXAMPLES;i++){
arr[i] = i;
}
sshuffle<int>(arr,NUMEXAMPLES);
// make them alternate odd and even
alternate<int>(arr,NUMEXAMPLES,CYCLE,[](int v){return v;});
// for(int i=0;i<NUMEXAMPLES;i++)printf("%d ",arr[i]%CYCLE);
// make sure each item is there only once and that the sequence
// alternates odd and even
bool seen[NUMEXAMPLES];
for(int i=0;i<NUMEXAMPLES;i++)seen[i]=false;
for(int i=0;i<NUMEXAMPLES;i++){
int n = arr[i];
BOOST_REQUIRE(!seen[n]);
seen[n]=true;
BOOST_REQUIRE((n%CYCLE) == (i%CYCLE));
}
}
/**
* \brief Test the alternation function on examples, simple version
*/
BOOST_AUTO_TEST_CASE(altex){
TestExampleSet e;
for(int i=0;i<e.getCount();i++){
e.setH(i, i<e.getCount()/2 ? 1:0);
}
drand48_data rd;
srand48_r(10,&rd);
e.shuffle(&rd,ExampleSet::ALTERNATE);
for(int i=0;i<e.getCount();i++){
BOOST_REQUIRE((e.getH(i)<0.5 ? 0 : 1) == i%2);
}
}
/**
* \brief test strided example shuffle, 4 different modulator levels
*/
BOOST_AUTO_TEST_CASE(stride){
static const int NUMBEREXAMPLES=32;
// 32 examples (or however many) with 2 inputs and 1 output, and 4
// different modulator values. We then create 4 different groups.
// Each group has 4 examples with the same inputs, but with different outputs
// between the groups. Examples within each group have the same modulator value.
ExampleSet e(NUMBEREXAMPLES,2,1, 4);
for(int i=0;i<NUMBEREXAMPLES/4;i++){
int exbase = i*4;
for(int j=0;j<4;j++){
int ex = exbase+j;
double *d = e.getInputs(ex);
for(int k=0;k<e.getInputCount();k++)
d[k] = k*10+j;
*e.getOutputs(ex) = ex;
e.setH(ex,i);
}
}
// shuffle the data with STRIDE, so that the four groups are each considered as
// a whole and moved en-bloc.
drand48_data rd;
srand48_r(10,&rd);
e.shuffle(&rd,ExampleSet::STRIDE);
// check the results, both that the output is non-monotonic (i.e. data is shuffled)
// and that the inputs appear to show that the blocks are intact.
int lasto = -1;
bool monotonic_increasing=true;
for(int i=0;i<NUMBEREXAMPLES;i++){
double *d = e.getInputs(i);
int i0 = (int)d[0];
int i1 = (int)d[1];
int o = (int)*e.getOutputs(i);
int h = (int)e.getH(i);
if(o<lasto)monotonic_increasing=false;
lasto=o;
BOOST_REQUIRE(i0 == i%4);
BOOST_REQUIRE(o/4 == h);
}
BOOST_REQUIRE(!monotonic_increasing);
}
/**
* \brief test strided example shuffle, 4 different modulator levels
*/
BOOST_AUTO_TEST_CASE(altex4){
static const int NUMBEREXAMPLES=32;
// 32 examples (or however many) with 2 inputs and 1 output, and 4
// different modulator values. We then create 4 different groups.
// Each group has 4 examples with the same inputs, but with different outputs
// between the groups. Examples within each group have the same modulator value.
// Concretely, examples (index,in0,in1,out,h) will be
// (0 0 10 0 0) (1 1 11 1 0) (2 2 12 2 0) (3 3 13 3 0) (4 0 10 4 0) (5 1 11 5 0)
// (6 2 12 6 0) (7 3 13 7 0)
// (8 0 10 8 1) (9 1 11 9 1) (10 2 12 10 1) (11 3 13 11 1) (12 0 10 12 1) (13 1 11 13 1)
// (14 2 12 14 1) (15 3 13 15 1)
// and so on. These should be shuffled randomly, and then rearranged so that the
// h-value (the last value in the brackets) goes (0,1,2,3,0,1,2,3...)
ExampleSet e(NUMBEREXAMPLES,2,1, 4);
for(int i=0;i<NUMBEREXAMPLES/4;i++){
int exbase = i*4;
for(int j=0;j<4;j++){
int ex = exbase+j;
double *d = e.getInputs(ex);
for(int k=0;k<e.getInputCount();k++)
d[k] = k*10+j;
*e.getOutputs(ex) = ex;
e.setH(ex,i/2); // gives four hormone levels
e.setHRange(0,i/2); // reset max h-level (will end up at 3)
}
}
// shuffle the data with ALTERNATE, so everything is shuffled freely, but
// ensure afterwards that the h-levels go 01230123....
drand48_data rd;
srand48_r(10,&rd);
e.shuffle(&rd,ExampleSet::ALTERNATE);
// check the results, both that the output is non-monotonic (i.e. data is shuffled)
// and that the h-sequence is correct
int lasto = -1;
bool monotonic_increasing=true;
for(int i=0;i<NUMBEREXAMPLES;i++){
double *d = e.getInputs(i);
int i0 = (int)d[0];
int i1 = (int)d[1];
int o = (int)*e.getOutputs(i);
int h = (int)e.getH(i);
if(o<lasto)monotonic_increasing=false;
lasto=o;
BOOST_REQUIRE(i%4==h);
BOOST_REQUIRE(o%4==i0); // check internal integrity of datum
}
BOOST_REQUIRE(!monotonic_increasing);
}
/**
* \brief set all parameters (weights and biases) in a network to zero
* \param n the network to zero
*/
void zero(Net *n){
int ct = n->getDataSize();
double *buf = new double[ct];
for(int i=0;i<ct;i++)buf[i]=0;
n->load(buf);
delete[] buf;
}
/**
* \brief Test mean sum squared error of outputs.
* This test finds the mean of the sum of the squared errors on the outputs
* across all examples in the test set, in the case of a network where
* all the parameters are zero. In this case, all outputs will be 0.5 given
* the logistic sigmoid activation function. We test for the correct value
* determined by lots of print statements during development.
*/
BOOST_AUTO_TEST_CASE(testmse) {
TestExampleSet e;
// make a standard net
Net *n = NetFactory::makeNet(NetType::PLAIN,e,2);
// zero it so all the nodes produce 0.5 as their output
zero(n);
// get the MSE on all examples, given the example values
// in the test set
double t = n->test(e);
delete n;
// value determined by running the network with a lot of
// debug printing
BOOST_REQUIRE(t==11400.25);
}
/**
* \brief Loading MNIST data and converting to an example set.
* Ensure we can load MNIST data into an example set, and that
* the image and its label are correct. The former is hard to test
* automatically, so I'll rely on having eyeballed it.
*/
BOOST_AUTO_TEST_CASE(loadmnist) {
MNIST m("../testdata/t10k-labels-idx1-ubyte","../testdata/t10k-images-idx3-ubyte");
ExampleSet e(m);
// in this data set, example 1233 should be a 5.
double *in = e.getInputs(1233);
for(int y=0;y<28;y++){
for(int x=0;x<28;x++){
uint8_t qq = *in++ * 10;
if(qq>9)putchar('?');
else putchar(qq?qq+'0':'.');
}
putchar('\n');
}
double *out = e.getOutputs(1233);
BOOST_REQUIRE(e.getOutputCount()==10);
for(int i=0;i<10;i++){
if(i==5)
BOOST_REQUIRE(out[i]==1.0);
else
BOOST_REQUIRE(out[i]==0.0);
}
}
/**
* @}
*/
BOOST_AUTO_TEST_SUITE_END()