~ubuntu-branches/ubuntu/trusty/weka/trusty-proposed

« back to all changes in this revision

Viewing changes to weka/classifiers/functions/VotedPerceptron.java

  • Committer: Bazaar Package Importer
  • Author(s): Soeren Sonnenburg
  • Date: 2008-02-24 09:18:45 UTC
  • Revision ID: james.westby@ubuntu.com-20080224091845-1l8zy6fm6xipbzsr
Tags: upstream-3.5.7+tut1
ImportĀ upstreamĀ versionĀ 3.5.7+tut1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *    This program is free software; you can redistribute it and/or modify
 
3
 *    it under the terms of the GNU General Public License as published by
 
4
 *    the Free Software Foundation; either version 2 of the License, or
 
5
 *    (at your option) any later version.
 
6
 *
 
7
 *    This program is distributed in the hope that it will be useful,
 
8
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
9
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
10
 *    GNU General Public License for more details.
 
11
 *
 
12
 *    You should have received a copy of the GNU General Public License
 
13
 *    along with this program; if not, write to the Free Software
 
14
 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
15
 */
 
16
 
 
17
/*
 
18
 *    VotedPerceptron.java
 
19
 *    Copyright (C) 1999 University of Waikato, Hamilton, New Zealand
 
20
 *
 
21
 */
 
22
 
 
23
 
 
24
package weka.classifiers.functions;
 
25
 
 
26
import weka.classifiers.Classifier;
 
27
import weka.core.Capabilities;
 
28
import weka.core.Instance;
 
29
import weka.core.Instances;
 
30
import weka.core.Option;
 
31
import weka.core.OptionHandler;
 
32
import weka.core.TechnicalInformation;
 
33
import weka.core.TechnicalInformationHandler;
 
34
import weka.core.Utils;
 
35
import weka.core.Capabilities.Capability;
 
36
import weka.core.TechnicalInformation.Field;
 
37
import weka.core.TechnicalInformation.Type;
 
38
import weka.filters.Filter;
 
39
import weka.filters.unsupervised.attribute.NominalToBinary;
 
40
import weka.filters.unsupervised.attribute.ReplaceMissingValues;
 
41
 
 
42
import java.util.Enumeration;
 
43
import java.util.Random;
 
44
import java.util.Vector;
 
45
 
 
46
/**
 
47
 <!-- globalinfo-start -->
 
48
 * Implementation of the voted perceptron algorithm by Freund and Schapire. Globally replaces all missing values, and transforms nominal attributes into binary ones.<br/>
 
49
 * <br/>
 
50
 * For more information, see:<br/>
 
51
 * <br/>
 
52
 * Y. Freund, R. E. Schapire: Large margin classification using the perceptron algorithm. In: 11th Annual Conference on Computational Learning Theory, New York, NY, 209-217, 1998.
 
53
 * <p/>
 
54
 <!-- globalinfo-end -->
 
55
 *
 
56
 <!-- technical-bibtex-start -->
 
57
 * BibTeX:
 
58
 * <pre>
 
59
 * &#64;inproceedings{Freund1998,
 
60
 *    address = {New York, NY},
 
61
 *    author = {Y. Freund and R. E. Schapire},
 
62
 *    booktitle = {11th Annual Conference on Computational Learning Theory},
 
63
 *    pages = {209-217},
 
64
 *    publisher = {ACM Press},
 
65
 *    title = {Large margin classification using the perceptron algorithm},
 
66
 *    year = {1998}
 
67
 * }
 
68
 * </pre>
 
69
 * <p/>
 
70
 <!-- technical-bibtex-end -->
 
71
 *
 
72
 <!-- options-start -->
 
73
 * Valid options are: <p/>
 
74
 * 
 
75
 * <pre> -I &lt;int&gt;
 
76
 *  The number of iterations to be performed.
 
77
 *  (default 1)</pre>
 
78
 * 
 
79
 * <pre> -E &lt;double&gt;
 
80
 *  The exponent for the polynomial kernel.
 
81
 *  (default 1)</pre>
 
82
 * 
 
83
 * <pre> -S &lt;int&gt;
 
84
 *  The seed for the random number generation.
 
85
 *  (default 1)</pre>
 
86
 * 
 
87
 * <pre> -M &lt;int&gt;
 
88
 *  The maximum number of alterations allowed.
 
89
 *  (default 10000)</pre>
 
90
 * 
 
91
 <!-- options-end -->
 
92
 *
 
93
 * @author Eibe Frank (eibe@cs.waikato.ac.nz)
 
94
 * @version $Revision: 1.22 $ 
 
95
 */
 
