~ubuntu-branches/ubuntu/precise/weka/precise

« back to all changes in this revision

Viewing changes to weka/classifiers/bayes/NaiveBayesMultinomialUpdateable.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
 *    NaiveBayesMultinomialUpdateable.java
 
19
 *    Copyright (C) 2003 University of Waikato, Hamilton, New Zealand
 
20
 *    Copyright (C) 2007 Jiang Su (incremental version)
 
21
 */
 
22
 
 
23
package weka.classifiers.bayes;
 
24
 
 
25
import weka.classifiers.UpdateableClassifier;
 
26
import weka.core.Instance;
 
27
import weka.core.Instances;
 
28
import weka.core.Utils;
 
29
 
 
30
/**
 
31
 <!-- globalinfo-start -->
 
32
 * Class for building and using a multinomial Naive Bayes classifier. For more information see,<br/>
 
33
 * <br/>
 
34
 * Andrew Mccallum, Kamal Nigam: A Comparison of Event Models for Naive Bayes Text Classification. In: AAAI-98 Workshop on 'Learning for Text Categorization', 1998.<br/>
 
35
 * <br/>
 
36
 * The core equation for this classifier:<br/>
 
37
 * <br/>
 
38
 * P[Ci|D] = (P[D|Ci] x P[Ci]) / P[D] (Bayes rule)<br/>
 
39
 * <br/>
 
40
 * where Ci is class i and D is a document.<br/>
 
41
 * <br/>
 
42
 * Incremental version of the algorithm.
 
43
 * <p/>
 
44
 <!-- globalinfo-end -->
 
45
 *
 
46
 <!-- technical-bibtex-start -->
 
47
 * BibTeX:
 
48
 * <pre>
 
49
 * &#64;inproceedings{Mccallum1998,
 
50
 *    author = {Andrew Mccallum and Kamal Nigam},
 
51
 *    booktitle = {AAAI-98 Workshop on 'Learning for Text Categorization'},
 
52
 *    title = {A Comparison of Event Models for Naive Bayes Text Classification},
 
53
 *    year = {1998}
 
54
 * }
 
55
 * </pre>
 
56
 * <p/>
 
57
 <!-- technical-bibtex-end -->
 
58
 *
 
59
 <!-- options-start -->
 
60
 * Valid options are: <p/>
 
61
 * 
 
62
 * <pre> -D
 
63
 *  If set, classifier is run in debug mode and
 
64
 *  may output additional info to the console</pre>
 
65
 * 
 
66
 <!-- options-end -->
 
67
 *
 
68
 * @author Andrew Golightly (acg4@cs.waikato.ac.nz)
 
69
 * @author Bernhard Pfahringer (bernhard@cs.waikato.ac.nz)
 
70
 * @author Jiang Su
 
71
 * @version $Revision: 1.2 $
 
72
 */
 
73
public class NaiveBayesMultinomialUpdateable
 
74
  extends NaiveBayesMultinomial
 
