Ignore:
Timestamp:
08/17/16 19:21:07 (8 years ago)
Author:
atrautsch
Message:

Bigger code cleanup, still some work to do in the attribute selection

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/CrossPare/src/de/ugoe/cs/cpdp/training/MetricMatchingTraining.java

    r137 r138  
    4040import weka.core.Attribute; 
    4141import weka.core.DenseInstance; 
    42 import weka.core.FastVector; 
    4342import weka.core.Instance; 
    4443import weka.core.Instances; 
    4544 
    4645/** 
    47  * Implements Heterogenous Defect Prediction after Nam et al. 
     46 * Implements Heterogenous Defect Prediction after Nam et al.  2015. 
     47 *  
     48 * We extend WekaBaseTraining because we have to Wrap the Classifier to use MetricMatching. 
     49 * This also means we can use any Weka Classifier not just LogisticRegression. 
    4850 *  
    4951 * TODO: 
    5052 * - spacing, coding conventions 
    51  * - we depend on having exactly one class attribute on multiple locations 
    52  * -  
     53 * - clean up attribute selection on train data 
     54 * - percentile test run 
    5355 */ 
    5456public class MetricMatchingTraining extends WekaBaseTraining implements ISetWiseTestdataAwareTrainingStrategy { 
    5557 
    5658    private SetUniqueList<Instances> traindataSet; 
    57     private MetricMatch mm; 
    58     private final Classifier classifier = new MetricMatchingClassifier(); 
     59    private MetricMatch mm = null; 
     60    private Classifier classifier = new MetricMatchingClassifier(); 
    5961     
    6062    private String method; 
     
    7072    } 
    7173 
    72  
     74    /** 
     75     * Set similarity measure method. 
     76     */ 
    7377    @Override 
    7478    public void setMethod(String method) { 
     
    7680    } 
    7781 
    78  
     82    /** 
     83     * Set threshold for similarity measure. 
     84     */ 
    7985    @Override 
    8086    public void setThreshold(String threshold) { 
     
    8894        @Override 
    8995        public void apply(SetUniqueList<Instances> traindataSet, Instances testdata) { 
    90                 this.traindataSet = traindataSet; 
    91  
    92                 double score = 0; // custom ranking score to select the best training data from the set 
     96 
     97                double score = 0; // matching score to select the best matching training data from the set 
    9398                int num = 0; 
    9499                int biggest_num = 0; 
    95100                MetricMatch tmp; 
    96                 MetricMatch biggest = null; 
    97                 for (Instances traindata : this.traindataSet) { 
     101                for (Instances traindata : traindataSet) { 
    98102                        num++; 
    99103 
     
    110114                         
    111115                        // we only select the training data from our set with the most matching attributes 
    112                         if (tmp.getScore() > score) { 
     116                        if (tmp.getScore() > score && tmp.attributes.size() > 0) { 
    113117                                score = tmp.getScore(); 
    114                                 biggest = tmp; 
     118                                this.mm = tmp; 
    115119                                biggest_num = num; 
    116120                        } 
    117121                } 
    118122                 
    119                 if (biggest == null) { 
    120                     throw new RuntimeException("not enough matching attributes found"); 
    121                 } 
    122  
    123                 // we use the best match according to our matching score 
    124                 this.mm = biggest; 
    125                 Instances ilist = this.mm.getMatchedTrain(); 
    126                 Console.traceln(Level.INFO, "Chosing the trainingdata set num "+biggest_num +" with " + score + " matching score, " + ilist.size() + " instances, and " + biggest.attributes.size() + " matched attributes out of a possible set of " + traindataSet.size() + " sets"); 
    127                  
    128                 for(int i = 0; i < this.mm.attributes.size(); i++) { 
    129                     Console.traceln(Level.INFO, "Matched Attribute: " + this.mm.train.attribute(i).name() + " with " + this.mm.test.attribute((int)this.mm.attributes.get(i)).name()); 
    130                 } 
    131                 // replace traindataSEt 
    132                 //traindataSet = new SetUniqueList<Instances>(); 
    133                 traindataSet.clear(); 
    134                 traindataSet.add(ilist); 
    135                  
    136                 // we have to build the classifier here: 
     123                // if we have found a matching instance we use it 
     124                Instances ilist = null; 
     125                if (this.mm != null) { 
     126                ilist = this.mm.getMatchedTrain(); 
     127                Console.traceln(Level.INFO, "[MATCH FOUND] match: ["+biggest_num +"], score: [" + score + "], instances: [" + ilist.size() + "], attributes: [" + this.mm.attributes.size() + "], ilist attrs: ["+ilist.numAttributes()+"]"); 
     128                for(Map.Entry<Integer, Integer> attmatch : this.mm.attributes.entrySet()) { 
     129                    Console.traceln(Level.INFO, "[MATCHED ATTRIBUTE] source attribute: [" + this.mm.train.attribute(attmatch.getKey()).name() + "], target attribute: [" + this.mm.test.attribute(attmatch.getValue()).name() + "]"); 
     130                } 
     131                 
     132                // replace traindataSet 
     133                //traindataSet.clear(); 
     134                //traindataSet.add(ilist); 
     135            } 
     136                 
     137                // if we have a match we build the special classifier, if not we fall back to FixClass 
    137138                try { 
    138                      
    139                         // 
    140                     if (this.classifier == null) { 
    141                         Console.traceln(Level.SEVERE, "Classifier is null"); 
    142                     } 
    143                         //Console.traceln(Level.INFO, "Building classifier with the matched training data with " + ilist.size() + " instances and "+ ilist.numAttributes() + " attributes"); 
    144                         this.classifier.buildClassifier(ilist); 
    145                         ((MetricMatchingClassifier) this.classifier).setMetricMatching(this.mm); 
     139                        if(this.mm != null) { 
     140                            this.classifier.buildClassifier(ilist); 
     141                            ((MetricMatchingClassifier) this.classifier).setMetricMatching(this.mm); 
     142                        }else { 
     143                            this.classifier = new FixClass(); 
     144                            this.classifier.buildClassifier(ilist);  // this is null, but the FixClass Classifier does not use it anyway 
     145                        } 
    146146                }catch(Exception e) { 
    147147                        e.printStackTrace(); 
     
    166166                        this.classifier.buildClassifier(traindata); 
    167167                } 
    168  
     168                 
     169                /** 
     170                 * Sets the MetricMatch instance so that we can use matched test data later. 
     171                 * @param mm 
     172                 */ 
    169173                public void setMetricMatching(MetricMatch mm) { 
    170174                        this.mm = mm; 
     
    172176                 
    173177                /** 
    174                  * Here we can not do the metric matching because we only get one instance 
     178                 * Here we can not do the metric matching because we only get one instance. 
     179                 * Therefore we need a MetricMatch instance beforehand to use here. 
    175180                 */ 
    176181                public double classifyInstance(Instance testdata) { 
    177                         // todo: maybe we can pull the instance out of our matched testdata? 
     182                    // get a copy of testdata Instance with only the matched attributes 
    178183                        Instance ntest = this.mm.getMatchedTestInstance(testdata); 
    179184 
     
    191196         
    192197        /** 
    193          * Encapsulates MetricMatching on Instances Arrays 
     198         * Encapsulates one MetricMatching process. 
     199         * One source (train) matches against one target (test). 
    194200         */ 
    195201    public class MetricMatch { 
     
    198204                 
    199205                // used to sum up the matching values of all attributes 
    200                 double p_sum = 0; 
     206                protected double p_sum = 0; 
    201207                 
    202208                // attribute matching, train -> test 
    203209                HashMap<Integer, Integer> attributes = new HashMap<Integer,Integer>(); 
    204                 //double[][] weights;  /* weight matrix, needed to find maximum weighted bipartite matching */ 
    205                   
    206                 ArrayList<double[]> train_values; 
    207                 ArrayList<double[]> test_values; 
    208  
    209                 // todo: this constructor does not work 
    210                 public MetricMatch() { 
    211                 } 
     210                 
     211                // used for similarity tests 
     212                protected ArrayList<double[]> train_values; 
     213                protected ArrayList<double[]> test_values; 
     214 
    212215                  
    213216                public MetricMatch(Instances train, Instances test) { 
    214                     // expensive! but we are dropping the attributes so we have to copy all of the data 
    215                     this.train = new Instances(train); 
    216                         this.test = new Instances(test); 
    217                           
    218                         // 1. convert metrics of testdata and traindata to later use in test 
     217                        this.train = train; 
     218                        this.test = test; 
     219                         
     220                        // convert metrics of testdata and traindata to later use in similarity tests 
    219221                        this.train_values = new ArrayList<double[]>(); 
    220                         for (int i = 0; i < this.train.numAttributes()-1; i++) { 
    221                             this.train_values.add(train.attributeToDoubleArray(i)); 
     222                        for (int i = 0; i < this.train.numAttributes(); i++) { 
     223                            if(this.train.classIndex() != i) { 
     224                                this.train_values.add(this.train.attributeToDoubleArray(i)); 
     225                            } 
    222226                        } 
    223227                         
    224228                        this.test_values = new ArrayList<double[]>(); 
    225                         for (int i=0; i < this.test.numAttributes()-1; i++) { 
    226                             this.test_values.add(this.test.attributeToDoubleArray(i)); 
     229                        for (int i=0; i < this.test.numAttributes(); i++) { 
     230                            if(this.test.classIndex() != i) { 
     231                                this.test_values.add(this.test.attributeToDoubleArray(i)); 
     232                            } 
    227233                        } 
    228234                } 
     
    235241                 */ 
    236242            public double getScore() { 
    237                 int as = this.attributes.size(); 
     243                int as = this.attributes.size();  // # of attributes that were matched 
    238244                 
    239245                // we use thresholding ranking approach for numInstances to influence the matching score 
     
    250256            } 
    251257                  
    252                  public HashMap<Integer, Integer> getAttributes() { 
    253                      return this.attributes; 
    254                  } 
     258                public HashMap<Integer, Integer> getAttributes() { 
     259                    return this.attributes; 
     260                } 
    255261                  
    256                  public int getNumInstances() { 
    257                      return this.train_values.get(0).length; 
    258                  } 
     262                public int getNumInstances() { 
     263                    return this.train_values.get(0).length; 
     264                } 
     265                 
     266                /**  
     267                 * This creates a new Instance out of the passed Instance and the previously matched attributes. 
     268                 * We do this because the evaluation phase requires an original Instance with every attribute. 
     269                 *  
     270                 * @param test instance 
     271                 * @return new instance 
     272                 */ 
     273                public Instance getMatchedTestInstance(Instance test) { 
     274                    //create new instance with our matched number of attributes + 1 (the class attribute) 
     275                        Instances testdata = this.getMatchedTest(); 
     276 
     277                        Instance ni = new DenseInstance(this.attributes.size()+1); 
     278                        ni.setDataset(testdata); 
     279                          
     280                for(Map.Entry<Integer, Integer> attmatch : this.attributes.entrySet()) { 
     281                    ni.setValue(testdata.attribute(attmatch.getKey()), test.value(attmatch.getValue())); 
     282                } 
     283                 
     284                        ni.setClassValue(test.value(test.classAttribute())); 
     285 
     286                        return ni; 
     287        } 
     288 
     289        /** 
     290         * returns a new instances array with the metric matched training data 
     291         *  
     292         * @return instances 
     293         */ 
     294                public Instances getMatchedTrain() { 
     295                    return this.getMatchedInstances("train", this.train); 
     296                } 
    259297                  
    260                  public Instance getMatchedTestInstance(Instance test) { 
    261                          // create new instance with our matched number of attributes + 1 (the class attribute) 
    262                          //Console.traceln(Level.INFO, "getting matched instance"); 
    263                          Instances testdata = this.getMatchedTest(); 
    264                           
    265                          //Instance ni = new DenseInstance(this.attmatch.size()+1); 
    266                          Instance ni = new DenseInstance(this.attributes.size()+1); 
    267                          ni.setDataset(testdata); 
    268                           
    269                          //Console.traceln(Level.INFO, "Attributes to match: " + this.attmatch.size() + ""); 
    270                           
    271                          Iterator it = this.attributes.entrySet().iterator(); 
    272                          int j = 0; 
    273                          while (it.hasNext()) { 
    274                                  Map.Entry values = (Map.Entry)it.next(); 
    275                                  ni.setValue(testdata.attribute(j), test.value((int)values.getValue())); 
    276                                  j++; 
    277                                   
    278                          } 
    279                           
    280                          ni.setClassValue(test.value(test.classAttribute())); 
    281  
    282                          return ni; 
    283                  } 
    284  
    285          /** 
    286           * returns a new instances array with the metric matched training data 
    287           *  
    288           * @return instances 
    289           */ 
    290                  public Instances getMatchedTrain() { 
    291                          return this.getMatchedInstances("train", this.train); 
    292                  } 
    293                   
    294                  /** 
    295                   * returns a new instances array with the metric matched test data 
    296                   *  
    297                   * @return instances 
    298                   */ 
    299                  public Instances getMatchedTest() { 
    300                          return this.getMatchedInstances("test", this.test); 
    301                  } 
    302                   
    303                  // todo: there must be a better way 
    304                  // https://weka.wikispaces.com/Programmatic+Use 
    305                  private Instances getMatchedInstances(String name, Instances data) { 
    306                          //Console.traceln(Level.INFO, "Constructing instances from: " + name); 
    307                      // construct our new attributes 
    308                          Attribute[] attrs = new Attribute[this.attributes.size()+1]; 
    309                          FastVector fwTrain = new FastVector(this.attributes.size()); 
    310                          for (int i=0; i < this.attributes.size(); i++) { 
    311                                  attrs[i] = new Attribute(String.valueOf(i)); 
    312                                  fwTrain.addElement(attrs[i]); 
    313                          } 
    314                          // add our ClassAttribute (which is not numeric!) 
    315                          ArrayList<String> acl= new ArrayList<String>(); 
    316                          acl.add("0"); 
    317                          acl.add("1"); 
    318                           
    319                          fwTrain.addElement(new Attribute("bug", acl)); 
    320                          Instances newTrain = new Instances(name, fwTrain, data.size()); 
    321                          newTrain.setClassIndex(newTrain.numAttributes()-1); 
    322                           
    323                          //Console.traceln(Level.INFO, "data attributes: " + data.numAttributes() + ", this.attributes: "+this.attributes.size()); 
    324                           
    325                          for (int i=0; i < data.size(); i++) { 
    326                                  Instance ni = new DenseInstance(this.attributes.size()+1); 
    327                                  
    328                                  Iterator it = this.attributes.entrySet().iterator(); 
    329                                  int j = 0; 
    330                                  while (it.hasNext()) { 
    331                                          Map.Entry values = (Map.Entry)it.next(); 
    332                                          int value = (int)values.getValue(); 
    333                                           
    334                                          // key ist traindata 
    335                                          if (name.equals("train")) { 
    336                                                  value = (int)values.getKey(); 
    337                                          } 
    338                                          //Console.traceln(Level.INFO, "setting attribute " + j + " with data from instance: " + i); 
    339                                          ni.setValue(newTrain.attribute(j), data.instance(i).value(value)); 
    340                                          j++; 
    341                                  } 
    342                                  ni.setValue(ni.numAttributes()-1, data.instance(i).value(data.classAttribute())); 
    343                                   
    344                                  newTrain.add(ni); 
    345                          } 
    346                           
    347                     return newTrain; 
    348         } 
    349                   
     298        /** 
     299                 * returns a new instances array with the metric matched test data 
     300                 *  
     301                 * @return instances 
     302                 */ 
     303                public Instances getMatchedTest() { 
     304                    return this.getMatchedInstances("test", this.test); 
     305                } 
     306                 
     307                /** 
     308                 * We could drop unmatched attributes from our instances datasets. 
     309                 * Alas, that would not be nice for the following postprocessing jobs and would not work at all for evaluation. 
     310                 * We keep this as a warning for future generations. 
     311                 *  
     312                 * @param name 
     313                 * @param data 
     314                 */ 
     315                @SuppressWarnings("unused") 
     316        private void dropUnmatched(String name, Instances data) { 
     317                    for(int i = 0; i < data.numAttributes(); i++) { 
     318                        if(data.classIndex() == i) { 
     319                            continue; 
     320                        } 
     321                         
     322                        if(name.equals("train") && !this.attributes.containsKey(i)) {  
     323                            data.deleteAttributeAt(i); 
     324                        } 
     325                         
     326                        if(name.equals("test") && !this.attributes.containsValue(i)) { 
     327                            data.deleteAttributeAt(i); 
     328                        } 
     329                    } 
     330                } 
     331 
     332                 
     333        /** 
     334         * Returns a deep copy of passed Instances data for Train or Test data. 
     335         * It only keeps attributes that have been matched. 
     336         *  
     337         * @param name 
     338         * @param data 
     339         * @return matched Instances 
     340         */ 
     341                private Instances getMatchedInstances(String name, Instances data) { 
     342                    ArrayList<Attribute> attrs = new ArrayList<Attribute>(); 
     343             
     344                    // bug attr is a string, really! 
     345                    ArrayList<String> bug = new ArrayList<String>(); 
     346            bug.add("0"); 
     347            bug.add("1"); 
     348             
     349            // add our matched attributes and last the bug 
     350                    for(Map.Entry<Integer, Integer> attmatch : this.attributes.entrySet()) { 
     351                        attrs.add(new Attribute(String.valueOf(attmatch.getValue()))); 
     352                    } 
     353                    attrs.add(new Attribute("bug", bug)); 
     354                     
     355                    // create new instances object of the same size (at least for instances) 
     356                    Instances newInst = new Instances(name, attrs, data.size()); 
     357                     
     358                    // set last as class 
     359                    newInst.setClassIndex(newInst.numAttributes()-1); 
     360                     
     361                    // copy data for matched attributes, this depends if we return train or test data 
     362            for (int i=0; i < data.size(); i++) { 
     363                Instance ni = new DenseInstance(this.attributes.size()+1); 
     364                 
     365                int j = 0; // new indices! 
     366                for(Map.Entry<Integer, Integer> attmatch : this.attributes.entrySet()) { 
     367   
     368                    // test attribute match 
     369                    int value = attmatch.getValue(); 
     370                     
     371                    // train attribute match 
     372                    if(name.equals("train")) { 
     373                        value = attmatch.getKey(); 
     374                    } 
     375                     
     376                    ni.setValue(newInst.attribute(j), data.instance(i).value(value)); 
     377                    j++; 
     378                } 
     379                ni.setValue(ni.numAttributes()-1, data.instance(i).value(data.classAttribute())); 
     380                newInst.add(ni); 
     381            } 
     382 
     383            return newInst; 
     384                } 
    350385                  
    351386                /** 
     
    364399                        //Console.traceln(Level.INFO, "-----"); 
    365400                } 
     401                 
    366402                 
    367403                private void attributeSelection(Instances which) throws Exception { 
     
    431467                  
    432468                 
    433          
     469        /** 
     470         * Executes the similarity matching between train and test data. 
     471         *  
     472         * After this function is finished we have this.attributes with the correct matching between train and test data attributes. 
     473         *  
     474         * @param type 
     475         * @param cutoff 
     476         */ 
    434477                public void matchAttributes(String type, double cutoff) { 
    435478                     
    436  
    437479                    MWBMatchingAlgorithm mwbm = new MWBMatchingAlgorithm(this.train.numAttributes(), this.test.numAttributes()); 
    438480                     
     
    447489                    } 
    448490                     
    449                     // resulting maximal match 
     491                    // resulting maximal match gets assigned to this.attributes 
    450492            int[] result = mwbm.getMatching(); 
    451493            for( int i = 0; i < result.length; i++) { 
     
    468510                    for( int i = 0; i < this.train.numAttributes(); i++ ) { 
    469511                for( int j = 0; j < this.test.numAttributes(); j++ ) { 
    470                     // negative infinity counts as not present, we do this so we don't have to map between attribute indexs in weka 
     512                    // negative infinity counts as not present, we do this so we don't have to map between attribute indexes in weka 
    471513                    // and the result of the mwbm computation 
    472514                    mwbm.setWeight(i, j, Double.NEGATIVE_INFINITY); 
     
    510552                } 
    511553                  
    512                  /** 
    513                   * Calculate Spearmans rank correlation coefficient as matching score 
    514                   * The number of instances for the source and target needs to be the same so we randomly sample from the bigger one. 
    515                   *  
    516                   * @param cutoff 
    517                   * @param mwbmatching 
    518                   */ 
    519                  public void spearmansRankCorrelation(double cutoff, MWBMatchingAlgorithm mwbm) { 
    520                         double p = 0; 
    521  
    522                          SpearmansCorrelation t = new SpearmansCorrelation(); 
    523  
    524                          // size has to be the same so we randomly sample the number of the smaller sample from the big sample 
    525                          if (this.train.size() > this.test.size()) { 
    526                              this.sample(this.train, this.test, this.train_values); 
    527                          }else if (this.test.size() > this.train.size()) { 
    528                              this.sample(this.test, this.train, this.test_values); 
    529                          } 
     554                /** 
     555                 * Calculate Spearmans rank correlation coefficient as matching score 
     556                 * The number of instances for the source and target needs to be the same so we randomly sample from the bigger one. 
     557                 *  
     558                 * @param cutoff 
     559                 * @param mwbmatching 
     560                 */ 
     561                public void spearmansRankCorrelation(double cutoff, MWBMatchingAlgorithm mwbm) { 
     562                    double p = 0; 
     563 
     564                        SpearmansCorrelation t = new SpearmansCorrelation(); 
     565 
     566                        // size has to be the same so we randomly sample the number of the smaller sample from the big sample 
     567                        if (this.train.size() > this.test.size()) { 
     568                            this.sample(this.train, this.test, this.train_values); 
     569                        }else if (this.test.size() > this.train.size()) { 
     570                            this.sample(this.test, this.train, this.test_values); 
     571                        } 
    530572                         
    531573            // try out possible attribute combinations 
     
    622664                    // this may invoke exactP on small sample sizes which will not terminate in all cases 
    623665                                        //p = t.kolmogorovSmirnovTest(this.train_values.get(i), this.test_values.get(j), false); 
     666 
     667                    // this uses approximateP everytime 
    624668                                        p = t.approximateP(t.kolmogorovSmirnovStatistic(this.train_values.get(i), this.test_values.get(j)), this.train_values.get(i).length, this.test_values.get(j).length); 
    625669                                        if (p > cutoff) { 
Note: See TracChangeset for help on using the changeset viewer.