96
public class VotedPerceptron 
 
97
  extends Classifier 
 
98
  implements OptionHandler, TechnicalInformationHandler {
 
99
  
 
100
  /** for serialization */
 
101
  static final long serialVersionUID = -1072429260104568698L;
 
102
  
 
103
  /** The maximum number of alterations to the perceptron */
 
104
  private int m_MaxK = 10000;
 
105
 
 
106
  /** The number of iterations */
 
107
  private int m_NumIterations = 1;
 
108
 
 
109
  /** The exponent */
 
110
  private double m_Exponent = 1.0;
 
111
 
 
112
  /** The actual number of alterations */
 
113
  private int m_K = 0;
 
114
 
 
115
  /** The training instances added to the perceptron */
 
116
  private int[] m_Additions = null;
 
117
 
 
118
  /** Addition or subtraction? */
 
119
  private boolean[] m_IsAddition = null;
 
120
 
 
121
  /** The weights for each perceptron */
 
122
  private int[] m_Weights = null;
 
123
  
 
124
  /** The training instances */
 
125
  private Instances m_Train = null;
 
126
 
 
127
  /** Seed used for shuffling the dataset */
 
128
  private int m_Seed = 1;
 
129
 
 
130
  /** The filter used to make attributes numeric. */
 
131
  private NominalToBinary m_NominalToBinary;
 
132
 
 
133
  /** The filter used to get rid of missing values. */
 
134
  private ReplaceMissingValues m_ReplaceMissingValues;
 
135
 
 
136
  /**
 
137
   * Returns a string describing this classifier
 
138
   * @return a description of the classifier suitable for
 
139
   * displaying in the explorer/experimenter gui
 
140
   */
 
141
  public String globalInfo() {
 
142
    return 
 
143
        "Implementation of the voted perceptron algorithm by Freund and "
 
144
      + "Schapire. Globally replaces all missing values, and transforms "
 
145
      + "nominal attributes into binary ones.\n\n"
 
146
      + "For more information, see:\n\n"
 
147
      + getTechnicalInformation().toString();
 
148
  }
 
149
 
 
150
  /**
 
151
   * Returns an instance of a TechnicalInformation object, containing 
 
152
   * detailed information about the technical background of this class,
 
153
   * e.g., paper reference or book this class is based on.
 
154
   * 
 
155
   * @return the technical information about this class
 
156
   */
 
157
  public TechnicalInformation getTechnicalInformation() {
 
158
    TechnicalInformation        result;
 
159
    
 
160
    result = new TechnicalInformation(Type.INPROCEEDINGS);
 
161
    result.setValue(Field.AUTHOR, "Y. Freund and R. E. Schapire");
 
162
    result.setValue(Field.TITLE, "Large margin classification using the perceptron algorithm");
 
163
    result.setValue(Field.BOOKTITLE, "11th Annual Conference on Computational Learning Theory");
 
164
    result.setValue(Field.YEAR, "1998");
 
165
    result.setValue(Field.PAGES, "209-217");
 
166
    result.setValue(Field.PUBLISHER, "ACM Press");
 
167
    result.setValue(Field.ADDRESS, "New York, NY");
 
168
    
 
169
    return result;
 
170
  }
 
171
 
 
172
  /**
 
173
   * Returns an enumeration describing the available options.
 
174
   *
 
175
   * @return an enumeration of all the available options.
 
176
   */
 
177
  public Enumeration listOptions() {
 
178
 
 
179
    Vector newVector = new Vector(4);
 
180
 
 
181
    newVector.addElement(new Option("\tThe number of iterations to be performed.\n"
 
182
                                    + "\t(default 1)",
 
183
                                    "I", 1, "-I <int>"));
 
184
    newVector.addElement(new Option("\tThe exponent for the polynomial kernel.\n"
 
185
                                    + "\t(default 1)",
 
186
                                    "E", 1, "-E <double>"));
 
187
    newVector.addElement(new Option("\tThe seed for the random number generation.\n"
 
188
                                    + "\t(default 1)",
 
189
                                    "S", 1, "-S <int>"));
 
190
    newVector.addElement(new Option("\tThe maximum number of alterations allowed.\n"
 
191
                                    + "\t(default 10000)",
 
192
                                    "M", 1, "-M <int>"));
 
193
 
 
194
    return newVector.elements();
 
195
  }
 
196
 
 
197
  /**
 
198
   * Parses a given list of options. <p/>
 
199
   *
 
200
   <!-- options-start -->
 
201
   * Valid options are: <p/>
 
202
   * 
 
203
   * <pre> -I &lt;int&gt;
 
204
   *  The number of iterations to be performed.
 
205
   *  (default 1)</pre>
 
206
   * 
 
207
   * <pre> -E &lt;double&gt;
 
208
   *  The exponent for the polynomial kernel.
 
209
   *  (default 1)</pre>
 
210
   * 
 
211
   * <pre> -S &lt;int&gt;
 
212
   *  The seed for the random number generation.
 
213
   *  (default 1)</pre>
 
214
   * 
 
215
   * <pre> -M &lt;int&gt;
 
216
   *  The maximum number of alterations allowed.
 
217
   *  (default 10000)</pre>
 
218
   * 
 
219
   <!-- options-end -->
 
220
   *
 
221
   * @param options the list of options as an array of strings
 
222
   * @throws Exception if an option is not supported
 
223
   */
 
224
  public void setOptions(String[] options) throws Exception {
 
225
    
 
226
    String iterationsString = Utils.getOption('I', options);
 
227
    if (iterationsString.length() != 0) {
 
228
      m_NumIterations = Integer.parseInt(iterationsString);
 
229
    } else {
 
230
      m_NumIterations = 1;
 
231
    }
 
232
    String exponentsString = Utils.getOption('E', options);
 
233
    if (exponentsString.length() != 0) {
 
234
      m_Exponent = (new Double(exponentsString)).doubleValue();
 
235
    } else {
 
236
      m_Exponent = 1.0;
 
237
    }
 
238
    String seedString = Utils.getOption('S', options);
 
239
    if (seedString.length() != 0) {
 
240
      m_Seed = Integer.parseInt(seedString);
 
241
    } else {
 
242
      m_Seed = 1;
 
243
    }
 
244
    String alterationsString = Utils.getOption('M', options);
 
245
    if (alterationsString.length() != 0) {
 
246
      m_MaxK = Integer.parseInt(alterationsString);
 
247
    } else {
 
248
      m_MaxK = 10000;
 
249
    }
 
250
  }
 
251
 
 
252
  /**
 
253
   * Gets the current settings of the classifier.
 
254
   *
 
255
   * @return an array of strings suitable for passing to setOptions
 
256
   */
 
257
  public String[] getOptions() {
 
258
 
 
259
    String[] options = new String [8];
 
260
    int current = 0;
 
261
 
 
262
    options[current++] = "-I"; options[current++] = "" + m_NumIterations;
 
263
    options[current++] = "-E"; options[current++] = "" + m_Exponent;
 
264
    options[current++] = "-S"; options[current++] = "" + m_Seed;
 
265
    options[current++] = "-M"; options[current++] = "" + m_MaxK;
 
266
    while (current < options.length) {
 
267
      options[current++] = "";
 
268
    }
 
269
    return options;
 
270
  }
 
271
 
 
272
  /**
 
273
   * Returns default capabilities of the classifier.
 
274
   *
 
275
   * @return      the capabilities of this classifier
 
276
   */
 
277
  public Capabilities getCapabilities() {
 
278
    Capabilities result = super.getCapabilities();
 
279
 
 
280
    // attributes
 
281
    result.enable(Capability.NOMINAL_ATTRIBUTES);
 
282
    result.enable(Capability.NUMERIC_ATTRIBUTES);
 
283
    result.enable(Capability.DATE_ATTRIBUTES);
 
284
    result.enable(Capability.MISSING_VALUES);
 
285
 
 
286
    // class
 
287
    result.enable(Capability.BINARY_CLASS);
 
288
    result.enable(Capability.MISSING_CLASS_VALUES);
 
289
 
 
290
    // instances
 
291
    result.setMinimumNumberInstances(0);
 
292
    
 
293
    return result;
 
294
  }
 
295
 
 
296
  /**
 
297
   * Builds the ensemble of perceptrons.
 
298
   *
 
299
   * @param insts the data to train the classifier with
 
300
   * @throws Exception if something goes wrong during building
 
301
   */
 
302
  public void buildClassifier(Instances insts) throws Exception {
 
303
 
 
304
    // can classifier handle the data?
 
305
    getCapabilities().testWithFail(insts);
 
306
 
 
307
    // remove instances with missing class
 
308
    insts = new Instances(insts);
 
309
    insts.deleteWithMissingClass();
 
310
    
 
311
    // Filter data
 
312
    m_Train = new Instances(insts);
 
313
    m_ReplaceMissingValues = new ReplaceMissingValues();
 
314
    m_ReplaceMissingValues.setInputFormat(m_Train);
 
315
    m_Train = Filter.useFilter(m_Train, m_ReplaceMissingValues);
 
316
    
 
317
    m_NominalToBinary = new NominalToBinary();
 
318
    m_NominalToBinary.setInputFormat(m_Train);
 
319
    m_Train = Filter.useFilter(m_Train, m_NominalToBinary);
 
320
 
 
321
    /** Randomize training data */
 
322
    m_Train.randomize(new Random(m_Seed));
 
323
 
 
324
    /** Make space to store perceptrons */
 
325
    m_Additions = new int[m_MaxK + 1];
 
326
    m_IsAddition = new boolean[m_MaxK + 1];
 
327
    m_Weights = new int[m_MaxK + 1];
 
328
 
 
329
    /** Compute perceptrons */
 
330
    m_K = 0;
 
331
  out:
 
332
    for (int it = 0; it < m_NumIterations; it++) {
 
333
      for (int i = 0; i < m_Train.numInstances(); i++) {
 
334
        Instance inst = m_Train.instance(i);
 
335
        if (!inst.classIsMissing()) {
 
336
          int prediction = makePrediction(m_K, inst);
 
337
          int classValue = (int) inst.classValue();
 
338
          if (prediction == classValue) {
 
339
            m_Weights[m_K]++;
 
340
          } else {
 
341
            m_IsAddition[m_K] = (classValue == 1);
 
342
            m_Additions[m_K] = i;
 
343
            m_K++;
 
344
            m_Weights[m_K]++;
 
345
          }
 
346
          if (m_K == m_MaxK) {
 
347
            break out;
 
348
          }
 
349
        }
 
350
      }
 
351
    }
 
352
  }
 
353
 
 
354
  /**
 
355
   * Outputs the distribution for the given output.
 
356
   *
 
357
   * Pipes output of SVM through sigmoid function.
 
358
   * @param inst the instance for which distribution is to be computed
 
359
   * @return the distribution
 
360
   * @throws Exception if something goes wrong
 
361
   */
 
362
  public double[] distributionForInstance(Instance inst) throws Exception {
 
363
 
 
364
    // Filter instance
 
365
    m_ReplaceMissingValues.input(inst);
 
366
    m_ReplaceMissingValues.batchFinished();
 
367
    inst = m_ReplaceMissingValues.output();
 
368
 
 
369
    m_NominalToBinary.input(inst);
 
370
    m_NominalToBinary.batchFinished();
 
371
    inst = m_NominalToBinary.output();
 
372
    
 
373
    // Get probabilities
 
374
    double output = 0, sumSoFar = 0;
 
375
    if (m_K > 0) {
 
376
      for (int i = 0; i <= m_K; i++) {
 
377
        if (sumSoFar < 0) {
 
378
          output -= m_Weights[i];
 
379
        } else {
 
380
          output += m_Weights[i];
 
381
        }
 
382
        if (m_IsAddition[i]) {
 
383
          sumSoFar += innerProduct(m_Train.instance(m_Additions[i]), inst);
 
384
        } else {
 
385
          sumSoFar -= innerProduct(m_Train.instance(m_Additions[i]), inst);
 
386
        }
 
387
      }
 
388
    }
 
389
    double[] result = new double[2];
 
390
    result[1] = 1 / (1 + Math.exp(-output));
 
391
    result[0] = 1 - result[1];
 
392
 
 
393
    return result;
 
394
  }
 
395
 
 
396
  /**
 
397
   * Returns textual description of classifier.
 
398
   * 
 
399
   * @return the model as string
 
400
   */
 
401
  public String toString() {
 
402
 
 
403
    return "VotedPerceptron: Number of perceptrons=" + m_K;
 
404
  }
 
405
 
 
406
  /**
 
407
   * Returns the tip text for this property
 
408
   * @return tip text for this property suitable for
 
409
   * displaying in the explorer/experimenter gui
 
410
   */
 
411
  public String maxKTipText() {
 
412
    return "The maximum number of alterations to the perceptron.";
 
413
  }
 
414
 
 
415
  /**
 
416
   * Get the value of maxK.
 
417
   *
 
418
   * @return Value of maxK.
 
419
   */
 
420
  public int getMaxK() {
 
421
    
 
422
    return m_MaxK;
 
423
  }
 
424
  
 
425
  /**
 
426
   * Set the value of maxK.
 
427
   *
 
428
   * @param v  Value to assign to maxK.
 
429
   */
 
430
  public void setMaxK(int v) {
 
431
    
 
432
    m_MaxK = v;
 
433
  }
 
434
  
 
435
  /**
 
436
   * Returns the tip text for this property
 
437
   * @return tip text for this property suitable for
 
438
   * displaying in the explorer/experimenter gui
 
439
   */
 
440
  public String numIterationsTipText() {
 
441
    return "Number of iterations to be performed.";
 
442
  }
 
443
 
 
444
  /**
 
445
   * Get the value of NumIterations.
 
446
   *
 
447
   * @return Value of NumIterations.
 
448
   */
 
449
  public int getNumIterations() {
 
450
    
 
451
    return m_NumIterations;
 
452
  }
 
453
  
 
454
  /**
 
455
   * Set the value of NumIterations.
 
456
   *
 
457
   * @param v  Value to assign to NumIterations.
 
458
   */
 
459
  public void setNumIterations(int v) {
 
460
    
 
461
    m_NumIterations = v;
 
462
  }
 
463
 
 
464
  /**
 
465
   * Returns the tip text for this property
 
466
   * @return tip text for this property suitable for
 
467
   * displaying in the explorer/experimenter gui
 
468
   */
 
469
  public String exponentTipText() {
 
470
    return "Exponent for the polynomial kernel.";
 
471
  }
 
472
 
 
473
  /**
 
474
   * Get the value of exponent.
 
475
   *
 
476
   * @return Value of exponent.
 
477
   */
 
478
  public double getExponent() {
 
479
    
 
480
    return m_Exponent;
 
481
  }
 
482
  
 
483
  /**
 
484
   * Set the value of exponent.
 
485
   *
 
486
   * @param v  Value to assign to exponent.
 
487
   */
 
488
  public void setExponent(double v) {
 
489
    
 
490
    m_Exponent = v;
 
491
  }
 
492
  
 
493
  /**
 
494
   * Returns the tip text for this property
 
495
   * @return tip text for this property suitable for
 
496
   * displaying in the explorer/experimenter gui
 
497
   */
 
498
  public String seedTipText() {
 
499
    return "Seed for the random number generator.";
 
500
  }
 
501
 
 
502
  /**
 
503
   * Get the value of Seed.
 
504
   *
 
505
   * @return Value of Seed.
 
506
   */
 
507
  public int getSeed() {
 
508
    
 
509
    return m_Seed;
 
510
  }
 
511
  
 
512
  /**
 
513
   * Set the value of Seed.
 
514
   *
 
515
   * @param v  Value to assign to Seed.
 
516
   */
 
517
  public void setSeed(int v) {
 
518
    
 
519
    m_Seed = v;
 
520
  }
 
521
 
 
522
  /** 
 
523
   * Computes the inner product of two instances
 
524
   * 
 
525
   * @param i1 first instance
 
526
   * @param i2 second instance
 
527
   * @return the inner product
 
528
   * @throws Exception if computation fails
 
529
   */
 
530
  private double innerProduct(Instance i1, Instance i2) throws Exception {
 
531
 
 
532
    // we can do a fast dot product
 
533
    double result = 0;
 
534
    int n1 = i1.numValues(); int n2 = i2.numValues();
 
535
    int classIndex = m_Train.classIndex();
 
536
    for (int p1 = 0, p2 = 0; p1 < n1 && p2 < n2;) {
 
537
        int ind1 = i1.index(p1);
 
538
        int ind2 = i2.index(p2);
 
539
        if (ind1 == ind2) {
 
540
            if (ind1 != classIndex) {
 
541
                result += i1.valueSparse(p1) *
 
542
                          i2.valueSparse(p2);
 
543
            }
 
544
            p1++; p2++;
 
545
        } else if (ind1 > ind2) {
 
546
            p2++;
 
547
        } else {
 
548
            p1++;
 
549
        }
 
550
    }
 
551
    result += 1.0;
 
552
    
 
553
    if (m_Exponent != 1) {
 
554
      return Math.pow(result, m_Exponent);
 
555
    } else {
 
556
      return result;
 
557
    }
 
558
  }
 
559
 
 
560
  /** 
 
561
   * Compute a prediction from a perceptron
 
562
   * 
 
563
   * @param k
 
564
   * @param inst the instance to make a prediction for
 
565
   * @return the prediction
 
566
   * @throws Exception if computation fails
 
567
   */
 
568
  private int makePrediction(int k, Instance inst) throws Exception {
 
569
 
 
570
    double result = 0;
 
571
    for (int i = 0; i < k; i++) {
 
572
      if (m_IsAddition[i]) {
 
573
        result += innerProduct(m_Train.instance(m_Additions[i]), inst);
 
574
      } else {
 
575
        result -= innerProduct(m_Train.instance(m_Additions[i]), inst);
 
576
      }
 
577
    }
 
578
    if (result < 0) {
 
579
      return 0;
 
580
    } else {
 
581
      return 1;
 
582
    }
 
583
  }
 
584
 
 
585
  /**
 
586
   * Main method.
 
587
   * 
 
588
   * @param argv the commandline options
 
589
   */
 
590
  public static void main(String[] argv) {
 
591
    runClassifier(new VotedPerceptron(), argv);
 
592
  }
 
593
}
 
594