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"
96 #define Malloc(type,n) (type *)malloc((n)*sizeof(type))
98 CostFactoryANN::CostFactoryANN(
const vector<unsigned int>& nneuron,
float connection,
const std::vector<float> weights,
float learning,
unsigned int maxit,
unsigned short cv,
bool verbose)
99 :
CostFactory(cv,verbose), m_nneuron(nneuron), m_connection(connection), m_weights(weights), m_learning(learning), m_maxit(maxit){};
101 CostFactoryANN::~CostFactoryANN(){
104 double CostFactoryANN::getCost(
const vector<
Vector2d<float> > &trainingFeatures)
106 unsigned short nclass=trainingFeatures.size();
107 unsigned int ntraining=0;
108 unsigned int ntest=0;
109 for(
int iclass=0;iclass<nclass;++iclass){
110 ntraining+=m_nctraining[iclass];
111 ntest+=m_nctest[iclass];
117 unsigned short nFeatures=trainingFeatures[0][0].size();
120 const unsigned int num_layers = m_nneuron.size()+2;
121 const float desired_error = 0.0003;
122 const unsigned int iterations_between_reports = (m_verbose) ? m_maxit+1:0;
124 cout <<
"creating artificial neural network with " << m_nneuron.size() <<
" hidden layer, having " << endl;
125 for(
int ilayer=0;ilayer<m_nneuron.size();++ilayer)
126 cout << m_nneuron[ilayer] <<
" ";
127 cout <<
"neurons" << endl;
131 unsigned int layers[3];
133 layers[1]=m_nneuron[0];
135 net.create_sparse_array(m_connection,num_layers,layers);
139 unsigned int layers[4];
141 layers[1]=m_nneuron[0];
142 layers[2]=m_nneuron[1];
144 net.create_sparse_array(m_connection,num_layers,layers);
148 cerr <<
"Only 1 or 2 hidden layers are supported!" << endl;
153 net.set_learning_rate(m_learning);
155 net.set_activation_function_hidden(FANN::SIGMOID_SYMMETRIC_STEPWISE);
156 net.set_activation_function_output(FANN::SIGMOID_SYMMETRIC_STEPWISE);
158 vector<unsigned short> referenceVector;
159 vector<unsigned short> outputVector;
161 vector<Vector2d<float> > tmpFeatures(nclass);
162 for(
int iclass=0;iclass<nclass;++iclass){
163 tmpFeatures[iclass].resize(trainingFeatures[iclass].size(),nFeatures);
164 for(
unsigned int isample=0;isample<m_nctraining[iclass];++isample){
165 for(
int ifeature=0;ifeature<nFeatures;++ifeature){
166 tmpFeatures[iclass][isample][ifeature]=trainingFeatures[iclass][isample][ifeature];
172 rmse=net.cross_validation(tmpFeatures,
180 for(
int isample=0;isample<referenceVector.size();++isample){
181 string refClassName=m_nameVector[referenceVector[isample]];
182 string className=m_nameVector[outputVector[isample]];
183 if(m_classValueMap.size())
184 m_cm.incrementResult(type2string<short>(m_classValueMap[refClassName]),type2string<short>(m_classValueMap[className]),1.0);
186 m_cm.incrementResult(m_cm.getClass(referenceVector[isample]),m_cm.getClass(outputVector[isample]),1.0);
191 bool initWeights=
true;
192 net.train_on_data(tmpFeatures,ntraining,initWeights, m_maxit,
193 iterations_between_reports, desired_error);
194 vector<Vector2d<float> > testFeatures(nclass);
195 vector<float> result(nclass);
197 for(
int iclass=0;iclass<nclass;++iclass){
198 testFeatures.resize(m_nctest[iclass],nFeatures);
199 for(
unsigned int isample=0;isample<m_nctraining[iclass];++isample){
200 for(
int ifeature=0;ifeature<nFeatures;++ifeature){
201 testFeatures[iclass][isample][ifeature]=trainingFeatures[iclass][m_nctraining[iclass]+isample][ifeature];
203 result=net.run(testFeatures[iclass][isample]);
204 string refClassName=m_nameVector[iclass];
206 for(
int ic=0;ic<nclass;++ic){
207 float pv=(result[ic]+1.0)/2.0;
213 string className=m_nameVector[maxClass];
214 if(m_classValueMap.size())
215 m_cm.incrementResult(type2string<short>(m_classValueMap[refClassName]),type2string<short>(m_classValueMap[className]),1.0);
217 m_cm.incrementResult(m_cm.getClass(referenceVector[isample]),m_cm.getClass(outputVector[isample]),1.0);
221 assert(m_cm.nReference());
222 return(m_cm.kappa());
225 int main(
int argc,
char *argv[])
230 Optionpk<string> input_opt(
"i",
"input",
"input test set (leave empty to perform a cross validation based on training only)");
231 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)");
233 Optionpk<string> label_opt(
"label",
"label",
"identifier for class label in training vector file.",
"label");
234 Optionpk<unsigned short> maxFeatures_opt(
"n",
"nf",
"number of features to select (0 to select optimal number, see also ecost option)", 0);
235 Optionpk<unsigned int> balance_opt(
"\0",
"balance",
"balance the input data to this number of samples for each class", 0);
236 Optionpk<bool> random_opt(
"random",
"random",
"in case of balance, randomize input data",
true);
237 Optionpk<int> minSize_opt(
"min",
"min",
"if number of training pixels is less then min, do not take this class into account", 0);
238 Optionpk<short> band_opt(
"b",
"band",
"band index (starting from 0, either use band option or use start to end)");
240 Optionpk<double> bend_opt(
"e",
"end",
"end band sequence number (set to 0 to include all bands)", 0);
241 Optionpk<double> offset_opt(
"\0",
"offset",
"offset value for each spectral band input features: refl[band]=(DN[band]-offset[band])/scale[band]", 0.0);
242 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);
243 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);
245 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");
246 Optionpk<float> epsilon_cost_opt(
"ecost",
"ecost",
"epsilon for stopping criterion in cost function to determine optimal number of features",0.001);
249 Optionpk<short> classvalue_opt(
"r",
"reclass",
"list of class values (use same order as in classname opt.");
250 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);
251 Optionpk<float> connection_opt(
"\0",
"connection",
"connection reate (default: 1.0 for a fully connected network)", 1.0);
252 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);
253 Optionpk<float> learning_opt(
"l",
"learning",
"learning rate (default: 0.7)", 0.7);
254 Optionpk<unsigned int> maxit_opt(
"\0",
"maxit",
"number of maximum iterations (epoch) (default: 500)", 500);
255 Optionpk<short> verbose_opt(
"v",
"verbose",
"set to: 0 (results only), 1 (confusion matrix), 2 (debug)",0,2);
257 tlayer_opt.setHide(1);
258 label_opt.setHide(1);
259 balance_opt.setHide(1);
260 random_opt.setHide(1);
261 minSize_opt.setHide(1);
263 bstart_opt.setHide(1);
265 offset_opt.setHide(1);
266 scale_opt.setHide(1);
267 aggreg_opt.setHide(1);
269 selector_opt.setHide(1);
270 epsilon_cost_opt.setHide(1);
272 classname_opt.setHide(1);
273 classvalue_opt.setHide(1);
274 nneuron_opt.setHide(1);
275 connection_opt.setHide(1);
276 weights_opt.setHide(1);
277 learning_opt.setHide(1);
278 maxit_opt.setHide(1);
282 doProcess=input_opt.retrieveOption(argc,argv);
283 training_opt.retrieveOption(argc,argv);
284 maxFeatures_opt.retrieveOption(argc,argv);
285 tlayer_opt.retrieveOption(argc,argv);
286 label_opt.retrieveOption(argc,argv);
287 balance_opt.retrieveOption(argc,argv);
288 random_opt.retrieveOption(argc,argv);
289 minSize_opt.retrieveOption(argc,argv);
290 band_opt.retrieveOption(argc,argv);
291 bstart_opt.retrieveOption(argc,argv);
292 bend_opt.retrieveOption(argc,argv);
293 offset_opt.retrieveOption(argc,argv);
294 scale_opt.retrieveOption(argc,argv);
295 aggreg_opt.retrieveOption(argc,argv);
297 selector_opt.retrieveOption(argc,argv);
298 epsilon_cost_opt.retrieveOption(argc,argv);
299 cv_opt.retrieveOption(argc,argv);
300 classname_opt.retrieveOption(argc,argv);
301 classvalue_opt.retrieveOption(argc,argv);
302 nneuron_opt.retrieveOption(argc,argv);
303 connection_opt.retrieveOption(argc,argv);
304 weights_opt.retrieveOption(argc,argv);
305 learning_opt.retrieveOption(argc,argv);
306 maxit_opt.retrieveOption(argc,argv);
307 verbose_opt.retrieveOption(argc,argv);
309 catch(
string predefinedString){
310 std::cout << predefinedString << std::endl;
315 cout <<
"Usage: pkfsann -t training -n number" << endl;
317 std::cout <<
"short option -h shows basic options only, use long option --help to show all options" << std::endl;
321 CostFactoryANN costfactory(nneuron_opt, connection_opt[0], weights_opt, learning_opt[0], maxit_opt[0], cv_opt[0], verbose_opt[0]);
323 assert(training_opt.size());
325 costfactory.setCv(0);
326 if(verbose_opt[0]>=1){
328 std::cout <<
"input filename: " << input_opt[0] << std::endl;
329 std::cout <<
"training vector file: " << std::endl;
330 for(
int ifile=0;ifile<training_opt.size();++ifile)
331 std::cout << training_opt[ifile] << std::endl;
332 std::cout <<
"verbose: " << verbose_opt[0] << std::endl;
335 static std::map<std::string, SelectorValue> selMap;
342 assert(training_opt.size());
345 if(verbose_opt[0]>=1)
346 std::cout <<
"training vector file: " << training_opt[0] << std::endl;
348 unsigned int totalSamples=0;
349 unsigned int totalTestSamples=0;
351 unsigned short nclass=0;
369 std::sort(band_opt.begin(),band_opt.end());
372 if(classname_opt.size()){
373 assert(classname_opt.size()==classvalue_opt.size());
374 for(
int iclass=0;iclass<classname_opt.size();++iclass)
375 costfactory.setClassValueMap(classname_opt[iclass],classvalue_opt[iclass]);
378 vector<double> offset;
379 vector<double> scale;
380 vector< Vector2d<float> > trainingPixels;
381 vector< Vector2d<float> > testPixels;
382 map<string,Vector2d<float> > trainingMap;
383 map<string,Vector2d<float> > testMap;
384 vector<string> fields;
387 trainingPixels.clear();
388 if(verbose_opt[0]>=1)
389 std::cout <<
"reading imageVector file " << training_opt[0] << std::endl;
393 totalSamples=trainingReader.readDataImageOgr(trainingMap,fields,band_opt,label_opt[0],tlayer_opt,verbose_opt[0]);
394 if(input_opt.size()){
396 totalTestSamples=trainingReader.readDataImageOgr(testMap,fields,band_opt,label_opt[0],tlayer_opt,verbose_opt[0]);
401 totalSamples=trainingReader.readDataImageOgr(trainingMap,fields,bstart_opt[0],bend_opt[0],label_opt[0],tlayer_opt,verbose_opt[0]);
402 if(input_opt.size()){
404 totalTestSamples=trainingReader.readDataImageOgr(testMap,fields,bstart_opt[0],bend_opt[0],label_opt[0],tlayer_opt,verbose_opt[0]);
408 if(trainingMap.size()<2){
409 string errorstring=
"Error: could not read at least two classes from training file";
412 if(input_opt.size()&&testMap.size()<2){
413 string errorstring=
"Error: could not read at least two classes from test input file";
416 trainingReader.close();
419 cerr << error << std::endl;
422 catch(std::exception& e){
423 std::cerr <<
"Error: ";
424 std::cerr << e.what() << std::endl;
425 std::cerr << CPLGetLastErrorMsg() << std::endl;
429 cerr <<
"error catched" << std::endl;
440 std::cout <<
"training pixels: " << std::endl;
441 map<string,Vector2d<float> >::iterator mapit=trainingMap.begin();
442 while(mapit!=trainingMap.end()){
455 if((mapit->second).size()<minSize_opt[0]){
456 trainingMap.erase(mapit);
459 trainingPixels.push_back(mapit->second);
461 std::cout << mapit->first <<
": " << (mapit->second).size() <<
" samples" << std::endl;
464 nclass=trainingPixels.size();
465 if(classname_opt.size())
466 assert(nclass==classname_opt.size());
467 nband=trainingPixels[0][0].size()-2;
469 mapit=testMap.begin();
470 while(mapit!=testMap.end()){
472 testPixels.push_back(mapit->second);
474 std::cout << mapit->first <<
": " << (mapit->second).size() <<
" samples" << std::endl;
477 if(input_opt.size()){
478 assert(nclass==testPixels.size());
479 assert(nband=testPixels[0][0].size()-2);
485 if(balance_opt[0]>0){
489 for(
int iclass=0;iclass<nclass;++iclass){
490 if(trainingPixels[iclass].size()>balance_opt[0]){
491 while(trainingPixels[iclass].size()>balance_opt[0]){
492 int index=rand()%trainingPixels[iclass].size();
493 trainingPixels[iclass].erase(trainingPixels[iclass].begin()+index);
497 int oldsize=trainingPixels[iclass].size();
498 for(
int isample=trainingPixels[iclass].size();isample<balance_opt[0];++isample){
499 int index = rand()%oldsize;
500 trainingPixels[iclass].push_back(trainingPixels[iclass][index]);
503 totalSamples+=trainingPixels[iclass].size();
505 assert(totalSamples==nclass*balance_opt[0]);
509 offset.resize(nband);
511 if(offset_opt.size()>1)
512 assert(offset_opt.size()==nband);
513 if(scale_opt.size()>1)
514 assert(scale_opt.size()==nband);
515 for(
int iband=0;iband<nband;++iband){
517 std::cout <<
"scaling for band" << iband << std::endl;
518 offset[iband]=(offset_opt.size()==1)?offset_opt[0]:offset_opt[iband];
519 scale[iband]=(scale_opt.size()==1)?scale_opt[0]:scale_opt[iband];
522 float theMin=trainingPixels[0][0][iband+startBand];
523 float theMax=trainingPixels[0][0][iband+startBand];
524 for(
int iclass=0;iclass<nclass;++iclass){
525 for(
int isample=0;isample<trainingPixels[iclass].size();++isample){
526 if(theMin>trainingPixels[iclass][isample][iband+startBand])
527 theMin=trainingPixels[iclass][isample][iband+startBand];
528 if(theMax<trainingPixels[iclass][isample][iband+startBand])
529 theMax=trainingPixels[iclass][isample][iband+startBand];
532 offset[iband]=theMin+(theMax-theMin)/2.0;
533 scale[iband]=(theMax-theMin)/2.0;
534 if(verbose_opt[0]>1){
535 std::cout <<
"Extreme image values for band " << iband <<
": [" << theMin <<
"," << theMax <<
"]" << std::endl;
536 std::cout <<
"Using offset, scale: " << offset[iband] <<
", " << scale[iband] << std::endl;
537 std::cout <<
"scaled values for band " << iband <<
": [" << (theMin-offset[iband])/scale[iband] <<
"," << (theMax-offset[iband])/scale[iband] <<
"]" << std::endl;
549 if(verbose_opt[0]>=1){
550 std::cout <<
"number of bands: " << nband << std::endl;
551 std::cout <<
"number of classes: " << nclass << std::endl;
559 vector<string> nameVector=costfactory.getNameVector();
560 for(
int iname=0;iname<nameVector.size();++iname){
561 if(costfactory.getClassValueMap().empty())
562 costfactory.pushBackClassName(nameVector[iname]);
564 else if(costfactory.getClassIndex(type2string<short>((costfactory.getClassValueMap())[nameVector[iname]]))<0)
565 costfactory.pushBackClassName(type2string<short>((costfactory.getClassValueMap())[nameVector[iname]]));
578 vector<unsigned int> nctraining;
579 vector<unsigned int> nctest;
580 nctraining.resize(nclass);
581 nctest.resize(nclass);
582 vector< Vector2d<float> > trainingFeatures(nclass);
583 for(
int iclass=0;iclass<nclass;++iclass){
584 if(verbose_opt[0]>=1)
585 std::cout <<
"calculating features for class " << iclass << std::endl;
586 nctraining[iclass]=trainingPixels[iclass].size();
587 if(verbose_opt[0]>=1)
588 std::cout <<
"nctraining[" << iclass <<
"]: " << nctraining[iclass] << std::endl;
589 if(testPixels.size()>iclass){
590 nctest[iclass]=testPixels[iclass].size();
591 if(verbose_opt[0]>=1){
592 std::cout <<
"nctest[" << iclass <<
"]: " << nctest[iclass] << std::endl;
598 trainingFeatures[iclass].resize(nctraining[iclass]+nctest[iclass]);
599 for(
int isample=0;isample<nctraining[iclass];++isample){
601 for(
int iband=0;iband<nband;++iband){
602 assert(trainingPixels[iclass].size()>isample);
603 assert(trainingPixels[iclass][isample].size()>iband+startBand);
604 assert(offset.size()>iband);
605 assert(scale.size()>iband);
606 float value=trainingPixels[iclass][isample][iband+startBand];
607 trainingFeatures[iclass][isample].push_back((value-offset[iband])/scale[iband]);
610 for(
int isample=0;isample<nctest[iclass];++isample){
612 for(
int iband=0;iband<nband;++iband){
613 assert(testPixels[iclass].size()>isample);
614 assert(testPixels[iclass][isample].size()>iband+startBand);
615 assert(offset.size()>iband);
616 assert(scale.size()>iband);
617 float value=testPixels[iclass][isample][iband+startBand];
619 trainingFeatures[iclass][nctraining[iclass]+isample].push_back((value-offset[iband])/scale[iband]);
622 assert(trainingFeatures[iclass].size()==nctraining[iclass]+nctest[iclass]);
625 costfactory.setNcTraining(nctraining);
626 costfactory.setNcTest(nctest);
627 int nFeatures=trainingFeatures[0][0].size();
628 int maxFeatures=(maxFeatures_opt[0])? maxFeatures_opt[0] : 1;
629 double previousCost=-1;
634 if(maxFeatures>=nFeatures){
636 for(
int ifeature=0;ifeature<nFeatures;++ifeature)
637 subset.push_back(ifeature);
638 cost=costfactory.getCost(trainingFeatures);
641 while(fabs(cost-previousCost)>=epsilon_cost_opt[0]){
643 switch(selMap[selector_opt[0]]){
646 cost=selector.floating(trainingFeatures,costfactory,subset,maxFeatures,epsilon_cost_opt[0],verbose_opt[0]);
649 cost=selector.forward(trainingFeatures,costfactory,subset,maxFeatures,verbose_opt[0]);
652 cost=selector.backward(trainingFeatures,costfactory,subset,maxFeatures,verbose_opt[0]);
656 cost=selector.bruteForce(trainingFeatures,costfactory,subset,maxFeatures,verbose_opt[0]);
659 std::cout <<
"Error: selector not supported, please use sffs, sfs, sbs or bfs" << std::endl;
663 if(verbose_opt[0]>1){
664 std::cout <<
"cost: " << cost << std::endl;
665 std::cout <<
"previousCost: " << previousCost << std::endl;
666 std::cout << std::setprecision(12) <<
"cost-previousCost: " << cost - previousCost <<
" ( " << epsilon_cost_opt[0] <<
")" << std::endl;
668 if(!maxFeatures_opt[0])
676 std::cout <<
"catched feature selection" << std::endl;
681 cout <<
"cost: " << cost << endl;
683 for(list<int>::const_iterator lit=subset.begin();lit!=subset.end();++lit)
684 std::cout <<
" -b " << *lit;
685 std::cout << std::endl;