001: /*
002: * This program is free software; you can redistribute it and/or modify
003: * it under the terms of the GNU General Public License as published by
004: * the Free Software Foundation; either version 2 of the License, or
005: * (at your option) any later version.
006: *
007: * This program is distributed in the hope that it will be useful,
008: * but WITHOUT ANY WARRANTY; without even the implied warranty of
009: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
010: * GNU General Public License for more details.
011: *
012: * You should have received a copy of the GNU General Public License
013: * along with this program; if not, write to the Free Software
014: * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
015: */
016:
017: /*
018: * MILR.java
019: * Copyright (C) 2005 University of Waikato, Hamilton, New Zealand
020: *
021: */
022:
023: package weka.classifiers.mi;
024:
025: import weka.classifiers.Classifier;
026: import weka.core.Capabilities;
027: import weka.core.Instance;
028: import weka.core.Instances;
029: import weka.core.MultiInstanceCapabilitiesHandler;
030: import weka.core.Optimization;
031: import weka.core.Option;
032: import weka.core.OptionHandler;
033: import weka.core.SelectedTag;
034: import weka.core.Tag;
035: import weka.core.Utils;
036: import weka.core.Capabilities.Capability;
037:
038: import java.util.Enumeration;
039: import java.util.Vector;
040:
041: /**
042: <!-- globalinfo-start -->
043: * Uses either standard or collective multi-instance assumption, but within linear regression. For the collective assumption, it offers arithmetic or geometric mean for the posteriors.
044: * <p/>
045: <!-- globalinfo-end -->
046: *
047: <!-- options-start -->
048: * Valid options are: <p/>
049: *
050: * <pre> -D
051: * Turn on debugging output.</pre>
052: *
053: * <pre> -R <ridge>
054: * Set the ridge in the log-likelihood.</pre>
055: *
056: * <pre> -A [0|1|2]
057: * Defines the type of algorithm:
058: * 0. standard MI assumption
059: * 1. collective MI assumption, arithmetic mean for posteriors
060: * 2. collective MI assumption, geometric mean for posteriors</pre>
061: *
062: <!-- options-end -->
063: *
064: * @author Eibe Frank (eibe@cs.waikato.ac.nz)
065: * @author Xin Xu (xx5@cs.waikato.ac.nz)
066: * @version $Revision: 1.3 $
067: */
068: public class MILR extends Classifier implements OptionHandler,
069: MultiInstanceCapabilitiesHandler {
070:
071: /** for serialization */
072: static final long serialVersionUID = 1996101190172373826L;
073:
074: protected double[] m_Par;
075:
076: /** The number of the class labels */
077: protected int m_NumClasses;
078:
079: /** The ridge parameter. */
080: protected double m_Ridge = 1e-6;
081:
082: /** Class labels for each bag */
083: protected int[] m_Classes;
084:
085: /** MI data */
086: protected double[][][] m_Data;
087:
088: /** All attribute names */
089: protected Instances m_Attributes;
090:
091: protected double[] xMean = null, xSD = null;
092:
093: /** the type of processing */
094: protected int m_AlgorithmType = ALGORITHMTYPE_DEFAULT;
095:
096: /** standard MI assumption */
097: public static final int ALGORITHMTYPE_DEFAULT = 0;
098: /** collective MI assumption, arithmetic mean for posteriors */
099: public static final int ALGORITHMTYPE_ARITHMETIC = 1;
100: /** collective MI assumption, geometric mean for posteriors */
101: public static final int ALGORITHMTYPE_GEOMETRIC = 2;
102: /** the types of algorithms */
103: public static final Tag[] TAGS_ALGORITHMTYPE = {
104: new Tag(ALGORITHMTYPE_DEFAULT, "standard MI assumption"),
105: new Tag(ALGORITHMTYPE_ARITHMETIC,
106: "collective MI assumption, arithmetic mean for posteriors"),
107: new Tag(ALGORITHMTYPE_GEOMETRIC,
108: "collective MI assumption, geometric mean for posteriors"), };
109:
110: /**
111: * Returns the tip text for this property
112: *
113: * @return tip text for this property suitable for
114: * displaying in the explorer/experimenter gui
115: */
116: public String globalInfo() {
117: return "Uses either standard or collective multi-instance assumption, but "
118: + "within linear regression. For the collective assumption, it offers "
119: + "arithmetic or geometric mean for the posteriors.";
120: }
121:
122: /**
123: * Returns an enumeration describing the available options
124: *
125: * @return an enumeration of all the available options
126: */
127: public Enumeration listOptions() {
128: Vector result = new Vector();
129:
130: result.addElement(new Option("\tTurn on debugging output.",
131: "D", 0, "-D"));
132:
133: result.addElement(new Option(
134: "\tSet the ridge in the log-likelihood.", "R", 1,
135: "-R <ridge>"));
136:
137: result
138: .addElement(new Option(
139: "\tDefines the type of algorithm:\n"
140: + "\t 0. standard MI assumption\n"
141: + "\t 1. collective MI assumption, arithmetic mean for posteriors\n"
142: + "\t 2. collective MI assumption, geometric mean for posteriors",
143: "A", 1, "-A [0|1|2]"));
144:
145: return result.elements();
146: }
147:
148: /**
149: * Parses a given list of options.
150: *
151: * @param options the list of options as an array of strings
152: * @throws Exception if an option is not supported
153: */
154: public void setOptions(String[] options) throws Exception {
155: String tmpStr;
156:
157: setDebug(Utils.getFlag('D', options));
158:
159: tmpStr = Utils.getOption('R', options);
160: if (tmpStr.length() != 0)
161: setRidge(Double.parseDouble(tmpStr));
162: else
163: setRidge(1.0e-6);
164:
165: tmpStr = Utils.getOption('A', options);
166: if (tmpStr.length() != 0) {
167: setAlgorithmType(new SelectedTag(Integer.parseInt(tmpStr),
168: TAGS_ALGORITHMTYPE));
169: } else {
170: setAlgorithmType(new SelectedTag(ALGORITHMTYPE_DEFAULT,
171: TAGS_ALGORITHMTYPE));
172: }
173: }
174:
175: /**
176: * Gets the current settings of the classifier.
177: *
178: * @return an array of strings suitable for passing to setOptions
179: */
180: public String[] getOptions() {
181: Vector result;
182:
183: result = new Vector();
184:
185: if (getDebug())
186: result.add("-D");
187:
188: result.add("-R");
189: result.add("" + getRidge());
190:
191: result.add("-A");
192: result.add("" + m_AlgorithmType);
193:
194: return (String[]) result.toArray(new String[result.size()]);
195: }
196:
197: /**
198: * Returns the tip text for this property
199: *
200: * @return tip text for this property suitable for
201: * displaying in the explorer/experimenter gui
202: */
203: public String ridgeTipText() {
204: return "The ridge in the log-likelihood.";
205: }
206:
207: /**
208: * Sets the ridge in the log-likelihood.
209: *
210: * @param ridge the ridge
211: */
212: public void setRidge(double ridge) {
213: m_Ridge = ridge;
214: }
215:
216: /**
217: * Gets the ridge in the log-likelihood.
218: *
219: * @return the ridge
220: */
221: public double getRidge() {
222: return m_Ridge;
223: }
224:
225: /**
226: * Returns the tip text for this property
227: *
228: * @return tip text for this property suitable for
229: * displaying in the explorer/experimenter gui
230: */
231: public String algorithmTypeTipText() {
232: return "The mean type for the posteriors.";
233: }
234:
235: /**
236: * Gets the type of algorithm.
237: *
238: * @return the algorithm type
239: */
240: public SelectedTag getAlgorithmType() {
241: return new SelectedTag(m_AlgorithmType, TAGS_ALGORITHMTYPE);
242: }
243:
244: /**
245: * Sets the algorithm type.
246: *
247: * @param newType the new algorithm type
248: */
249: public void setAlgorithmType(SelectedTag newType) {
250: if (newType.getTags() == TAGS_ALGORITHMTYPE) {
251: m_AlgorithmType = newType.getSelectedTag().getID();
252: }
253: }
254:
255: private class OptEng extends Optimization {
256:
257: /** the type to use
258: * @see MILR#TAGS_ALGORITHMTYPE */
259: private int m_Type;
260:
261: /**
262: * initializes the object
263: *
264: * @param type the type top use
265: * @see MILR#TAGS_ALGORITHMTYPE
266: */
267: public OptEng(int type) {
268: super ();
269:
270: m_Type = type;
271: }
272:
273: /**
274: * Evaluate objective function
275: * @param x the current values of variables
276: * @return the value of the objective function
277: */
278: protected double objectiveFunction(double[] x) {
279: double nll = 0; // -LogLikelihood
280:
281: switch (m_Type) {
282: case ALGORITHMTYPE_DEFAULT:
283: for (int i = 0; i < m_Classes.length; i++) { // ith bag
284: int nI = m_Data[i][0].length; // numInstances in ith bag
285: double bag = 0.0, // NLL of each bag
286: prod = 0.0; // Log-prob.
287:
288: for (int j = 0; j < nI; j++) {
289: double exp = 0.0;
290: for (int k = m_Data[i].length - 1; k >= 0; k--)
291: exp += m_Data[i][k][j] * x[k + 1];
292: exp += x[0];
293: exp = Math.exp(exp);
294:
295: if (m_Classes[i] == 1)
296: prod -= Math.log(1.0 + exp);
297: else
298: bag += Math.log(1.0 + exp);
299: }
300:
301: if (m_Classes[i] == 1)
302: bag = -Math.log(1.0 - Math.exp(prod));
303:
304: nll += bag;
305: }
306: break;
307:
308: case ALGORITHMTYPE_ARITHMETIC:
309: for (int i = 0; i < m_Classes.length; i++) { // ith bag
310: int nI = m_Data[i][0].length; // numInstances in ith bag
311: double bag = 0; // NLL of each bag
312:
313: for (int j = 0; j < nI; j++) {
314: double exp = 0.0;
315: for (int k = m_Data[i].length - 1; k >= 0; k--)
316: exp += m_Data[i][k][j] * x[k + 1];
317: exp += x[0];
318: exp = Math.exp(exp);
319:
320: if (m_Classes[i] == 1)
321: bag += 1.0 - 1.0 / (1.0 + exp); // To avoid exp infinite
322: else
323: bag += 1.0 / (1.0 + exp);
324: }
325: bag /= (double) nI;
326:
327: nll -= Math.log(bag);
328: }
329: break;
330:
331: case ALGORITHMTYPE_GEOMETRIC:
332: for (int i = 0; i < m_Classes.length; i++) { // ith bag
333: int nI = m_Data[i][0].length; // numInstances in ith bag
334: double bag = 0; // Log-prob.
335:
336: for (int j = 0; j < nI; j++) {
337: double exp = 0.0;
338: for (int k = m_Data[i].length - 1; k >= 0; k--)
339: exp += m_Data[i][k][j] * x[k + 1];
340: exp += x[0];
341:
342: if (m_Classes[i] == 1)
343: bag -= exp / (double) nI;
344: else
345: bag += exp / (double) nI;
346: }
347:
348: nll += Math.log(1.0 + Math.exp(bag));
349: }
350: break;
351: }
352:
353: // ridge: note that intercepts NOT included
354: for (int r = 1; r < x.length; r++)
355: nll += m_Ridge * x[r] * x[r];
356:
357: return nll;
358: }
359:
360: /**
361: * Evaluate Jacobian vector
362: * @param x the current values of variables
363: * @return the gradient vector
364: */
365: protected double[] evaluateGradient(double[] x) {
366: double[] grad = new double[x.length];
367:
368: switch (m_Type) {
369: case ALGORITHMTYPE_DEFAULT:
370: for (int i = 0; i < m_Classes.length; i++) { // ith bag
371: int nI = m_Data[i][0].length; // numInstances in ith bag
372:
373: double denom = 0.0; // denominator, in log-scale
374: double[] bag = new double[grad.length]; //gradient update with ith bag
375:
376: for (int j = 0; j < nI; j++) {
377: // Compute exp(b0+b1*Xi1j+...)/[1+exp(b0+b1*Xi1j+...)]
378: double exp = 0.0;
379: for (int k = m_Data[i].length - 1; k >= 0; k--)
380: exp += m_Data[i][k][j] * x[k + 1];
381: exp += x[0];
382: exp = Math.exp(exp) / (1.0 + Math.exp(exp));
383:
384: if (m_Classes[i] == 1)
385: // Bug fix: it used to be denom += Math.log(1.0+exp);
386: // Fixed 21 Jan 2005 (Eibe)
387: denom -= Math.log(1.0 - exp);
388:
389: // Instance-wise update of dNLL/dBk
390: for (int p = 0; p < x.length; p++) { // pth variable
391: double m = 1.0;
392: if (p > 0)
393: m = m_Data[i][p - 1][j];
394: bag[p] += m * exp;
395: }
396: }
397:
398: denom = Math.exp(denom);
399:
400: // Bag-wise update of dNLL/dBk
401: for (int q = 0; q < grad.length; q++) {
402: if (m_Classes[i] == 1)
403: grad[q] -= bag[q] / (denom - 1.0);
404: else
405: grad[q] += bag[q];
406: }
407: }
408: break;
409:
410: case ALGORITHMTYPE_ARITHMETIC:
411: for (int i = 0; i < m_Classes.length; i++) { // ith bag
412: int nI = m_Data[i][0].length; // numInstances in ith bag
413:
414: double denom = 0.0;
415: double[] numrt = new double[x.length];
416:
417: for (int j = 0; j < nI; j++) {
418: // Compute exp(b0+b1*Xi1j+...)/[1+exp(b0+b1*Xi1j+...)]
419: double exp = 0.0;
420: for (int k = m_Data[i].length - 1; k >= 0; k--)
421: exp += m_Data[i][k][j] * x[k + 1];
422: exp += x[0];
423: exp = Math.exp(exp);
424: if (m_Classes[i] == 1)
425: denom += exp / (1.0 + exp);
426: else
427: denom += 1.0 / (1.0 + exp);
428:
429: // Instance-wise update of dNLL/dBk
430: for (int p = 0; p < x.length; p++) { // pth variable
431: double m = 1.0;
432: if (p > 0)
433: m = m_Data[i][p - 1][j];
434: numrt[p] += m * exp
435: / ((1.0 + exp) * (1.0 + exp));
436: }
437: }
438:
439: // Bag-wise update of dNLL/dBk
440: for (int q = 0; q < grad.length; q++) {
441: if (m_Classes[i] == 1)
442: grad[q] -= numrt[q] / denom;
443: else
444: grad[q] += numrt[q] / denom;
445: }
446: }
447: break;
448:
449: case ALGORITHMTYPE_GEOMETRIC:
450: for (int i = 0; i < m_Classes.length; i++) { // ith bag
451: int nI = m_Data[i][0].length; // numInstances in ith bag
452: double bag = 0;
453: double[] sumX = new double[x.length];
454: for (int j = 0; j < nI; j++) {
455: // Compute exp(b0+b1*Xi1j+...)/[1+exp(b0+b1*Xi1j+...)]
456: double exp = 0.0;
457: for (int k = m_Data[i].length - 1; k >= 0; k--)
458: exp += m_Data[i][k][j] * x[k + 1];
459: exp += x[0];
460:
461: if (m_Classes[i] == 1) {
462: bag -= exp / (double) nI;
463: for (int q = 0; q < grad.length; q++) {
464: double m = 1.0;
465: if (q > 0)
466: m = m_Data[i][q - 1][j];
467: sumX[q] -= m / (double) nI;
468: }
469: } else {
470: bag += exp / (double) nI;
471: for (int q = 0; q < grad.length; q++) {
472: double m = 1.0;
473: if (q > 0)
474: m = m_Data[i][q - 1][j];
475: sumX[q] += m / (double) nI;
476: }
477: }
478: }
479:
480: for (int p = 0; p < x.length; p++)
481: grad[p] += Math.exp(bag) * sumX[p]
482: / (1.0 + Math.exp(bag));
483: }
484: break;
485: }
486:
487: // ridge: note that intercepts NOT included
488: for (int r = 1; r < x.length; r++) {
489: grad[r] += 2.0 * m_Ridge * x[r];
490: }
491:
492: return grad;
493: }
494: }
495:
496: /**
497: * Returns default capabilities of the classifier.
498: *
499: * @return the capabilities of this classifier
500: */
501: public Capabilities getCapabilities() {
502: Capabilities result = super .getCapabilities();
503:
504: // attributes
505: result.enable(Capability.NOMINAL_ATTRIBUTES);
506: result.enable(Capability.RELATIONAL_ATTRIBUTES);
507: result.enable(Capability.MISSING_VALUES);
508:
509: // class
510: result.enable(Capability.BINARY_CLASS);
511: result.enable(Capability.MISSING_CLASS_VALUES);
512:
513: // other
514: result.enable(Capability.ONLY_MULTIINSTANCE);
515:
516: return result;
517: }
518:
519: /**
520: * Returns the capabilities of this multi-instance classifier for the
521: * relational data.
522: *
523: * @return the capabilities of this object
524: * @see Capabilities
525: */
526: public Capabilities getMultiInstanceCapabilities() {
527: Capabilities result = super .getCapabilities();
528:
529: // attributes
530: result.enable(Capability.NOMINAL_ATTRIBUTES);
531: result.enable(Capability.NUMERIC_ATTRIBUTES);
532: result.enable(Capability.DATE_ATTRIBUTES);
533: result.enable(Capability.MISSING_VALUES);
534:
535: // class
536: result.disableAllClasses();
537: result.enable(Capability.NO_CLASS);
538:
539: return result;
540: }
541:
542: /**
543: * Builds the classifier
544: *
545: * @param train the training data to be used for generating the
546: * boosted classifier.
547: * @throws Exception if the classifier could not be built successfully
548: */
549: public void buildClassifier(Instances train) throws Exception {
550: // can classifier handle the data?
551: getCapabilities().testWithFail(train);
552:
553: // remove instances with missing class
554: train = new Instances(train);
555: train.deleteWithMissingClass();
556:
557: m_NumClasses = train.numClasses();
558:
559: int nR = train.attribute(1).relation().numAttributes();
560: int nC = train.numInstances();
561:
562: m_Data = new double[nC][nR][]; // Data values
563: m_Classes = new int[nC]; // Class values
564: m_Attributes = train.attribute(1).relation();
565:
566: xMean = new double[nR]; // Mean of mean
567: xSD = new double[nR]; // Mode of stddev
568:
569: double sY1 = 0, sY0 = 0, totIns = 0; // Number of classes
570: int[] missingbags = new int[nR];
571:
572: if (m_Debug) {
573: System.out.println("Extracting data...");
574: }
575:
576: for (int h = 0; h < m_Data.length; h++) {
577: Instance current = train.instance(h);
578: m_Classes[h] = (int) current.classValue(); // Class value starts from 0
579: Instances currInsts = current.relationalValue(1);
580: int nI = currInsts.numInstances();
581: totIns += (double) nI;
582:
583: for (int i = 0; i < nR; i++) {
584: // initialize m_data[][][]
585: m_Data[h][i] = new double[nI];
586: double avg = 0, std = 0, num = 0;
587: for (int k = 0; k < nI; k++) {
588: if (!currInsts.instance(k).isMissing(i)) {
589: m_Data[h][i][k] = currInsts.instance(k)
590: .value(i);
591: avg += m_Data[h][i][k];
592: std += m_Data[h][i][k] * m_Data[h][i][k];
593: num++;
594: } else
595: m_Data[h][i][k] = Double.NaN;
596: }
597:
598: if (num > 0) {
599: xMean[i] += avg / num;
600: xSD[i] += std / num;
601: } else
602: missingbags[i]++;
603: }
604:
605: // Class count
606: if (m_Classes[h] == 1)
607: sY1++;
608: else
609: sY0++;
610: }
611:
612: for (int j = 0; j < nR; j++) {
613: xMean[j] = xMean[j] / (double) (nC - missingbags[j]);
614: xSD[j] = Math.sqrt(Math.abs(xSD[j]
615: / ((double) (nC - missingbags[j]) - 1.0) - xMean[j]
616: * xMean[j] * (double) (nC - missingbags[j])
617: / ((double) (nC - missingbags[j]) - 1.0)));
618: }
619:
620: if (m_Debug) {
621: // Output stats about input data
622: System.out.println("Descriptives...");
623: System.out.println(sY0 + " bags have class 0 and " + sY1
624: + " bags have class 1");
625: System.out.println("\n Variable Avg SD ");
626: for (int j = 0; j < nR; j++)
627: System.out.println(Utils.doubleToString(j, 8, 4)
628: + Utils.doubleToString(xMean[j], 10, 4)
629: + Utils.doubleToString(xSD[j], 10, 4));
630: }
631:
632: // Normalise input data and remove ignored attributes
633: for (int i = 0; i < nC; i++) {
634: for (int j = 0; j < nR; j++) {
635: for (int k = 0; k < m_Data[i][j].length; k++) {
636: if (xSD[j] != 0) {
637: if (!Double.isNaN(m_Data[i][j][k]))
638: m_Data[i][j][k] = (m_Data[i][j][k] - xMean[j])
639: / xSD[j];
640: else
641: m_Data[i][j][k] = 0;
642: }
643: }
644: }
645: }
646:
647: if (m_Debug) {
648: System.out.println("\nIteration History...");
649: }
650:
651: double x[] = new double[nR + 1];
652: x[0] = Math.log((sY1 + 1.0) / (sY0 + 1.0));
653: double[][] b = new double[2][x.length];
654: b[0][0] = Double.NaN;
655: b[1][0] = Double.NaN;
656: for (int q = 1; q < x.length; q++) {
657: x[q] = 0.0;
658: b[0][q] = Double.NaN;
659: b[1][q] = Double.NaN;
660: }
661:
662: OptEng opt = new OptEng(m_AlgorithmType);
663: opt.setDebug(m_Debug);
664: m_Par = opt.findArgmin(x, b);
665: while (m_Par == null) {
666: m_Par = opt.getVarbValues();
667: if (m_Debug)
668: System.out
669: .println("200 iterations finished, not enough!");
670: m_Par = opt.findArgmin(m_Par, b);
671: }
672: if (m_Debug)
673: System.out
674: .println(" -------------<Converged>--------------");
675:
676: // feature selection use
677: if (m_AlgorithmType == ALGORITHMTYPE_ARITHMETIC) {
678: double[] fs = new double[nR];
679: for (int k = 1; k < nR + 1; k++)
680: fs[k - 1] = Math.abs(m_Par[k]);
681: int[] idx = Utils.sort(fs);
682: double max = fs[idx[idx.length - 1]];
683: for (int k = idx.length - 1; k >= 0; k--)
684: System.out.println(m_Attributes.attribute(idx[k])
685: .name()
686: + "\t" + (fs[idx[k]] * 100 / max));
687: }
688:
689: // Convert coefficients back to non-normalized attribute units
690: for (int j = 1; j < nR + 1; j++) {
691: if (xSD[j - 1] != 0) {
692: m_Par[j] /= xSD[j - 1];
693: m_Par[0] -= m_Par[j] * xMean[j - 1];
694: }
695: }
696: }
697:
698: /**
699: * Computes the distribution for a given exemplar
700: *
701: * @param exmp the exemplar for which distribution is computed
702: * @return the distribution
703: * @throws Exception if the distribution can't be computed successfully
704: */
705: public double[] distributionForInstance(Instance exmp)
706: throws Exception {
707:
708: // Extract the data
709: Instances ins = exmp.relationalValue(1);
710: int nI = ins.numInstances(), nA = ins.numAttributes();
711: double[][] dat = new double[nI][nA + 1];
712: for (int j = 0; j < nI; j++) {
713: dat[j][0] = 1.0;
714: int idx = 1;
715: for (int k = 0; k < nA; k++) {
716: if (!ins.instance(j).isMissing(k))
717: dat[j][idx] = ins.instance(j).value(k);
718: else
719: dat[j][idx] = xMean[idx - 1];
720: idx++;
721: }
722: }
723:
724: // Compute the probability of the bag
725: double[] distribution = new double[2];
726: switch (m_AlgorithmType) {
727: case ALGORITHMTYPE_DEFAULT:
728: distribution[0] = 0.0; // Log-Prob. for class 0
729:
730: for (int i = 0; i < nI; i++) {
731: double exp = 0.0;
732: for (int r = 0; r < m_Par.length; r++)
733: exp += m_Par[r] * dat[i][r];
734: exp = Math.exp(exp);
735:
736: // Prob. updated for one instance
737: distribution[0] -= Math.log(1.0 + exp);
738: }
739:
740: // Prob. for class 0
741: distribution[0] = Math.exp(distribution[0]);
742: // Prob. for class 1
743: distribution[1] = 1.0 - distribution[0];
744: break;
745:
746: case ALGORITHMTYPE_ARITHMETIC:
747: distribution[0] = 0.0; // Prob. for class 0
748:
749: for (int i = 0; i < nI; i++) {
750: double exp = 0.0;
751: for (int r = 0; r < m_Par.length; r++)
752: exp += m_Par[r] * dat[i][r];
753: exp = Math.exp(exp);
754:
755: // Prob. updated for one instance
756: distribution[0] += 1.0 / (1.0 + exp);
757: }
758:
759: // Prob. for class 0
760: distribution[0] /= (double) nI;
761: // Prob. for class 1
762: distribution[1] = 1.0 - distribution[0];
763: break;
764:
765: case ALGORITHMTYPE_GEOMETRIC:
766: for (int i = 0; i < nI; i++) {
767: double exp = 0.0;
768: for (int r = 0; r < m_Par.length; r++)
769: exp += m_Par[r] * dat[i][r];
770: distribution[1] += exp / (double) nI;
771: }
772:
773: // Prob. for class 1
774: distribution[1] = 1.0 / (1.0 + Math.exp(-distribution[1]));
775: // Prob. for class 0
776: distribution[0] = 1 - distribution[1];
777: break;
778: }
779:
780: return distribution;
781: }
782:
783: /**
784: * Gets a string describing the classifier.
785: *
786: * @return a string describing the classifer built.
787: */
788: public String toString() {
789:
790: String result = "Modified Logistic Regression";
791: if (m_Par == null) {
792: return result + ": No model built yet.";
793: }
794:
795: result += "\nMean type: "
796: + getAlgorithmType().getSelectedTag().getReadable()
797: + "\n";
798: result += "\nCoefficients...\n" + "Variable Coeff.\n";
799: for (int j = 1, idx = 0; j < m_Par.length; j++, idx++) {
800: result += m_Attributes.attribute(idx).name();
801: result += " " + Utils.doubleToString(m_Par[j], 12, 4);
802: result += "\n";
803: }
804:
805: result += "Intercept:";
806: result += " " + Utils.doubleToString(m_Par[0], 10, 4);
807: result += "\n";
808:
809: result += "\nOdds Ratios...\n" + "Variable O.R.\n";
810: for (int j = 1, idx = 0; j < m_Par.length; j++, idx++) {
811: result += " " + m_Attributes.attribute(idx).name();
812: double ORc = Math.exp(m_Par[j]);
813: result += " "
814: + ((ORc > 1e10) ? "" + ORc : Utils.doubleToString(
815: ORc, 12, 4));
816: }
817: result += "\n";
818: return result;
819: }
820:
821: /**
822: * Main method for testing this class.
823: *
824: * @param argv should contain the command line arguments to the
825: * scheme (see Evaluation)
826: */
827: public static void main(String[] argv) {
828: runClassifier(new MILR(), argv);
829: }
830: }
|