source: trunk/CrossPare/src/de/ugoe/cs/cpdp/training/GPTraining.java @ 104

Last change on this file since 104 was 104, checked in by atrautsch, 8 years ago

Implementation multiple runs

File size: 24.1 KB
Line 
1package de.ugoe.cs.cpdp.training;
2
3import java.util.LinkedList;
4import java.util.List;
5
6import org.apache.commons.collections4.list.SetUniqueList;
7
8import weka.classifiers.AbstractClassifier;
9import weka.classifiers.Classifier;
10import weka.core.Instance;
11import weka.core.Instances;
12import org.apache.commons.lang3.ArrayUtils;
13import org.jgap.Configuration;
14import org.jgap.InvalidConfigurationException;
15import org.jgap.gp.CommandGene;
16import org.jgap.gp.GPProblem;
17
18import org.jgap.gp.function.Add;
19import org.jgap.gp.function.Multiply;
20import org.jgap.gp.function.Log;
21import org.jgap.gp.function.Subtract;
22import org.jgap.gp.function.Divide;
23import org.jgap.gp.function.Sine;
24import org.jgap.gp.function.Cosine;
25import org.jgap.gp.function.Max;
26import org.jgap.gp.function.Exp;
27
28import org.jgap.gp.impl.DeltaGPFitnessEvaluator;
29import org.jgap.gp.impl.GPConfiguration;
30import org.jgap.gp.impl.GPGenotype;
31import org.jgap.gp.impl.TournamentSelector;
32import org.jgap.gp.terminal.Terminal;
33import org.jgap.gp.GPFitnessFunction;
34import org.jgap.gp.IGPProgram;
35import org.jgap.gp.terminal.Variable;
36import org.jgap.gp.MathCommand;
37import org.jgap.util.ICloneable;
38
39import de.ugoe.cs.cpdp.util.WekaUtils;
40
41import org.jgap.gp.impl.ProgramChromosome;
42import org.jgap.util.CloneException;
43
44/**
45 * Genetic Programming Trainer
46 *
47 */
48public class GPTraining implements ISetWiseTrainingStrategy, IWekaCompatibleTrainer  {
49   
50    private GPVVClassifier classifier = new GPVVClassifier();
51   
52    private int populationSize = 1000;
53    private int initMinDepth = 2;
54    private int initMaxDepth = 6;
55    private int tournamentSize = 7;
56   
57    @Override
58    public void setParameter(String parameters) {
59        // todo, which type of classifier? GPV, GPVV?
60        // more config population size, etc.
61        // todo: voting for gpvv only 3 votes necessary?
62    }
63
64    @Override
65    public void apply(SetUniqueList<Instances> traindataSet) {
66        try {
67            classifier.buildClassifier(traindataSet);
68        }catch(Exception e) {
69            throw new RuntimeException(e);
70        }
71    }
72
73    @Override
74    public String getName() {
75        return "GPTraining";
76    }
77
78    @Override
79    public Classifier getClassifier() {
80        return this.classifier;
81    }
82   
83    public class InstanceData {
84        private double[][] instances_x;
85        private boolean[] instances_y;
86       
87        public InstanceData(Instances instances) {
88            this.instances_x = new double[instances.numInstances()][instances.numAttributes()-1];
89            this.instances_y = new boolean[instances.numInstances()];
90           
91            Instance current;
92            for(int i=0; i < this.instances_x.length; i++) {
93                current = instances.get(i);
94                this.instances_x[i] = WekaUtils.instanceValues(current);
95                this.instances_y[i] = 1.0 == current.classValue();
96            }
97        }
98       
99        public double[][] getX() {
100            return instances_x;
101        }
102        public boolean[] getY() {
103            return instances_y;
104        }
105    }
106   
107    // one gprun, we want several for voting
108    public class GPRun extends AbstractClassifier {
109        private static final long serialVersionUID = -4250422550107888789L;
110
111        private int populationSize = 1000;
112        private int initMinDepth = 2;
113        private int initMaxDepth = 6;
114        private int tournamentSize = 7;
115        private int maxGenerations = 50;
116       
117        private GPGenotype gp;
118        private GPProblem problem;
119       
120        public void configure(int populationSize, int initMinDepth, int initMaxDepth, int tournamentSize, int maxGenerations) {
121            this.populationSize = populationSize;
122            this.initMinDepth = initMinDepth;
123            this.initMaxDepth = initMaxDepth;
124            this.tournamentSize = tournamentSize;
125            this.maxGenerations = maxGenerations;
126        }
127       
128        public GPGenotype getGp() {
129            return this.gp;
130        }
131       
132        public Variable[] getVariables() {
133            return ((CrossPareGP)this.problem).getVariables();
134        }
135       
136        public void setEvaldata(Instances testdata) {
137           
138        }
139       
140        /**
141         * GPProblem implementation
142         */
143        class CrossPareGP extends GPProblem {
144           
145            //private static final long serialVersionUID = 7526472295622776147L;
146
147            private double[][] instances;
148            private boolean[] output;
149
150            private Variable[] x;
151
152            public CrossPareGP(double[][] instances, boolean[] output, int populationSize, int minInitDept, int maxInitDepth, int tournamentSize) throws InvalidConfigurationException {
153                super(new GPConfiguration());
154               
155                this.instances = instances;
156                this.output = output;
157
158                Configuration.reset();
159                GPConfiguration config = this.getGPConfiguration();
160                //config.reset();
161               
162                this.x = new Variable[this.instances[0].length];
163
164               
165                for(int j=0; j < this.x.length; j++) {
166                    this.x[j] = Variable.create(config, "X"+j, CommandGene.DoubleClass);   
167                }
168
169                config.setGPFitnessEvaluator(new DeltaGPFitnessEvaluator()); // smaller fitness is better
170                //config.setGPFitnessEvaluator(new DefaultGPFitnessEvaluator()); // bigger fitness is better
171
172                // from paper: 2-6
173                config.setMinInitDepth(minInitDept);
174                config.setMaxInitDepth(maxInitDepth);
175
176                // missing from paper
177                // config.setMaxDepth(20);
178
179                config.setCrossoverProb((float)0.60);
180                config.setReproductionProb((float)0.10);
181                config.setMutationProb((float)0.30);
182
183                config.setSelectionMethod(new TournamentSelector(tournamentSize));
184
185                // from paper 1000
186                config.setPopulationSize(populationSize);
187
188                // BranchTypingCross
189                config.setMaxCrossoverDepth(4);
190                config.setFitnessFunction(new CrossPareFitness(this.x, this.instances, this.output));
191                config.setStrictProgramCreation(true);
192            }
193
194            // used for running the fitness function again for testing
195            public Variable[] getVariables() {
196                return this.x;
197            }
198
199
200            public GPGenotype create() throws InvalidConfigurationException {
201                GPConfiguration config = this.getGPConfiguration();
202
203                // return type
204                Class[] types = {CommandGene.DoubleClass};
205
206                // Arguments of result-producing chromosome: none
207                Class[][] argTypes = { {} };
208
209                // variables + functions, we set the variables with the values of the instances here
210                CommandGene[] vars = new CommandGene[this.instances[0].length];
211                for(int j=0; j < this.instances[0].length; j++) {
212                    vars[j] = this.x[j];
213                }
214                CommandGene[] funcs = {
215                    new Add(config, CommandGene.DoubleClass),
216                    new Subtract(config, CommandGene.DoubleClass),
217                    new Multiply(config, CommandGene.DoubleClass),
218                    new Divide(config, CommandGene.DoubleClass),
219                    new Sine(config, CommandGene.DoubleClass),
220                    new Cosine(config, CommandGene.DoubleClass),
221                    new Exp(config, CommandGene.DoubleClass),
222                    new Log(config, CommandGene.DoubleClass),
223                    new GT(config, CommandGene.DoubleClass),
224                    new Max(config, CommandGene.DoubleClass),
225                    new Terminal(config, CommandGene.DoubleClass, -100.0, 100.0, true), // min, max, whole numbers
226                };
227
228                CommandGene[] comb = (CommandGene[])ArrayUtils.addAll(vars, funcs);
229                CommandGene[][] nodeSets = {
230                    comb,
231                };
232
233                GPGenotype result = GPGenotype.randomInitialGenotype(config, types, argTypes, nodeSets, 20, true); // 20 = maxNodes, true = verbose output
234
235                return result;
236            }
237        }
238
239       
240        /**
241         * Fitness function
242         */
243        class CrossPareFitness extends GPFitnessFunction {
244           
245            private static final long serialVersionUID = 75234832484387L;
246
247            private Variable[] x;
248
249            private double[][] instances;
250            private boolean[] output;
251
252            private double error_type2_weight = 1.0;
253
254            // needed in evaluate
255            private Object[] NO_ARGS = new Object[0];
256
257            private double sfitness = 0.0f;
258            private int error_type1 = 0;
259            private int error_type2 = 0;
260
261            public CrossPareFitness(Variable[] x, double[][] instances, boolean[] output) {
262                this.x = x;
263                this.instances = instances;
264                this.output = output;
265            }
266
267            public int getErrorType1() {
268                return this.error_type1;
269            }
270
271            public int getErrorType2() {
272                return this.error_type2;
273            }
274
275            public double getSecondFitness() {
276                return this.sfitness;
277            }
278
279            public int getNumInstances() {
280                return this.instances.length;
281            }
282
283            @Override
284            protected double evaluate(final IGPProgram program) {
285                double pfitness = 0.0f;
286                this.sfitness = 0.0f;
287                double value = 0.0f;
288
289                // count classification errors
290                this.error_type1 = 0;
291                this.error_type2 = 0;
292
293                for(int i=0; i < this.instances.length; i++) {
294
295                    // requires that we have a variable for each column of our dataset (attribute of instance)
296                    for(int j=0; j < this.x.length; j++) {
297                        this.x[j].set(this.instances[i][j]);
298                    }
299
300                    // value gives us a double, if < 0.5 we set this instance as faulty
301                    value = program.execute_double(0, NO_ARGS);  // todo: test with this.x
302
303                    if(value < 0.5) {
304                        if(this.output[i] != true) {
305                            this.error_type1 += 1;
306                        }
307                    }else {
308                        if(this.output[i] == true) {
309                            this.error_type2 += 1;
310                        }
311                    }
312                }
313
314                // now calc pfitness
315                pfitness = (this.error_type1 + this.error_type2_weight * this.error_type2) / this.instances.length;
316
317                //System.out.println("pfitness: " + pfitness);
318
319                // number of nodes in the programm, if lower then 10 we assign sFitness of 10
320                // we can set metadata with setProgramData to save this
321                if(program.getChromosome(0).getSize(0) < 10) {
322                    program.setApplicationData(10.0f);
323                    this.sfitness = 10.0f;
324                    //System.out.println("wenige nodes: "+program.getChromosome(0).getSize(0));
325                    //System.out.println(program.toStringNorm(0));
326                }
327
328                // sfitness counts the number of nodes in the tree, if it is lower than 10 fitness is increased by 10
329
330                return pfitness;
331            }
332        }
333
334        @Override
335        public void buildClassifier(Instances traindata) throws Exception {
336            InstanceData train = new InstanceData(traindata);           
337            this.problem = new CrossPareGP(train.getX(), train.getY(), this.populationSize, this.initMinDepth, this.initMaxDepth, this.tournamentSize);
338            this.gp = problem.create();
339            this.gp.evolve(this.maxGenerations);
340        }
341    }
342   
343    /**
344     * GP Multiple Data Sets Validation-Voting Classifier
345     *
346     * As the GP Multiple Data Sets Validation Classifier
347     * But here we do keep a model candidate for each training set which may later vote
348     *
349     */
350    public class GPVVClassifier extends GPVClassifier {
351       
352        private List<Classifier> classifiers = null;
353       
354        @Override
355        public void buildClassifier(Instances arg0) throws Exception {
356            // TODO Auto-generated method stub
357           
358        }
359       
360        public void buildClassifier(SetUniqueList<Instances> traindataSet) throws Exception {
361
362            // each classifier is trained with one project from the set
363            // then is evaluated on the rest
364            for(int i=0; i < traindataSet.size(); i++) {
365               
366                // candidates we get out of evaluation
367                LinkedList<Classifier> candidates = new LinkedList<>();
368               
369                // 200 runs
370               
371                for(int k=0; k < 200; k++) {
372                    Classifier classifier = new GPRun();
373                   
374                    // one project is training data
375                    classifier.buildClassifier(traindataSet.get(i));
376                   
377                    double[] errors;
378                    // rest of the set is evaluation data, we evaluate now
379                    for(int j=0; j < traindataSet.size(); j++) {
380                        if(j != i) {
381                            // if type1 and type2 errors are < 0.5 we allow the model in the final voting
382                            errors = this.evaluate((GPRun)classifier, traindataSet.get(j));
383                            if((errors[0] / traindataSet.get(j).numInstances()) < 0.5 && (errors[0] / traindataSet.get(j).numInstances()) < 0.5) {
384                                candidates.add(classifier);                           
385                            }
386                        }
387                    }
388                }
389               
390                // now after the evaluation we do a model selection where only one model remains for the given training data
391                double smallest_error_count = Double.MAX_VALUE;
392                double[] errors;
393                Classifier best = null;
394                for(int ii=0; ii < candidates.size(); ii++) {
395                    for(int j=0; j < traindataSet.size(); j++) {
396                        if(j != i) {
397                            errors = this.evaluate((GPRun)candidates.get(ii), traindataSet.get(j));
398                           
399                            if(errors[0]+errors[1] < smallest_error_count) {
400                                best = candidates.get(ii);
401                            }
402                        }
403                    }
404                }
405               
406                // now we have the best classifier for this training data
407                classifiers.add(best);
408               
409            }
410        }
411       
412        /**
413         * Use the remaining classifiers for our voting
414         */
415        @Override
416        public double classifyInstance(Instance instance) {
417           
418            int vote_positive = 0;
419            int vote_negative = 0;
420           
421            for (int i = 0; i < classifiers.size(); i++) {
422                Classifier classifier = classifiers.get(i);
423               
424                GPGenotype gp = ((GPRun)classifier).getGp();
425                Variable[] vars = ((GPRun)classifier).getVariables();
426               
427                IGPProgram fitest = gp.getAllTimeBest();  // all time fitest
428                for(int j = 0; j < instance.numAttributes()-1; j++) {
429                   vars[j].set(instance.value(j));
430                }
431               
432                if(fitest.execute_double(0, vars) < 0.5) {
433                    vote_positive += 1;
434                }else {
435                    vote_negative += 1;
436                }
437            }
438           
439            if(vote_positive >= (classifiers.size()/2)) {
440                return 1.0;
441            }else {
442                return 0.0;
443            }
444        }
445    }
446   
447    /**
448     * GP Multiple Data Sets Validation Classifier
449     *
450     *
451     * for one test data set:
452     *   for one in 6 possible training data sets:
453     *     For 200 GP Runs:
454     *       train one Classifier with this training data
455     *       then evaluate the classifier with the remaining project
456     *       if the candidate model performs bad (error type1 or type2 > 50%) discard it
457     * for the remaining model candidates the best one is used
458     *
459     */
460    public class GPVClassifier extends AbstractClassifier {
461       
462        private List<Classifier> classifiers = null;
463        private Classifier best = null;
464
465        private static final long serialVersionUID = 3708714057579101522L;
466
467
468        /** Build the GP Multiple Data Sets Validation Classifier
469         *
470         * - Traindata one of the Instances of the Set (which one? The firsT? as it is a list?)
471         * - Testdata one other Instances of the Set (the next one? chose randomly?)
472         * - Evaluation the rest of the instances
473         *
474         * @param traindataSet
475         * @throws Exception
476         */
477        public void buildClassifier(SetUniqueList<Instances> traindataSet) throws Exception {
478
479            // each classifier is trained with one project from the set
480            // then is evaluated on the rest
481            for(int i=0; i < traindataSet.size(); i++) {
482               
483                // candidates we get out of evaluation
484                LinkedList<Classifier> candidates = new LinkedList<>();
485               
486                // 200 runs
487                for(int k=0; k < 200; k++) {
488                    Classifier classifier = new GPRun();
489                   
490                    // one project is training data
491                    classifier.buildClassifier(traindataSet.get(i));
492                   
493                    double[] errors;
494                   
495                    // rest of the set is evaluation data, we evaluate now
496                    for(int j=0; j < traindataSet.size(); j++) {
497                        if(j != i) {
498                            // if type1 and type2 errors are < 0.5 we allow the model in the final voting
499                            errors = this.evaluate((GPRun)classifier, traindataSet.get(j));
500                            if((errors[0] / traindataSet.get(j).numInstances()) < 0.5 && (errors[0] / traindataSet.get(j).numInstances()) < 0.5) {
501                                candidates.add(classifier);                           
502                            }
503                        }
504                    }
505                }
506               
507                // now after the evaluation we do a model selection where only one model remains per training data set
508                // from that we chose the best model
509               
510                // now after the evaluation we do a model selection where only one model remains for the given training data
511                double smallest_error_count = Double.MAX_VALUE;
512                double[] errors;
513                Classifier best = null;
514                for(int ii=0; ii < candidates.size(); ii++) {
515                    for(int j=0; j < traindataSet.size(); j++) {
516                        if(j != i) {
517                            errors = this.evaluate((GPRun)candidates.get(ii), traindataSet.get(j));
518                           
519                            if(errors[0]+errors[1] < smallest_error_count) {
520                                best = candidates.get(ii);
521                            }
522                        }
523                    }
524                }
525               
526                // now we have the best classifier for this training data
527                classifiers.add(best);
528            }
529           
530            // now determine the best classifier for all training data
531            double smallest_error_count = Double.MAX_VALUE;
532            double error_count;
533            double errors[];
534            for(int j=0; j < classifiers.size(); j++) {
535                error_count = 0;
536                Classifier current = classifiers.get(j);
537                for(int i=0; i < traindataSet.size(); i++) {
538                    errors = this.evaluate((GPRun)current, traindataSet.get(i));
539                    error_count = errors[0] + errors[1];
540                }
541               
542                if(error_count < smallest_error_count) {
543                    best = current;
544                }
545            }
546        }
547       
548        @Override
549        public void buildClassifier(Instances traindata) throws Exception {
550            final Classifier classifier = new GPRun();
551            classifier.buildClassifier(traindata);
552            classifiers.add(classifier);
553        }
554       
555        public double[] evaluate(GPRun classifier, Instances evalData) {
556            GPGenotype gp = classifier.getGp();
557            Variable[] vars = classifier.getVariables();
558           
559            IGPProgram fitest = gp.getAllTimeBest();  // selects the fitest of all not just the last generation
560           
561            double classification;
562            int error_type1 = 0;
563            int error_type2 = 0;
564            int number_instances = evalData.numInstances();
565           
566            for(Instance instance: evalData) {
567               
568                for(int i = 0; i < instance.numAttributes()-1; i++) {
569                    vars[i].set(instance.value(i));
570                }
571               
572                classification = fitest.execute_double(0, vars);
573               
574                // classification < 0.5 we say defective
575                if(classification < 0.5) {
576                    if(instance.classValue() != 1.0) {
577                        error_type1 += 1;
578                    }
579                }else {
580                    if(instance.classValue() == 1.0) {
581                        error_type2 += 1;
582                    }
583                }
584            }
585           
586            double et1_per = error_type1 / number_instances;
587            double et2_per = error_type2 / number_instances;
588           
589            // return some kind of fehlerquote?
590            //return (error_type1 + error_type2) / number_instances;
591            return new double[]{error_type1, error_type2};
592        }
593       
594        /**
595         * Use only the best classifier from our evaluation phase
596         */
597        @Override
598        public double classifyInstance(Instance instance) {
599            GPGenotype gp = ((GPRun)best).getGp();
600            Variable[] vars = ((GPRun)best).getVariables();
601           
602            IGPProgram fitest = gp.getAllTimeBest();  // all time fitest
603            for(int i = 0; i < instance.numAttributes()-1; i++) {
604               vars[i].set(instance.value(i));
605            }
606           
607            double classification = fitest.execute_double(0, vars);
608           
609            if(classification < 0.5) {
610                return 1.0;
611            }else {
612                return 0.0;
613            }
614        }
615    }
616   
617   
618    /**
619    * Custom GT implementation from the paper
620    */
621    public class GT extends MathCommand implements ICloneable {
622       
623        private static final long serialVersionUID = 113454184817L;
624
625        public GT(final GPConfiguration a_conf, java.lang.Class a_returnType) throws InvalidConfigurationException {
626            super(a_conf, 2, a_returnType);
627        }
628
629        public String toString() {
630            return "GT(&1, &2)";
631        }
632
633        public String getName() {
634            return "GT";
635        }   
636
637        public float execute_float(ProgramChromosome c, int n, Object[] args) {
638            float f1 = c.execute_float(n, 0, args);
639            float f2 = c.execute_float(n, 1, args);
640
641            float ret = 1.0f;
642            if(f1 > f2) {
643                ret = 0.0f;
644            }
645
646            return ret;
647        }
648
649        public double execute_double(ProgramChromosome c, int n, Object[] args) {
650            double f1 = c.execute_double(n, 0, args);
651            double f2 = c.execute_double(n, 1, args);
652
653            double ret = 1;
654            if(f1 > f2)  {
655                ret = 0;
656            }
657            return ret;
658        }
659
660        public Object clone() {
661            try {
662                GT result = new GT(getGPConfiguration(), getReturnType());
663                return result;
664            }catch(Exception ex) {
665                throw new CloneException(ex);
666            }
667        }
668    }
669}
Note: See TracBrowser for help on using the repository browser.