forked from jabogithub/fann-excel
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathInterface.cpp
349 lines (308 loc) · 12.1 KB
/
Interface.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
#include "Interface.h"
#include <sstream>
#include <fstream>
#include <fann.h>
#include <xlw/xlw.h>
static std::ofstream logStreamG("FANN-excel.log", std::ios::app); // Global log stream
static const unsigned int reportingFrequency = 10; // number of reporting lines during one training
static const double defaultError = 1e-3; // default MSE
std::string
fannInqLibraryVersion()
{
std::stringstream ss;
ss << "FANNExcel 0.03; " << __TIMESTAMP__ << ", xlw: " << XLW_VERSION << ", fann: 2.1.0";
return ss.str();
}
bool // create training file for FANN network
fannCreateTrainingFile(const NEMatrix& inData // is input data matrix. Variables are in columns. Each training set in rows
, const NEMatrix& outData // is output data matrix. Variables in columns. Training sets in rows
, const std::string& trainFileName // is name of the output file
)
{
if (inData.rows() != outData.rows())
throw "Inconsitent in- and out- data. Number of row differs";
std::ofstream out(trainFileName.c_str());
// Description header: # of rows, # of in-variables, # of out-variables
out << inData.rows() << "\t" << inData.columns() << "\t" << outData.columns() << std::endl;
for (unsigned int i = 0; i<inData.rows(); ++i)
{
// In Data row
for (unsigned int j=0; j<inData.columns(); ++j)
{
out << inData(i,j);
if (j!=inData.columns()-1)
out << "\t";
else
out << std::endl;
}
// Out data row
for (unsigned int j=0; j<outData.columns(); ++j)
{
out << outData(i,j);
if (j!=outData.columns()-1)
out << "\t";
else
out << std::endl;
}
}
out.close();
return true;
}
bool // create a standard fully connected backpropagation neural network and save it into file.
fannCreateStandardArray(int nOfLayers // is number of layers
, const MyArray& neurons // vector with number of neurons in each layer (including input, hiddenand output layers)
, const std::string& netFileName // is the name of the created network file (including path)
)
{
unsigned int* layers = new unsigned int[neurons.size()];
for(unsigned int i = 0; i < neurons.size(); ++i)
layers[i] = (unsigned int)neurons[i];
struct fann* ann = fann_create_standard_array(nOfLayers, layers);
fann_set_activation_function_hidden(ann, FANN_SIGMOID_SYMMETRIC);
fann_set_activation_function_output(ann, FANN_SIGMOID_SYMMETRIC);
fann_save(ann, netFileName.c_str());
return true;
}
// TODO: print to global ostream which will be directed to a file
int FANN_API
cbPrintStatusToLogStream(struct fann *ann, struct fann_train_data *train,
unsigned int max_epochs, unsigned int epochs_between_reports,
float desired_error, unsigned int epochs)
{
logStreamG << "Epochs: " << epochs << ".\t"
<< "MSE: " << fann_get_MSE(ann) << ".\t"
<< "bit_fail: " << fann_get_bit_fail(ann) << std::endl << std::flush;
return 0;
}
struct fann* openAnnFile(const std::string& netFile)
{
struct fann* ann = fann_create_from_file(netFile.c_str());
if (ann == NULL)
throw "Cannot open network file";
fann_set_callback(ann, cbPrintStatusToLogStream);
return ann;
}
double // train network on input file
fannTrainOnFile(const std::string& netFile // is the ANN file
, const std::string& trainFile // is name of the input training data file
, int maxEpochs // maximum number of epochs,
, DoubleOrNothing desiredError // desired error (MSE)
)
{
// Load the network form the file
struct fann* ann = openAnnFile(netFile);
// Check consistency between thenetwork and the training file
std::ifstream in(trainFile.c_str());
int nOfRows, nOfInputs, nOfOutputs;
in >> nOfRows >> nOfInputs >> nOfOutputs;
in.close();
if (nOfInputs != fann_get_num_input(ann))
{
std::stringstream ss;
ss << "number of input neurons in the training file is " << nOfInputs << ", which differs from number of input neurons in the network " << fann_get_num_input(ann);
throw ss.str();
}
if (nOfOutputs != fann_get_num_output(ann))
{
std::stringstream ss;
ss << "number of output neurons in the training file is " << nOfOutputs << ", which differs from number of output neurons in the network " << fann_get_num_output(ann);
throw ss.str();
}
// Train the network
const float dError =(float) desiredError.GetValueOrDefault(defaultError);
const int epochsBetweenReports = (int)maxEpochs / reportingFrequency;
fann_train_on_file(ann, trainFile.c_str(), maxEpochs, epochsBetweenReports, dError);
double mse = fann_get_MSE(ann);
// Save the trained network to the ANN file
fann_save(ann, netFile.c_str());
fann_destroy(ann);
return mse;
}
// ---- CallBack --------
static NEMatrix& inDataGlobal = NEMatrix(); // Ugly solution: Static valrable to get data into the callback...
static NEMatrix& outDataGlobal = NEMatrix();
static void __stdcall cbTrainOnData(unsigned int rowNo, unsigned int nIn, unsigned int nOut, fann_type* inRow, fann_type* outRow)
{
for (unsigned int i = 0; i < nIn; ++i)
inRow[i] = (fann_type) inDataGlobal(rowNo,i);
for (unsigned int j = 0; j < nOut; ++j)
outRow[j] = (fann_type) outDataGlobal(rowNo, j);
}
// create fann_train_data struct
struct fann_train_data*
prepareTrainData(struct fann* ann, const NEMatrix& inData, const NEMatrix& outData)
{
// Check input/output layer consistency with data
unsigned int nOfRows = inData.rows();
unsigned int nOfInputs = inData.columns();
unsigned int nOfOutputs= outData.columns();
if (nOfInputs != fann_get_num_input(ann))
{
std::stringstream ss;
ss << "number of input neurons in the training file is " << nOfInputs << ", which differs from number of input neurons in the network " << fann_get_num_input(ann);
throw ss.str();
}
if (nOfOutputs != fann_get_num_output(ann))
{
std::stringstream ss;
ss << "number of output neurons in the training file is " << nOfOutputs << ", which differs from number of output neurons in the network " << fann_get_num_output(ann);
throw ss.str();
}
if (inData.rows() != outData.rows())
{
std::stringstream ss;
ss << "number of inData rows " << inData.rows() << " differs from number of out-data rows " << outData.rows();
throw ss.str();
}
// Create train_data struct
inDataGlobal = inData;
outDataGlobal = outData;
struct fann_train_data* data = fann_create_train_from_callback(nOfRows, nOfInputs, nOfOutputs, cbTrainOnData);
return data;
}
double
fannTrainOnData(const std::string& netFile // is the ANN file
, const NEMatrix& inData // is input data matrix. Variables are in columns. Training sets in rows
, const NEMatrix& outData // is output data matrix. Variables in columns. Training sets in rows
, int maxEpochs // is maximum number of epochs,
, DoubleOrNothing desiredError // is desired error (MSE)
)
{
// create ANN from file
struct fann* ann = openAnnFile(netFile);
// create train data
struct fann_train_data* data = prepareTrainData(ann, inData, outData);
// Train the network
const float dError =(float) desiredError.GetValueOrDefault(defaultError);
const int epochsBetweenReports = (int)maxEpochs / reportingFrequency;
fann_train_on_data(ann, data, maxEpochs, epochsBetweenReports, dError);
fann_destroy_train(data);
double mse = fann_get_MSE(ann);
// Save and destroy
fann_save(ann, netFile.c_str());
fann_destroy(ann);
return mse;
}
double // test network on set of known in- and out-data withou modifying hte network. Return MSE
fannTestOnData(const std::string& netFile // is the network definition ANN file
, const NEMatrix& inData // is input data matrix. Variables are in columns. Training sets in rows
, const NEMatrix& outData // is output data matrix. Variables in columns. Training sets in rows
)
{
// create ANN from file
struct fann* ann = openAnnFile(netFile);
// create train data
struct fann_train_data* data = prepareTrainData(ann, inData, outData);
// test + report MSE
double mse = fann_test_data(ann, data);
// Clean-up
fann_destroy_train(data);
fann_destroy(ann);
return mse;
}
double // train on in/outTrainData and report MSE on both train and test data.
fannTrainOnDataAndTest(const std::string& netFile // is the network defition ANN file
, const NEMatrix& inTrainData // is input train data matrix. Variables are in columns. Training sets in rows
, const NEMatrix& outTrainData // is output train data matrix. Variables in columns. Training sets in rows
, int maxEpochs // is maximum number of epochs,
, const NEMatrix& inTestData // is input test data matrix.
, const NEMatrix& outTestData // is output test data matrix
, DoubleOrNothing desiredError // is desired error (MSE)
)
{
// create ANN from file
struct fann* ann = openAnnFile(netFile);
// create train/test data
struct fann_train_data* trainData = prepareTrainData(ann, inTrainData, outTrainData);
struct fann_train_data* testData = prepareTrainData(ann, inTestData, outTestData);
// -- Train --
double dError = desiredError.GetValueOrDefault(defaultError);
int epochsBetweenPeriods = maxEpochs / reportingFrequency;
std::string bestTrainNetFile = netFile + ".bestTrain.net";
std::string bestTestNetFile = netFile + ".bestTest.net";
// Reset and recalculate MSE for Train and Test data
double mseTrain = 1e9;
double mseTrainMin = mseTrain;
double mseTestMin = 1e9;
logStreamG << "--> fannTrainOnData(" << netFile << ")" << std::endl
<< "Max epochs: " << maxEpochs << ".\t"
<< "Desired error: " << dError << std::endl << std::flush;
for (int i = 0; i < maxEpochs; ++i)
{
mseTrain = fann_train_epoch(ann, trainData);
// Report to log file + save the best networks if necessary
if(i % epochsBetweenPeriods == 0)
{
double mseTest = fann_test_data(ann, testData);
logStreamG << "Epochs: " << i << "\tMSE(train): " << mseTrain << "\t"
<< "Bit fail: " << fann_get_bit_fail(ann) << "\t"
<< "MSE(test): " << mseTest << "\t";
if (mseTrain < mseTrainMin)
{
mseTrainMin = mseTrain;
fann_save(ann, bestTrainNetFile.c_str());
logStreamG << "saving bestTrain.net\t";
}
if (mseTest < mseTestMin)
{
mseTestMin = mseTest;
fann_save(ann, bestTestNetFile.c_str());
logStreamG << "saving bestTest.net";
}
logStreamG << std::endl << std::flush;
}
if (mseTrain < dError)
break;
}
// Save the best
logStreamG << "*** Finished *** MSE(lastTrain): " << mseTrain << ", "
<< "MSE(bestTrain): " << mseTrainMin << ", "
<< "MSE(bestTest): " << mseTestMin << std::endl << std::flush;
fann_save(ann, netFile.c_str());
// clean-up
fann_destroy_train(trainData);
fann_destroy_train(testData);
fann_destroy(ann);
return mseTrain;
}
NEMatrix // run input through the neural network, returning an array of outputs, The variables are incolumns (equal to # of neurons inoutput layer), sets are in rows (equal to # of rows of the input data)
fannRun(const std::string& netFile // is the network definition ANN file
, const NEMatrix& inData // is input data matrix. Variables are in columns. Sets is in rows
)
{
// create ANN from file
struct fann* ann = openAnnFile(netFile);
// prepare variables
unsigned int nOfRows = inData.rows();
unsigned int nOfInput = inData.columns();
unsigned int nOfOutput = fann_get_num_output(ann);
NEMatrix outData(nOfRows, nOfOutput);
fann_type* input = new fann_type[nOfInput];
// run neural network fit
for (unsigned int i = 0; i < nOfRows; ++i)
{
for (unsigned int j = 0; j < nOfInput; ++j)
input[j] = (fann_type)inData(i,j);
fann_type* output = fann_run(ann, input);
for (unsigned int j = 0; j < nOfOutput; ++j)
outData(i,j) = output[j];
}
// clean up
delete input;
fann_destroy(ann);
return outData;
}
bool
fannSetBitFailLimit(const std::string& netFile // is the network definition ANN file
, double bitFailLimit // is the new bit fail limit
)
{
// create ANN from file
struct fann* ann = openAnnFile(netFile);
// Set the limit
fann_set_bit_fail_limit(ann, (fann_type)bitFailLimit);
// Clean-up
fann_save(ann, netFile.c_str());
fann_destroy(ann);
return true;
}