75
  implements UpdateableClassifier {
 
76
 
 
77
  /** for serialization */
 
78
  private static final long serialVersionUID = -7204398796974263186L;
 
79
  
 
80
  /** the word count per class */
 
81
  protected double[] m_wordsPerClass;
 
82
  
 
83
  /**
 
84
   * Returns a string describing this classifier
 
85
   * 
 
86
   * @return            a description of the classifier suitable for
 
87
   *                    displaying in the explorer/experimenter gui
 
88
   */
 
89
  public String globalInfo() {
 
90
    return
 
91
        super.globalInfo() + "\n\n"
 
92
      + "Incremental version of the algorithm.";
 
93
  }
 
94
 
 
95
  /**
 
96
   * Generates the classifier.
 
97
   *
 
98
   * @param instances   set of instances serving as training data
 
99
   * @throws Exception  if the classifier has not been generated successfully
 
100
   */
 
101
  public void buildClassifier(Instances instances) throws Exception {
 
102
    // can classifier handle the data?
 
103
    getCapabilities().testWithFail(instances);
 
104
 
 
105
    // remove instances with missing class
 
106
    instances = new Instances(instances);
 
107
    instances.deleteWithMissingClass();
 
108
 
 
109
    m_headerInfo = new Instances(instances, 0);
 
110
    m_numClasses = instances.numClasses();
 
111
    m_numAttributes = instances.numAttributes();
 
112
    m_probOfWordGivenClass = new double[m_numClasses][];
 
113
    m_wordsPerClass = new double[m_numClasses];
 
114
    m_probOfClass = new double[m_numClasses];
 
115
 
 
116
    // initialising the matrix of word counts
 
117
    // NOTE: Laplace estimator introduced in case a word that does not 
 
118
    // appear for a class in the training set does so for the test set
 
119
    double laplace = 1;
 
120
    for (int c = 0; c < m_numClasses; c++) {
 
121
      m_probOfWordGivenClass[c] = new double[m_numAttributes];
 
122
      m_probOfClass[c]   = laplace;
 
123
      m_wordsPerClass[c] = laplace * m_numAttributes;
 
124
      for(int att = 0; att<m_numAttributes; att++) {
 
125
        m_probOfWordGivenClass[c][att] = laplace;
 
126
      }
 
127
    }
 
128
 
 
129
    for (int i = 0; i < instances.numInstances(); i++)
 
130
      updateClassifier(instances.instance(i));
 
131
  }
 
132
 
 
133
  /**
 
134
   * Updates the classifier with the given instance.
 
135
   *
 
136
   * @param instance    the new training instance to include in the model
 
137
   * @throws Exception  if the instance could not be incorporated in
 
138
   *                    the model.
 
139
   */
 
140
  public void updateClassifier(Instance instance) throws Exception {
 
141
    int classIndex = (int) instance.value(instance.classIndex());
 
142
    m_probOfClass[classIndex] += instance.weight();
 
143
 
 
144
    for (int a = 0; a < instance.numValues(); a++) {
 
145
      if (instance.index(a) == instance.classIndex() ||
 
146
          instance.isMissing(a))
 
147
        continue;
 
148
 
 
149
      double numOccurences = instance.valueSparse(a) * instance.weight();
 
150
      if (numOccurences < 0)
 
151
        throw new Exception(
 
152
            "Numeric attribute values must all be greater or equal to zero.");
 
153
      m_wordsPerClass[classIndex] += numOccurences;
 
154
      m_probOfWordGivenClass[classIndex][instance.index(a)] += numOccurences;
 
155
    }
 
156
  }
 
157
 
 
158
  /**
 
159
   * Calculates the class membership probabilities for the given test
 
160
   * instance.
 
161
   *
 
162
   * @param instance    the instance to be classified
 
163
   * @return            predicted class probability distribution
 
164
   * @throws Exception  if there is a problem generating the prediction
 
165
   */
 
166
  public double[] distributionForInstance(Instance instance) throws Exception {
 
167
    double[] probOfClassGivenDoc = new double[m_numClasses];
 
168
 
 
169
    // calculate the array of log(Pr[D|C])
 
170
    double[] logDocGivenClass = new double[m_numClasses];
 
171
    for (int c = 0; c < m_numClasses; c++) {
 
172
      logDocGivenClass[c] += Math.log(m_probOfClass[c]);
 
173
      int allWords = 0;
 
174
      for (int i = 0; i < instance.numValues(); i++) {
 
175
        if (instance.index(i) == instance.classIndex())
 
176
          continue;
 
177
        double frequencies = instance.valueSparse(i);
 
178
        allWords += frequencies;
 
179
        logDocGivenClass[c] += frequencies *
 
180
        Math.log(m_probOfWordGivenClass[c][instance.index(i)]);
 
181
      }
 
182
      logDocGivenClass[c] -= allWords * Math.log(m_wordsPerClass[c]);
 
183
    }
 
184
 
 
185
    double max = logDocGivenClass[Utils.maxIndex(logDocGivenClass)];
 
186
    for (int i = 0; i < m_numClasses; i++)
 
187
      probOfClassGivenDoc[i] = Math.exp(logDocGivenClass[i] - max);
 
188
 
 
189
    Utils.normalize(probOfClassGivenDoc);
 
190
 
 
191
    return probOfClassGivenDoc;
 
192
  }
 
193
 
 
194
  /**
 
195
   * Returns a string representation of the classifier.
 
196
   *
 
197
   * @return            a string representation of the classifier
 
198
   */
 
199
  public String toString() {
 
200
    StringBuffer result = new StringBuffer();
 
201
 
 
202
    result.append("The independent probability of a class\n");
 
203
    result.append("--------------------------------------\n");
 
204
 
 
205
    for (int c = 0; c < m_numClasses; c++)
 
206
      result.append(m_headerInfo.classAttribute().value(c)).append("\t").
 
207
      append(Double.toString(m_probOfClass[c])).append("\n");
 
208
 
 
209
    result.append("\nThe probability of a word given the class\n");
 
210
    result.append("-----------------------------------------\n\t");
 
211
 
 
212
    for (int c = 0; c < m_numClasses; c++)
 
213
      result.append(m_headerInfo.classAttribute().value(c)).append("\t");
 
214
 
 
215
    result.append("\n");
 
216
 
 
217
    for (int w = 0; w < m_numAttributes; w++) {
 
218
      result.append(m_headerInfo.attribute(w).name()).append("\t");
 
219
      for (int c = 0; c < m_numClasses; c++)
 
220
        result.append(
 
221
            Double.toString(Math.exp(m_probOfWordGivenClass[c][w]))).append("\t");
 
222
      result.append("\n");
 
223
    }
 
224
 
 
225
    return result.toString();
 
226
  }
 
227
 
 
228
  /**
 
229
   * Main method for testing this class.
 
230
   *
 
231
   * @param args        the options
 
232
   */
 
233
  public static void main(String[] args) {
 
234
    runClassifier(new NaiveBayesMultinomialUpdateable(), args);
 
235
  }
 
236
}