pktools  2.6.3
Processing Kernel for geospatial data
pkregann.cc
1 /**********************************************************************
2 pkregann.cc: regression with artificial neural network (multi-layer perceptron)
3 Copyright (C) 2008-2014 Pieter Kempeneers
4 
5 This file is part of pktools
6 
7 pktools is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11 
12 pktools is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with pktools. If not, see <http://www.gnu.org/licenses/>.
19 ***********************************************************************/
20 #include <vector>
21 #include <fstream>
22 #include "base/Optionpk.h"
23 #include "fileclasses/FileReaderAscii.h"
24 #include "floatfann.h"
25 #include "algorithms/myfann_cpp.h"
26 using namespace std;
27 
28 int main(int argc, char *argv[])
29 {
30  //--------------------------- command line options ------------------------------------
31  Optionpk<string> input_opt("i", "input", "input ASCII file");
32  Optionpk<string> output_opt("o", "output", "output ASCII file for result");
33  Optionpk<int> inputCols_opt("ic", "inputCols", "input columns (e.g., for three dimensional input data in first three columns use: -ic 0 -ic 1 -ic 2");
34  Optionpk<int> outputCols_opt("oc", "outputCols", "output columns (e.g., for two dimensional output in columns 3 and 4 (starting from 0) use: -oc 3 -oc 4");
35  Optionpk<string> training_opt("t", "training", "training ASCII file (each row represents one sampling unit. Input features should be provided as columns, followed by output)");
36  Optionpk<double> from_opt("from", "from", "start from this row in training file (start from 0)",0);
37  Optionpk<double> to_opt("to", "to", "read until this row in training file (start from 0 or set leave 0 as default to read until end of file)", 0);
38  Optionpk<double> offset_opt("\0", "offset", "offset value for each spectral band input features: refl[band]=(DN[band]-offset[band])/scale[band]", 0.0);
39  Optionpk<double> scale_opt("\0", "scale", "scale value for each spectral band input features: refl=(DN[band]-offset[band])/scale[band] (use 0 if scale min and max in each band to -1.0 and 1.0)", 0.0);
40  Optionpk<unsigned short> cv_opt("cv", "cv", "n-fold cross validation mode",0);
41  Optionpk<unsigned int> nneuron_opt("nn", "nneuron", "number of neurons in hidden layers in neural network (multiple hidden layers are set by defining multiple number of neurons: -n 15 -n 1, default is one hidden layer with 5 neurons)", 5);
42  Optionpk<float> connection_opt("\0", "connection", "connection reate (default: 1.0 for a fully connected network)", 1.0);
43  // Optionpk<float> weights_opt("w", "weights", "weights for neural network. Apply to fully connected network only, starting from first input neuron to last output neuron, including the bias neurons (last neuron in each but last layer)", 0.0);
44  Optionpk<float> learning_opt("l", "learning", "learning rate (default: 0.7)", 0.7);
45  Optionpk<unsigned int> maxit_opt("\0", "maxit", "number of maximum iterations (epoch) (default: 500)", 500);
46  Optionpk<short> verbose_opt("v", "verbose", "set to: 0 (results only), 1 (confusion matrix), 2 (debug)",0,2);
47 
48  offset_opt.setHide(1);
49  scale_opt.setHide(1);
50  connection_opt.setHide(1);
51  learning_opt.setHide(1);
52  maxit_opt.setHide(1);
53 
54  bool doProcess;//stop process when program was invoked with help option (-h --help)
55  try{
56  doProcess=input_opt.retrieveOption(argc,argv);
57  training_opt.retrieveOption(argc,argv);
58  inputCols_opt.retrieveOption(argc,argv);
59  outputCols_opt.retrieveOption(argc,argv);
60  output_opt.retrieveOption(argc,argv);
61  from_opt.retrieveOption(argc,argv);
62  to_opt.retrieveOption(argc,argv);
63  cv_opt.retrieveOption(argc,argv);
64  nneuron_opt.retrieveOption(argc,argv);
65  offset_opt.retrieveOption(argc,argv);
66  scale_opt.retrieveOption(argc,argv);
67  connection_opt.retrieveOption(argc,argv);
68  // weights_opt.retrieveOption(argc,argv);
69  learning_opt.retrieveOption(argc,argv);
70  maxit_opt.retrieveOption(argc,argv);
71  verbose_opt.retrieveOption(argc,argv);
72  }
73  catch(string predefinedString){
74  std::cout << predefinedString << std::endl;
75  exit(0);
76  }
77  if(!doProcess){
78  cout << endl;
79  cout << "Usage: pkregann -i input -t training [-ic col]* [-oc col]* -o output" << endl;
80  cout << endl;
81  std::cout << "short option -h shows basic options only, use long option --help to show all options" << std::endl;
82  exit(0);//help was invoked, stop processing
83  }
84 
85  unsigned int ninput=inputCols_opt.size();
86  unsigned int noutput=outputCols_opt.size();
87  assert(ninput);
88  assert(noutput);
89  vector< vector<float> > inputUnits;
90  vector< vector<float> > trainingUnits;
91  vector< vector<float> > trainingOutput;
92  FileReaderAscii inputFile;
93  unsigned int inputSize=0;
94  if(input_opt.size()){
95  inputFile.open(input_opt[0]);
96  inputFile.setMinRow(from_opt[0]);
97  inputFile.setMaxRow(to_opt[0]);
98  inputFile.setComment('#');
99  inputFile.readData(inputUnits,inputCols_opt,1,0,true,verbose_opt[0]);
100  inputFile.close();
101  inputSize=inputUnits.size();
102  }
103  FileReaderAscii trainingFile(training_opt[0]);
104  unsigned int sampleSize=0;
105  trainingFile.setMinRow(from_opt[0]);
106  trainingFile.setMaxRow(to_opt[0]);
107  trainingFile.setComment('#');
108  trainingFile.readData(trainingUnits,inputCols_opt,1,0,true,verbose_opt[0]);
109  trainingFile.readData(trainingOutput,outputCols_opt,1,0,true,verbose_opt[0]);
110  trainingFile.close();
111  sampleSize=trainingUnits.size();
112 
113  if(verbose_opt[0]>1){
114  std::cout << "sampleSize: " << sampleSize << std::endl;
115  std::cout << "ninput: " << ninput << std::endl;
116  std::cout << "noutput: " << noutput << std::endl;
117  std::cout << "trainingUnits[0].size(): " << trainingUnits[0].size() << std::endl;
118  std::cout << "trainingOutput[0].size(): " << trainingOutput[0].size() << std::endl;
119  std::cout << "trainingUnits.size(): " << trainingUnits.size() << std::endl;
120  std::cout << "trainingOutput.size(): " << trainingOutput.size() << std::endl;
121  }
122 
123  assert(ninput==trainingUnits[0].size());
124  assert(noutput==trainingOutput[0].size());
125  assert(trainingUnits.size()==trainingOutput.size());
126 
127  //set scale and offset
128  if(offset_opt.size()>1)
129  assert(offset_opt.size()==ninput);
130  if(scale_opt.size()>1)
131  assert(scale_opt.size()==ninput);
132 
133  std::vector<float> offset_input(ninput);
134  std::vector<float> scale_input(ninput);
135 
136  std::vector<float> offset_output(noutput);
137  std::vector<float> scale_output(noutput);
138 
139  for(int iinput=0;iinput<ninput;++iinput){
140  if(verbose_opt[0]>=1)
141  cout << "scaling for input feature" << iinput << endl;
142  offset_input[iinput]=(offset_opt.size()==1)?offset_opt[0]:offset_opt[iinput];
143  scale_input[iinput]=(scale_opt.size()==1)?scale_opt[0]:scale_opt[iinput];
144  //search for min and maximum
145  if(scale_input[iinput]<=0){
146  float theMin=trainingUnits[0][iinput];
147  float theMax=trainingUnits[0][iinput];
148  for(int isample=0;isample<trainingUnits.size();++isample){
149  if(theMin>trainingUnits[isample][iinput])
150  theMin=trainingUnits[isample][iinput];
151  if(theMax<trainingUnits[isample][iinput])
152  theMax=trainingUnits[isample][iinput];
153  }
154  offset_input[iinput]=theMin+(theMax-theMin)/2.0;
155  scale_input[iinput]=(theMax-theMin)/2.0;
156  if(verbose_opt[0]>=1){
157  std::cout << "Extreme image values for input feature " << iinput << ": [" << theMin << "," << theMax << "]" << std::endl;
158  std::cout << "Using offset, scale: " << offset_input[iinput] << ", " << scale_input[iinput] << std::endl;
159  std::cout << "scaled values for input feature " << iinput << ": [" << (theMin-offset_input[iinput])/scale_input[iinput] << "," << (theMax-offset_input[iinput])/scale_input[iinput] << "]" << std::endl;
160  }
161  }
162  }
163 
164  for(int ioutput=0;ioutput<noutput;++ioutput){
165  if(verbose_opt[0]>=1)
166  cout << "scaling for output feature" << ioutput << endl;
167  //search for min and maximum
168  float theMin=trainingOutput[0][ioutput];
169  float theMax=trainingOutput[0][ioutput];
170  for(int isample=0;isample<trainingOutput.size();++isample){
171  if(theMin>trainingOutput[isample][ioutput])
172  theMin=trainingOutput[isample][ioutput];
173  if(theMax<trainingOutput[isample][ioutput])
174  theMax=trainingOutput[isample][ioutput];
175  }
176  offset_output[ioutput]=theMin+(theMax-theMin)/2.0;
177  scale_output[ioutput]=(theMax-theMin)/2.0;
178  if(verbose_opt[0]>=1){
179  std::cout << "Extreme image values for output feature " << ioutput << ": [" << theMin << "," << theMax << "]" << std::endl;
180  std::cout << "Using offset, scale: " << offset_output[ioutput] << ", " << scale_output[ioutput] << std::endl;
181  std::cout << "scaled values for output feature " << ioutput << ": [" << (theMin-offset_output[ioutput])/scale_output[ioutput] << "," << (theMax-offset_output[ioutput])/scale_output[ioutput] << "]" << std::endl;
182  }
183  }
184 
185 
186 
187  FANN::neural_net net;//the neural network
188 
189  const unsigned int num_layers = nneuron_opt.size()+2;
190  const float desired_error = 0.0003;
191  const unsigned int iterations_between_reports = (verbose_opt[0])? maxit_opt[0]+1:0;
192  if(verbose_opt[0]>=1){
193  cout << "creating artificial neural network with " << nneuron_opt.size() << " hidden layer, having " << endl;
194  for(int ilayer=0;ilayer<nneuron_opt.size();++ilayer)
195  cout << nneuron_opt[ilayer] << " ";
196  cout << "neurons" << endl;
197  }
198 
199  switch(num_layers){
200  case(3):{
201  unsigned int layers[3];
202  layers[0]=ninput;
203  layers[1]=nneuron_opt[0];
204  layers[2]=noutput;
205  net.create_sparse_array(connection_opt[0],num_layers,layers);
206  // net.create_sparse(connection_opt[0],num_layers, ninput, nneuron_opt[0], noutput);
207  break;
208  }
209  case(4):{
210  unsigned int layers[3];
211  layers[0]=ninput;
212  layers[1]=nneuron_opt[0];
213  layers[2]=nneuron_opt[1];
214  layers[3]=noutput;
215  net.create_sparse_array(connection_opt[0],num_layers,layers);
216  // net.create_sparse(connection_opt[0],num_layers, ninput, nneuron_opt[0], nneuron_opt[1], noutput);
217  break;
218  }
219  default:
220  cerr << "Only 1 or 2 hidden layers are supported!" << endl;
221  exit(1);
222  break;
223  }
224  if(verbose_opt[0]>=1)
225  cout << "network created" << endl;
226 
227  net.set_learning_rate(learning_opt[0]);
228 
229  // net.set_activation_steepness_hidden(1.0);
230  // net.set_activation_steepness_output(1.0);
231 
232  net.set_activation_function_hidden(FANN::SIGMOID_SYMMETRIC_STEPWISE);
233  net.set_activation_function_output(FANN::SIGMOID_SYMMETRIC_STEPWISE);
234 
235  // Set additional properties such as the training algorithm
236  // net.set_training_algorithm(FANN::TRAIN_QUICKPROP);
237 
238  // Output network type and parameters
239  if(verbose_opt[0]>=1){
240  cout << endl << "Network Type : ";
241  switch (net.get_network_type())
242  {
243  case FANN::LAYER:
244  cout << "LAYER" << endl;
245  break;
246  case FANN::SHORTCUT:
247  cout << "SHORTCUT" << endl;
248  break;
249  default:
250  cout << "UNKNOWN" << endl;
251  break;
252  }
253  net.print_parameters();
254  }
255 
256  if(verbose_opt[0]>=1){
257  cout << "Max Epochs " << setw(8) << maxit_opt[0] << ". "
258  << "Desired Error: " << left << desired_error << right << endl;
259  }
260  bool initWeights=true;
261 
262  Vector2d<float> trainingFeatures(sampleSize,ninput);
263  for(unsigned int isample=0;isample<sampleSize;++isample){
264  for(unsigned int iinput=0;iinput<ninput;++iinput)
265  trainingFeatures[isample][iinput]=(trainingUnits[isample][iinput]-offset_input[iinput])/scale_input[iinput];
266  }
267 
268  Vector2d<float> scaledOutput(sampleSize,noutput);
269  for(unsigned int isample=0;isample<sampleSize;++isample){
270  for(unsigned int ioutput=0;ioutput<noutput;++ioutput)
271  scaledOutput[isample][ioutput]=(trainingOutput[isample][ioutput]-offset_output[ioutput])/scale_output[ioutput];
272  }
273 
274  if(cv_opt[0]){
275  if(verbose_opt[0])
276  std::cout << "cross validation" << std::endl;
277  std::vector< std::vector<float> > referenceVector;
278  std::vector< std::vector<float> > outputVector;
279  net.cross_validation(trainingFeatures,
280  scaledOutput,
281  cv_opt[0],
282  maxit_opt[0],
283  desired_error,
284  referenceVector,
285  outputVector);
286  assert(referenceVector.size()==outputVector.size());
287  vector<double> rmse(noutput);
288  for(int isample=0;isample<referenceVector.size();++isample){
289  std::cout << isample << " ";
290  for(int ioutput=0;ioutput<noutput;++ioutput){
291  if(!isample)
292  rmse[ioutput]=0;
293  double ref=scale_output[ioutput]*referenceVector[isample][ioutput]+offset_output[ioutput];
294  double val=scale_output[ioutput]*outputVector[isample][ioutput]+offset_output[ioutput];
295  rmse[ioutput]+=(ref-val)*(ref-val);
296  std::cout << ref << " " << val;
297  if(ioutput<noutput-1)
298  std::cout << " ";
299  else
300  std::cout << std::endl;
301  }
302  }
303  for(int ioutput=0;ioutput<noutput;++ioutput)
304  std::cout << "rmse output variable " << ioutput << ": " << sqrt(rmse[ioutput]/referenceVector.size()) << std::endl;
305  }
306 
307 
308  net.train_on_data(trainingFeatures,
309  scaledOutput,
310  initWeights,
311  maxit_opt[0],
312  iterations_between_reports,
313  desired_error);
314 
315 
316  if(verbose_opt[0]>=2){
317  net.print_connections();
318  vector<fann_connection> convector;
319  net.get_connection_array(convector);
320  for(int i_connection=0;i_connection<net.get_total_connections();++i_connection)
321  cout << "connection " << i_connection << ": " << convector[i_connection].weight << endl;
322  }
323  //end of training
324 
325  ofstream outputStream;
326  if(!output_opt.empty())
327  outputStream.open(output_opt[0].c_str(),ios::out);
328  for(unsigned int isample=0;isample<inputUnits.size();++isample){
329  std::vector<float> inputFeatures(ninput);
330  for(unsigned int iinput=0;iinput<ninput;++iinput)
331  inputFeatures[iinput]=(inputUnits[isample][iinput]-offset_input[iinput])/scale_input[iinput];
332  vector<float> result(noutput);
333  result=net.run(inputFeatures);
334 
335  if(!output_opt.empty())
336  outputStream << isample << " ";
337  else
338  std::cout << isample << " ";
339  if(verbose_opt[0]){
340  for(unsigned int iinput=0;iinput<ninput;++iinput){
341  if(output_opt.size())
342  outputStream << inputUnits[isample][iinput] << " ";
343  else
344  std::cout << inputUnits[isample][iinput] << " ";
345  }
346  }
347  for(unsigned int ioutput=0;ioutput<noutput;++ioutput){
348  result[ioutput]=scale_output[ioutput]*result[ioutput]+offset_output[ioutput];
349  if(output_opt.size()){
350  outputStream << result[ioutput];
351  if(ioutput<noutput-1)
352  outputStream << " ";
353  else
354  outputStream << std::endl;
355  }
356  else{
357  std::cout << result[ioutput];
358  if(ioutput<noutput-1)
359  std::cout << " ";
360  else
361  std::cout << std::endl;
362  }
363  }
364  }
365  if(!output_opt.empty())
366  outputStream.close();
367 }