25 #include "base/Optionpk.h"
26 #include "imageclasses/ImgReaderOgr.h"
27 #include "algorithms/ConfusionMatrix.h"
28 #include "algorithms/CostFactory.h"
29 #include "algorithms/FeatureSelector.h"
30 #include "floatfann.h"
31 #include "algorithms/myfann_cpp.h"
40 #define Malloc(type,n) (type *)malloc((n)*sizeof(type))
42 CostFactoryANN::CostFactoryANN(
const vector<unsigned int>& nneuron,
float connection,
const std::vector<float> weights,
float learning,
unsigned int maxit,
unsigned short cv,
bool verbose)
43 :
CostFactory(cv,verbose), m_nneuron(nneuron), m_connection(connection), m_weights(weights), m_learning(learning), m_maxit(maxit){};
45 CostFactoryANN::~CostFactoryANN(){
48 double CostFactoryANN::getCost(
const vector<
Vector2d<float> > &trainingFeatures)
50 unsigned short nclass=trainingFeatures.size();
51 unsigned int ntraining=0;
53 for(
int iclass=0;iclass<nclass;++iclass){
54 ntraining+=m_nctraining[iclass];
55 ntest+=m_nctest[iclass];
61 unsigned short nFeatures=trainingFeatures[0][0].size();
64 const unsigned int num_layers = m_nneuron.size()+2;
65 const float desired_error = 0.0003;
66 const unsigned int iterations_between_reports = (m_verbose) ? m_maxit+1:0;
68 cout <<
"creating artificial neural network with " << m_nneuron.size() <<
" hidden layer, having " << endl;
69 for(
int ilayer=0;ilayer<m_nneuron.size();++ilayer)
70 cout << m_nneuron[ilayer] <<
" ";
71 cout <<
"neurons" << endl;
75 unsigned int layers[3];
77 layers[1]=m_nneuron[0];
79 net.create_sparse_array(m_connection,num_layers,layers);
83 unsigned int layers[4];
85 layers[1]=m_nneuron[0];
86 layers[2]=m_nneuron[1];
88 net.create_sparse_array(m_connection,num_layers,layers);
92 cerr <<
"Only 1 or 2 hidden layers are supported!" << endl;
97 net.set_learning_rate(m_learning);
99 net.set_activation_function_hidden(FANN::SIGMOID_SYMMETRIC_STEPWISE);
100 net.set_activation_function_output(FANN::SIGMOID_SYMMETRIC_STEPWISE);
102 vector<unsigned short> referenceVector;
103 vector<unsigned short> outputVector;
105 vector<Vector2d<float> > tmpFeatures(nclass);
106 for(
int iclass=0;iclass<nclass;++iclass){
107 tmpFeatures[iclass].resize(trainingFeatures[iclass].size(),nFeatures);
108 for(
unsigned int isample=0;isample<m_nctraining[iclass];++isample){
109 for(
int ifeature=0;ifeature<nFeatures;++ifeature){
110 tmpFeatures[iclass][isample][ifeature]=trainingFeatures[iclass][isample][ifeature];
116 rmse=net.cross_validation(tmpFeatures,
124 for(
int isample=0;isample<referenceVector.size();++isample){
125 string refClassName=m_nameVector[referenceVector[isample]];
126 string className=m_nameVector[outputVector[isample]];
127 if(m_classValueMap.size())
128 m_cm.incrementResult(type2string<short>(m_classValueMap[refClassName]),type2string<short>(m_classValueMap[className]),1.0);
130 m_cm.incrementResult(m_cm.getClass(referenceVector[isample]),m_cm.getClass(outputVector[isample]),1.0);
135 bool initWeights=
true;
136 net.train_on_data(tmpFeatures,ntraining,initWeights, m_maxit,
137 iterations_between_reports, desired_error);
138 vector<Vector2d<float> > testFeatures(nclass);
139 vector<float> result(nclass);
141 for(
int iclass=0;iclass<nclass;++iclass){
142 testFeatures.resize(m_nctest[iclass],nFeatures);
143 for(
unsigned int isample=0;isample<m_nctraining[iclass];++isample){
144 for(
int ifeature=0;ifeature<nFeatures;++ifeature){
145 testFeatures[iclass][isample][ifeature]=trainingFeatures[iclass][m_nctraining[iclass]+isample][ifeature];
147 result=net.run(testFeatures[iclass][isample]);
148 string refClassName=m_nameVector[iclass];
150 for(
int ic=0;ic<nclass;++ic){
151 float pv=(result[ic]+1.0)/2.0;
157 string className=m_nameVector[maxClass];
158 if(m_classValueMap.size())
159 m_cm.incrementResult(type2string<short>(m_classValueMap[refClassName]),type2string<short>(m_classValueMap[className]),1.0);
161 m_cm.incrementResult(m_cm.getClass(referenceVector[isample]),m_cm.getClass(outputVector[isample]),1.0);
165 assert(m_cm.nReference());
166 return(m_cm.kappa());
169 int main(
int argc,
char *argv[])
174 Optionpk<string> input_opt(
"i",
"input",
"input test set (leave empty to perform a cross validation based on training only)");
175 Optionpk<string> training_opt(
"t",
"training",
"training vector file. A single vector file contains all training features (must be set as: B0, B1, B2,...) for all classes (class numbers identified by label option). Use multiple training files for bootstrap aggregation (alternative to the bag and bsize options, where a random subset is taken from a single training file)");
177 Optionpk<string> label_opt(
"label",
"label",
"identifier for class label in training vector file.",
"label");
178 Optionpk<unsigned short> maxFeatures_opt(
"n",
"nf",
"number of features to select (0 to select optimal number, see also ecost option)", 0);
179 Optionpk<unsigned int> balance_opt(
"\0",
"balance",
"balance the input data to this number of samples for each class", 0);
180 Optionpk<bool> random_opt(
"random",
"random",
"in case of balance, randomize input data",
true);
181 Optionpk<int> minSize_opt(
"min",
"min",
"if number of training pixels is less then min, do not take this class into account", 0);
182 Optionpk<short> band_opt(
"b",
"band",
"band index (starting from 0, either use band option or use start to end)");
184 Optionpk<double> bend_opt(
"e",
"end",
"end band sequence number (set to 0 to include all bands)", 0);
185 Optionpk<double> offset_opt(
"\0",
"offset",
"offset value for each spectral band input features: refl[band]=(DN[band]-offset[band])/scale[band]", 0.0);
186 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);
187 Optionpk<unsigned short> aggreg_opt(
"a",
"aggreg",
"how to combine aggregated classifiers, see also rc option (0: no aggregation, 1: sum rule, 2: max rule).",0);
189 Optionpk<string> selector_opt(
"sm",
"sm",
"feature selection method (sffs=sequential floating forward search,sfs=sequential forward search, sbs, sequential backward search ,bfs=brute force search)",
"sffs");
190 Optionpk<float> epsilon_cost_opt(
"ecost",
"ecost",
"epsilon for stopping criterion in cost function to determine optimal number of features",0.001);
193 Optionpk<short> classvalue_opt(
"r",
"reclass",
"list of class values (use same order as in classname opt.");
194 Optionpk<unsigned int> nneuron_opt(
"n",
"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);
195 Optionpk<float> connection_opt(
"\0",
"connection",
"connection reate (default: 1.0 for a fully connected network)", 1.0);
196 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);
197 Optionpk<float> learning_opt(
"l",
"learning",
"learning rate (default: 0.7)", 0.7);
198 Optionpk<unsigned int> maxit_opt(
"\0",
"maxit",
"number of maximum iterations (epoch) (default: 500)", 500);
199 Optionpk<short> verbose_opt(
"v",
"verbose",
"set to: 0 (results only), 1 (confusion matrix), 2 (debug)",0,2);
201 tlayer_opt.setHide(1);
202 label_opt.setHide(1);
203 balance_opt.setHide(1);
204 random_opt.setHide(1);
205 minSize_opt.setHide(1);
207 bstart_opt.setHide(1);
209 offset_opt.setHide(1);
210 scale_opt.setHide(1);
211 aggreg_opt.setHide(1);
213 selector_opt.setHide(1);
214 epsilon_cost_opt.setHide(1);
216 classname_opt.setHide(1);
217 classvalue_opt.setHide(1);
218 nneuron_opt.setHide(1);
219 connection_opt.setHide(1);
220 weights_opt.setHide(1);
221 learning_opt.setHide(1);
222 maxit_opt.setHide(1);
226 doProcess=input_opt.retrieveOption(argc,argv);
227 training_opt.retrieveOption(argc,argv);
228 maxFeatures_opt.retrieveOption(argc,argv);
229 tlayer_opt.retrieveOption(argc,argv);
230 label_opt.retrieveOption(argc,argv);
231 balance_opt.retrieveOption(argc,argv);
232 random_opt.retrieveOption(argc,argv);
233 minSize_opt.retrieveOption(argc,argv);
234 band_opt.retrieveOption(argc,argv);
235 bstart_opt.retrieveOption(argc,argv);
236 bend_opt.retrieveOption(argc,argv);
237 offset_opt.retrieveOption(argc,argv);
238 scale_opt.retrieveOption(argc,argv);
239 aggreg_opt.retrieveOption(argc,argv);
241 selector_opt.retrieveOption(argc,argv);
242 epsilon_cost_opt.retrieveOption(argc,argv);
243 cv_opt.retrieveOption(argc,argv);
244 classname_opt.retrieveOption(argc,argv);
245 classvalue_opt.retrieveOption(argc,argv);
246 nneuron_opt.retrieveOption(argc,argv);
247 connection_opt.retrieveOption(argc,argv);
248 weights_opt.retrieveOption(argc,argv);
249 learning_opt.retrieveOption(argc,argv);
250 maxit_opt.retrieveOption(argc,argv);
251 verbose_opt.retrieveOption(argc,argv);
253 catch(
string predefinedString){
254 std::cout << predefinedString << std::endl;
259 cout <<
"Usage: pkfsann -t training -n number" << endl;
261 std::cout <<
"short option -h shows basic options only, use long option --help to show all options" << std::endl;
265 CostFactoryANN costfactory(nneuron_opt, connection_opt[0], weights_opt, learning_opt[0], maxit_opt[0], cv_opt[0], verbose_opt[0]);
267 assert(training_opt.size());
269 costfactory.setCv(0);
270 if(verbose_opt[0]>=1){
272 std::cout <<
"input filename: " << input_opt[0] << std::endl;
273 std::cout <<
"training vector file: " << std::endl;
274 for(
int ifile=0;ifile<training_opt.size();++ifile)
275 std::cout << training_opt[ifile] << std::endl;
276 std::cout <<
"verbose: " << verbose_opt[0] << std::endl;
279 static std::map<std::string, SelectorValue> selMap;
286 assert(training_opt.size());
289 if(verbose_opt[0]>=1)
290 std::cout <<
"training vector file: " << training_opt[0] << std::endl;
292 unsigned int totalSamples=0;
293 unsigned int totalTestSamples=0;
295 unsigned short nclass=0;
313 std::sort(band_opt.begin(),band_opt.end());
316 if(classname_opt.size()){
317 assert(classname_opt.size()==classvalue_opt.size());
318 for(
int iclass=0;iclass<classname_opt.size();++iclass)
319 costfactory.setClassValueMap(classname_opt[iclass],classvalue_opt[iclass]);
322 vector<double> offset;
323 vector<double> scale;
324 vector< Vector2d<float> > trainingPixels;
325 vector< Vector2d<float> > testPixels;
326 map<string,Vector2d<float> > trainingMap;
327 map<string,Vector2d<float> > testMap;
328 vector<string> fields;
331 trainingPixels.clear();
332 if(verbose_opt[0]>=1)
333 std::cout <<
"reading imageVector file " << training_opt[0] << std::endl;
337 totalSamples=trainingReader.readDataImageOgr(trainingMap,fields,band_opt,label_opt[0],tlayer_opt,verbose_opt[0]);
338 if(input_opt.size()){
340 totalTestSamples=trainingReader.readDataImageOgr(testMap,fields,band_opt,label_opt[0],tlayer_opt,verbose_opt[0]);
345 totalSamples=trainingReader.readDataImageOgr(trainingMap,fields,bstart_opt[0],bend_opt[0],label_opt[0],tlayer_opt,verbose_opt[0]);
346 if(input_opt.size()){
348 totalTestSamples=trainingReader.readDataImageOgr(testMap,fields,bstart_opt[0],bend_opt[0],label_opt[0],tlayer_opt,verbose_opt[0]);
352 if(trainingMap.size()<2){
353 string errorstring=
"Error: could not read at least two classes from training file";
356 if(input_opt.size()&&testMap.size()<2){
357 string errorstring=
"Error: could not read at least two classes from test input file";
360 trainingReader.close();
363 cerr << error << std::endl;
366 catch(std::exception& e){
367 std::cerr <<
"Error: ";
368 std::cerr << e.what() << std::endl;
369 std::cerr << CPLGetLastErrorMsg() << std::endl;
373 cerr <<
"error catched" << std::endl;
384 std::cout <<
"training pixels: " << std::endl;
385 map<string,Vector2d<float> >::iterator mapit=trainingMap.begin();
386 while(mapit!=trainingMap.end()){
399 if((mapit->second).size()<minSize_opt[0]){
400 trainingMap.erase(mapit);
403 trainingPixels.push_back(mapit->second);
405 std::cout << mapit->first <<
": " << (mapit->second).size() <<
" samples" << std::endl;
408 nclass=trainingPixels.size();
409 if(classname_opt.size())
410 assert(nclass==classname_opt.size());
411 nband=trainingPixels[0][0].size()-2;
413 mapit=testMap.begin();
414 while(mapit!=testMap.end()){
416 testPixels.push_back(mapit->second);
418 std::cout << mapit->first <<
": " << (mapit->second).size() <<
" samples" << std::endl;
421 if(input_opt.size()){
422 assert(nclass==testPixels.size());
423 assert(nband=testPixels[0][0].size()-2);
429 if(balance_opt[0]>0){
433 for(
int iclass=0;iclass<nclass;++iclass){
434 if(trainingPixels[iclass].size()>balance_opt[0]){
435 while(trainingPixels[iclass].size()>balance_opt[0]){
436 int index=rand()%trainingPixels[iclass].size();
437 trainingPixels[iclass].erase(trainingPixels[iclass].begin()+index);
441 int oldsize=trainingPixels[iclass].size();
442 for(
int isample=trainingPixels[iclass].size();isample<balance_opt[0];++isample){
443 int index = rand()%oldsize;
444 trainingPixels[iclass].push_back(trainingPixels[iclass][index]);
447 totalSamples+=trainingPixels[iclass].size();
449 assert(totalSamples==nclass*balance_opt[0]);
453 offset.resize(nband);
455 if(offset_opt.size()>1)
456 assert(offset_opt.size()==nband);
457 if(scale_opt.size()>1)
458 assert(scale_opt.size()==nband);
459 for(
int iband=0;iband<nband;++iband){
461 std::cout <<
"scaling for band" << iband << std::endl;
462 offset[iband]=(offset_opt.size()==1)?offset_opt[0]:offset_opt[iband];
463 scale[iband]=(scale_opt.size()==1)?scale_opt[0]:scale_opt[iband];
466 float theMin=trainingPixels[0][0][iband+startBand];
467 float theMax=trainingPixels[0][0][iband+startBand];
468 for(
int iclass=0;iclass<nclass;++iclass){
469 for(
int isample=0;isample<trainingPixels[iclass].size();++isample){
470 if(theMin>trainingPixels[iclass][isample][iband+startBand])
471 theMin=trainingPixels[iclass][isample][iband+startBand];
472 if(theMax<trainingPixels[iclass][isample][iband+startBand])
473 theMax=trainingPixels[iclass][isample][iband+startBand];
476 offset[iband]=theMin+(theMax-theMin)/2.0;
477 scale[iband]=(theMax-theMin)/2.0;
478 if(verbose_opt[0]>1){
479 std::cout <<
"Extreme image values for band " << iband <<
": [" << theMin <<
"," << theMax <<
"]" << std::endl;
480 std::cout <<
"Using offset, scale: " << offset[iband] <<
", " << scale[iband] << std::endl;
481 std::cout <<
"scaled values for band " << iband <<
": [" << (theMin-offset[iband])/scale[iband] <<
"," << (theMax-offset[iband])/scale[iband] <<
"]" << std::endl;
493 if(verbose_opt[0]>=1){
494 std::cout <<
"number of bands: " << nband << std::endl;
495 std::cout <<
"number of classes: " << nclass << std::endl;
503 vector<string> nameVector=costfactory.getNameVector();
504 for(
int iname=0;iname<nameVector.size();++iname){
505 if(costfactory.getClassValueMap().empty())
506 costfactory.pushBackClassName(nameVector[iname]);
508 else if(costfactory.getClassIndex(type2string<short>((costfactory.getClassValueMap())[nameVector[iname]]))<0)
509 costfactory.pushBackClassName(type2string<short>((costfactory.getClassValueMap())[nameVector[iname]]));
522 vector<unsigned int> nctraining;
523 vector<unsigned int> nctest;
524 nctraining.resize(nclass);
525 nctest.resize(nclass);
526 vector< Vector2d<float> > trainingFeatures(nclass);
527 for(
int iclass=0;iclass<nclass;++iclass){
528 if(verbose_opt[0]>=1)
529 std::cout <<
"calculating features for class " << iclass << std::endl;
530 nctraining[iclass]=trainingPixels[iclass].size();
531 if(verbose_opt[0]>=1)
532 std::cout <<
"nctraining[" << iclass <<
"]: " << nctraining[iclass] << std::endl;
533 if(testPixels.size()>iclass){
534 nctest[iclass]=testPixels[iclass].size();
535 if(verbose_opt[0]>=1){
536 std::cout <<
"nctest[" << iclass <<
"]: " << nctest[iclass] << std::endl;
542 trainingFeatures[iclass].resize(nctraining[iclass]+nctest[iclass]);
543 for(
int isample=0;isample<nctraining[iclass];++isample){
545 for(
int iband=0;iband<nband;++iband){
546 assert(trainingPixels[iclass].size()>isample);
547 assert(trainingPixels[iclass][isample].size()>iband+startBand);
548 assert(offset.size()>iband);
549 assert(scale.size()>iband);
550 float value=trainingPixels[iclass][isample][iband+startBand];
551 trainingFeatures[iclass][isample].push_back((value-offset[iband])/scale[iband]);
554 for(
int isample=0;isample<nctest[iclass];++isample){
556 for(
int iband=0;iband<nband;++iband){
557 assert(testPixels[iclass].size()>isample);
558 assert(testPixels[iclass][isample].size()>iband+startBand);
559 assert(offset.size()>iband);
560 assert(scale.size()>iband);
561 float value=testPixels[iclass][isample][iband+startBand];
563 trainingFeatures[iclass][nctraining[iclass]+isample].push_back((value-offset[iband])/scale[iband]);
566 assert(trainingFeatures[iclass].size()==nctraining[iclass]+nctest[iclass]);
569 costfactory.setNcTraining(nctraining);
570 costfactory.setNcTest(nctest);
571 int nFeatures=trainingFeatures[0][0].size();
572 int maxFeatures=(maxFeatures_opt[0])? maxFeatures_opt[0] : 1;
573 double previousCost=-1;
578 if(maxFeatures>=nFeatures){
580 for(
int ifeature=0;ifeature<nFeatures;++ifeature)
581 subset.push_back(ifeature);
582 cost=costfactory.getCost(trainingFeatures);
585 while(fabs(cost-previousCost)>=epsilon_cost_opt[0]){
587 switch(selMap[selector_opt[0]]){
590 cost=selector.floating(trainingFeatures,costfactory,subset,maxFeatures,epsilon_cost_opt[0],verbose_opt[0]);
593 cost=selector.forward(trainingFeatures,costfactory,subset,maxFeatures,verbose_opt[0]);
596 cost=selector.backward(trainingFeatures,costfactory,subset,maxFeatures,verbose_opt[0]);
600 cost=selector.bruteForce(trainingFeatures,costfactory,subset,maxFeatures,verbose_opt[0]);
603 std::cout <<
"Error: selector not supported, please use sffs, sfs, sbs or bfs" << std::endl;
607 if(verbose_opt[0]>1){
608 std::cout <<
"cost: " << cost << std::endl;
609 std::cout <<
"previousCost: " << previousCost << std::endl;
610 std::cout << std::setprecision(12) <<
"cost-previousCost: " << cost - previousCost <<
" ( " << epsilon_cost_opt[0] <<
")" << std::endl;
612 if(!maxFeatures_opt[0])
620 std::cout <<
"catched feature selection" << std::endl;
625 cout <<
"cost: " << cost << endl;
627 for(list<int>::const_iterator lit=subset.begin();lit!=subset.end();++lit)
628 std::cout <<
" -b " << *lit;
629 std::cout << std::endl;