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.
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.
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.
19
* Copyright (C) 2005 University of Waikato, Hamilton, New Zealand
23
package weka.classifiers.mi;
25
import weka.classifiers.Classifier;
26
import weka.core.Capabilities;
27
import weka.core.FastVector;
28
import weka.core.Instance;
29
import weka.core.Instances;
30
import weka.core.MultiInstanceCapabilitiesHandler;
31
import weka.core.Optimization;
32
import weka.core.Option;
33
import weka.core.OptionHandler;
34
import weka.core.SelectedTag;
36
import weka.core.TechnicalInformation;
37
import weka.core.TechnicalInformationHandler;
38
import weka.core.Utils;
39
import weka.core.Capabilities.Capability;
40
import weka.core.TechnicalInformation.Field;
41
import weka.core.TechnicalInformation.Type;
42
import weka.filters.Filter;
43
import weka.filters.unsupervised.attribute.Normalize;
44
import weka.filters.unsupervised.attribute.ReplaceMissingValues;
45
import weka.filters.unsupervised.attribute.Standardize;
47
import java.util.Enumeration;
48
import java.util.Vector;
51
<!-- globalinfo-start -->
52
* Re-implement the Diverse Density algorithm, changes the testing procedure.<br/>
54
* Oded Maron (1998). Learning from ambiguity.<br/>
56
* O. Maron, T. Lozano-Perez (1998). A Framework for Multiple Instance Learning. Neural Information Processing Systems. 10.
58
<!-- globalinfo-end -->
60
<!-- technical-bibtex-start -->
63
* @phdthesis{Maron1998,
64
* author = {Oded Maron},
65
* school = {Massachusetts Institute of Technology},
66
* title = {Learning from ambiguity},
70
* @article{Maron1998,
71
* author = {O. Maron and T. Lozano-Perez},
72
* journal = {Neural Information Processing Systems},
73
* title = {A Framework for Multiple Instance Learning},
79
<!-- technical-bibtex-end -->
81
<!-- options-start -->
82
* Valid options are: <p/>
85
* Turn on debugging output.</pre>
87
* <pre> -N <num>
88
* Whether to 0=normalize/1=standardize/2=neither.
89
* (default 1=standardize)</pre>
93
* @author Eibe Frank (eibe@cs.waikato.ac.nz)
94
* @author Xin Xu (xx5@cs.waikato.ac.nz)
95
* @version $Revision: 1.3 $
99
implements OptionHandler, MultiInstanceCapabilitiesHandler,
100
TechnicalInformationHandler {
102
/** for serialization */
103
static final long serialVersionUID = 4263507733600536168L;
105
/** The index of the class attribute */
106
protected int m_ClassIndex;
108
protected double[] m_Par;
110
/** The number of the class labels */
111
protected int m_NumClasses;
113
/** Class labels for each bag */
114
protected int[] m_Classes;
117
protected double[][][] m_Data;
119
/** All attribute names */
120
protected Instances m_Attributes;
122
/** The filter used to standardize/normalize all values. */
123
protected Filter m_Filter = null;
125
/** Whether to normalize/standardize/neither, default:standardize */
126
protected int m_filterType = FILTER_STANDARDIZE;
128
/** Normalize training data */
129
public static final int FILTER_NORMALIZE = 0;
130
/** Standardize training data */
131
public static final int FILTER_STANDARDIZE = 1;
132
/** No normalization/standardization */
133
public static final int FILTER_NONE = 2;
134
/** The filter to apply to the training data */
135
public static final Tag [] TAGS_FILTER = {
136
new Tag(FILTER_NORMALIZE, "Normalize training data"),
137
new Tag(FILTER_STANDARDIZE, "Standardize training data"),
138
new Tag(FILTER_NONE, "No normalization/standardization"),
141
/** The filter used to get rid of missing values. */
142
protected ReplaceMissingValues m_Missing = new ReplaceMissingValues();
145
* Returns a string describing this filter
147
* @return a description of the filter suitable for
148
* displaying in the explorer/experimenter gui
150
public String globalInfo() {
152
"Re-implement the Diverse Density algorithm, changes the testing "
154
+ getTechnicalInformation().toString();
158
* Returns an instance of a TechnicalInformation object, containing
159
* detailed information about the technical background of this class,
160
* e.g., paper reference or book this class is based on.
162
* @return the technical information about this class
164
public TechnicalInformation getTechnicalInformation() {
165
TechnicalInformation result;
166
TechnicalInformation additional;
168
result = new TechnicalInformation(Type.PHDTHESIS);
169
result.setValue(Field.AUTHOR, "Oded Maron");
170
result.setValue(Field.YEAR, "1998");
171
result.setValue(Field.TITLE, "Learning from ambiguity");
172
result.setValue(Field.SCHOOL, "Massachusetts Institute of Technology");
174
additional = result.add(Type.ARTICLE);
175
additional.setValue(Field.AUTHOR, "O. Maron and T. Lozano-Perez");
176
additional.setValue(Field.YEAR, "1998");
177
additional.setValue(Field.TITLE, "A Framework for Multiple Instance Learning");
178
additional.setValue(Field.JOURNAL, "Neural Information Processing Systems");
179
additional.setValue(Field.VOLUME, "10");
185
* Returns an enumeration describing the available options
187
* @return an enumeration of all the available options
189
public Enumeration listOptions() {
190
Vector result = new Vector();
192
result.addElement(new Option(
193
"\tTurn on debugging output.",
196
result.addElement(new Option(
197
"\tWhether to 0=normalize/1=standardize/2=neither.\n"
198
+ "\t(default 1=standardize)",
199
"N", 1, "-N <num>"));
201
return result.elements();
205
* Parses a given list of options. <p/>
207
<!-- options-start -->
208
* Valid options are: <p/>
211
* Turn on debugging output.</pre>
213
* <pre> -N <num>
214
* Whether to 0=normalize/1=standardize/2=neither.
215
* (default 1=standardize)</pre>
219
* @param options the list of options as an array of strings
220
* @throws Exception if an option is not supported
222
public void setOptions(String[] options) throws Exception {
223
setDebug(Utils.getFlag('D', options));
225
String nString = Utils.getOption('N', options);
226
if (nString.length() != 0) {
227
setFilterType(new SelectedTag(Integer.parseInt(nString), TAGS_FILTER));
229
setFilterType(new SelectedTag(FILTER_STANDARDIZE, TAGS_FILTER));
234
* Gets the current settings of the classifier.
236
* @return an array of strings suitable for passing to setOptions
238
public String[] getOptions() {
241
result = new Vector();
247
result.add("" + m_filterType);
249
return (String[]) result.toArray(new String[result.size()]);
253
* Returns the tip text for this property
255
* @return tip text for this property suitable for
256
* displaying in the explorer/experimenter gui
258
public String filterTypeTipText() {
259
return "The filter type for transforming the training data.";
263
* Gets how the training data will be transformed. Will be one of
264
* FILTER_NORMALIZE, FILTER_STANDARDIZE, FILTER_NONE.
266
* @return the filtering mode
268
public SelectedTag getFilterType() {
269
return new SelectedTag(m_filterType, TAGS_FILTER);
273
* Sets how the training data will be transformed. Should be one of
274
* FILTER_NORMALIZE, FILTER_STANDARDIZE, FILTER_NONE.
276
* @param newType the new filtering mode
278
public void setFilterType(SelectedTag newType) {
280
if (newType.getTags() == TAGS_FILTER) {
281
m_filterType = newType.getSelectedTag().getID();
286
extends Optimization {
289
* Evaluate objective function
290
* @param x the current values of variables
291
* @return the value of the objective function
293
protected double objectiveFunction(double[] x){
294
double nll = 0; // -LogLikelihood
295
for(int i=0; i<m_Classes.length; i++){ // ith bag
296
int nI = m_Data[i][0].length; // numInstances in ith bag
297
double bag = 0.0; // NLL of pos bag
299
for(int j=0; j<nI; j++){
301
for(int k=0; k<m_Data[i].length; k++)
302
ins += (m_Data[i][k][j]-x[k*2])*(m_Data[i][k][j]-x[k*2])*
304
ins = Math.exp(-ins);
307
if(m_Classes[i] == 1)
308
bag += Math.log(ins);
310
if(ins<=m_Zero) ins=m_Zero;
311
nll -= Math.log(ins);
315
if(m_Classes[i] == 1){
316
bag = 1.0 - Math.exp(bag);
317
if(bag<=m_Zero) bag=m_Zero;
318
nll -= Math.log(bag);
325
* Evaluate Jacobian vector
326
* @param x the current values of variables
327
* @return the gradient vector
329
protected double[] evaluateGradient(double[] x){
330
double[] grad = new double[x.length];
331
for(int i=0; i<m_Classes.length; i++){ // ith bag
332
int nI = m_Data[i][0].length; // numInstances in ith bag
335
double[] numrt = new double[x.length];
337
for(int j=0; j<nI; j++){
339
for(int k=0; k<m_Data[i].length; k++)
340
exp += (m_Data[i][k][j]-x[k*2])*(m_Data[i][k][j]-x[k*2])
342
exp = Math.exp(-exp);
345
denom += Math.log(exp);
347
if(exp<=m_Zero) exp=m_Zero;
348
// Instance-wise update
349
for(int p=0; p<m_Data[i].length; p++){ // pth variable
350
numrt[2*p] += (1.0-exp)*2.0*(x[2*p]-m_Data[i][p][j])*x[p*2+1]*x[p*2+1]
352
numrt[2*p+1] += 2.0*(1.0-exp)*(x[2*p]-m_Data[i][p][j])*(x[2*p]-m_Data[i][p][j])
358
denom = 1.0-Math.exp(denom);
359
if(denom <= m_Zero) denom = m_Zero;
360
for(int q=0; q<m_Data[i].length; q++){
362
grad[2*q] += numrt[2*q]*(1.0-denom)/denom;
363
grad[2*q+1] += numrt[2*q+1]*(1.0-denom)/denom;
365
grad[2*q] -= numrt[2*q];
366
grad[2*q+1] -= numrt[2*q+1];
376
* Returns default capabilities of the classifier.
378
* @return the capabilities of this classifier
380
public Capabilities getCapabilities() {
381
Capabilities result = super.getCapabilities();
384
result.enable(Capability.NOMINAL_ATTRIBUTES);
385
result.enable(Capability.RELATIONAL_ATTRIBUTES);
386
result.enable(Capability.MISSING_VALUES);
389
result.enable(Capability.BINARY_CLASS);
390
result.enable(Capability.MISSING_CLASS_VALUES);
393
result.enable(Capability.ONLY_MULTIINSTANCE);
399
* Returns the capabilities of this multi-instance classifier for the
402
* @return the capabilities of this object
405
public Capabilities getMultiInstanceCapabilities() {
406
Capabilities result = super.getCapabilities();
409
result.enable(Capability.NOMINAL_ATTRIBUTES);
410
result.enable(Capability.NUMERIC_ATTRIBUTES);
411
result.enable(Capability.DATE_ATTRIBUTES);
412
result.enable(Capability.MISSING_VALUES);
415
result.disableAllClasses();
416
result.enable(Capability.NO_CLASS);
422
* Builds the classifier
424
* @param train the training data to be used for generating the
425
* boosted classifier.
426
* @throws Exception if the classifier could not be built successfully
428
public void buildClassifier(Instances train) throws Exception {
429
// can classifier handle the data?
430
getCapabilities().testWithFail(train);
432
// remove instances with missing class
433
train = new Instances(train);
434
train.deleteWithMissingClass();
436
m_ClassIndex = train.classIndex();
437
m_NumClasses = train.numClasses();
439
int nR = train.attribute(1).relation().numAttributes();
440
int nC = train.numInstances();
441
FastVector maxSzIdx=new FastVector();
443
int [] bagSize=new int [nC];
444
Instances datasets= new Instances(train.attribute(1).relation(),0);
446
m_Data = new double [nC][nR][]; // Data values
447
m_Classes = new int [nC]; // Class values
448
m_Attributes = datasets.stringFreeStructure();
450
System.out.println("Extracting data...");
453
for(int h=0; h<nC; h++) {//h_th bag
454
Instance current = train.instance(h);
455
m_Classes[h] = (int)current.classValue(); // Class value starts from 0
456
Instances currInsts = current.relationalValue(1);
457
for (int i=0; i<currInsts.numInstances();i++){
458
Instance inst=currInsts.instance(i);
462
int nI = currInsts.numInstances();
467
maxSzIdx=new FastVector(1);
468
maxSzIdx.addElement(new Integer(h));
471
maxSzIdx.addElement(new Integer(h));
476
/* filter the training data */
477
if (m_filterType == FILTER_STANDARDIZE)
478
m_Filter = new Standardize();
479
else if (m_filterType == FILTER_NORMALIZE)
480
m_Filter = new Normalize();
484
if (m_Filter!=null) {
485
m_Filter.setInputFormat(datasets);
486
datasets = Filter.useFilter(datasets, m_Filter);
489
m_Missing.setInputFormat(datasets);
490
datasets = Filter.useFilter(datasets, m_Missing);
495
for(int h=0; h<nC; h++) {
496
for (int i = 0; i < datasets.numAttributes(); i++) {
497
// initialize m_data[][][]
498
m_Data[h][i] = new double[bagSize[h]];
500
for (int k=0; k<bagSize[h]; k++){
501
m_Data[h][i][k]=datasets.instance(instIndex).value(i);
510
System.out.println("\nIteration History..." );
513
double[] x = new double[nR*2], tmp = new double[x.length];
514
double[][] b = new double[2][x.length];
517
double nll, bestnll = Double.MAX_VALUE;
518
for (int t=0; t<x.length; t++){
519
b[0][t] = Double.NaN;
520
b[1][t] = Double.NaN;
523
// Largest Positive exemplar
524
for(int s=0; s<maxSzIdx.size(); s++){
525
int exIdx = ((Integer)maxSzIdx.elementAt(s)).intValue();
526
for(int p=0; p<m_Data[exIdx][0].length; p++){
527
for (int q=0; q < nR;q++){
528
x[2*q] = m_Data[exIdx][q][p]; // pick one instance
533
//opt.setDebug(m_Debug);
534
tmp = opt.findArgmin(x, b);
536
tmp = opt.getVarbValues();
538
System.out.println("200 iterations finished, not enough!");
539
tmp = opt.findArgmin(tmp, b);
541
nll = opt.getMinFunction();
546
tmp = new double[x.length]; // Save memory
548
System.out.println("!!!!!!!!!!!!!!!!Smaller NLL found: "+nll);
551
System.out.println(exIdx+": -------------<Converged>--------------");
557
* Computes the distribution for a given exemplar
559
* @param exmp the exemplar for which distribution is computed
560
* @return the distribution
561
* @throws Exception if the distribution can't be computed successfully
563
public double[] distributionForInstance(Instance exmp)
567
Instances ins = exmp.relationalValue(1);
569
ins = Filter.useFilter(ins, m_Filter);
571
ins = Filter.useFilter(ins, m_Missing);
573
int nI = ins.numInstances(), nA = ins.numAttributes();
574
double[][] dat = new double [nI][nA];
575
for(int j=0; j<nI; j++){
576
for(int k=0; k<nA; k++){
577
dat[j][k] = ins.instance(j).value(k);
581
// Compute the probability of the bag
582
double [] distribution = new double[2];
583
distribution[0]=0.0; // log-Prob. for class 0
585
for(int i=0; i<nI; i++){
587
for(int r=0; r<nA; r++)
588
exp += (m_Par[r*2]-dat[i][r])*(m_Par[r*2]-dat[i][r])*
589
m_Par[r*2+1]*m_Par[r*2+1];
590
exp = Math.exp(-exp);
592
// Prob. updated for one instance
593
distribution[0] += Math.log(1.0-exp);
596
distribution[0] = Math.exp(distribution[0]);
597
distribution[1] = 1.0-distribution[0];
603
* Gets a string describing the classifier.
605
* @return a string describing the classifer built.
607
public String toString() {
609
//double CSq = m_LLn - m_LL;
610
//int df = m_NumPredictors;
611
String result = "Diverse Density";
613
return result + ": No model built yet.";
616
result += "\nCoefficients...\n"
617
+ "Variable Point Scale\n";
618
for (int j = 0, idx=0; j < m_Par.length/2; j++, idx++) {
619
result += m_Attributes.attribute(idx).name();
620
result += " "+Utils.doubleToString(m_Par[j*2], 12, 4);
621
result += " "+Utils.doubleToString(m_Par[j*2+1], 12, 4)+"\n";
628
* Main method for testing this class.
630
* @param argv should contain the command line arguments to the
631
* scheme (see Evaluation)
633
public static void main(String[] argv) {
634
runClassifier(new MIDD(), argv);