weka.classifiers.functions.supportVector.RegOptimizer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of weka-stable Show documentation
Show all versions of weka-stable Show documentation
The Waikato Environment for Knowledge Analysis (WEKA), a machine
learning workbench. This is the stable version. Apart from bugfixes, this version
does not receive any other updates.
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* RegOptimizer.java
* Copyright (C) 2006 University of Waikato, Hamilton, New Zealand
*
*/
package weka.classifiers.functions.supportVector;
import weka.classifiers.functions.SMOreg;
import weka.core.Instance;
import weka.core.Instances;
import weka.core.Option;
import weka.core.OptionHandler;
import weka.core.RevisionHandler;
import weka.core.RevisionUtils;
import weka.core.Utils;
import java.io.Serializable;
import java.util.Enumeration;
import java.util.Random;
import java.util.Vector;
/**
* Base class implementation for learning algorithm of SMOreg
*
* Valid options are:
*
* -L <double>
* The epsilon parameter in epsilon-insensitive loss function.
* (default 1.0e-3)
*
* -W <double>
* The random number seed.
* (default 1)
*
*
* @author Remco Bouckaert ([email protected],[email protected])
* @version $Revision: 6622 $
*/
public class RegOptimizer
implements OptionHandler, Serializable, RevisionHandler {
/** for serialization */
private static final long serialVersionUID = -2198266997254461814L;
/** loss type **/
//protected int m_nLossType = EPSILON;
/** the loss type: L1 */
//public final static int L1 = 1;
/** the loss type: L2 */
//public final static int L2 = 2;
/** the loss type: HUBER */
//public final static int HUBER = 3;
/** the loss type: EPSILON */
//public final static int EPSILON = 4;
/** the loss type */
//public static final Tag[] TAGS_LOSS_TYPE = {
// new Tag(L2, "L2"),
// new Tag(L1, "L1"),
// new Tag(HUBER, "Huber"),
// new Tag(EPSILON, "EPSILON"),
//};
/** alpha and alpha* arrays containing weights for solving dual problem **/
public double[] m_alpha;
public double[] m_alphaStar;
/** offset **/
protected double m_b;
/** epsilon of epsilon-insensitive cost function **/
protected double m_epsilon = 1e-3;
/** capacity parameter, copied from SMOreg **/
protected double m_C = 1.0;
/** class values/desired output vector **/
protected double[] m_target;
/** points to data set **/
protected Instances m_data;
/** the kernel */
protected Kernel m_kernel;
/** index of class variable in data set **/
protected int m_classIndex = -1;
/** number of instances in data set **/
protected int m_nInstances = -1;
/** random number generator **/
protected Random m_random;
/** seed for initializing random number generator **/
protected int m_nSeed = 1;
/** set of support vectors, that is, vectors with alpha(*)!=0 **/
protected SMOset m_supportVectors;
/** number of kernel evaluations, used for printing statistics only **/
protected int m_nEvals = 0;
/** number of kernel cache hits, used for printing statistics only **/
protected int m_nCacheHits = -1;
/** weights for linear kernel **/
protected double[] m_weights;
/** Variables to hold weight vector in sparse form.
(To reduce storage requirements.) */
protected double[] m_sparseWeights;
protected int[] m_sparseIndices;
/** flag to indicate whether the model is built yet **/
protected boolean m_bModelBuilt = false;
/** parent SMOreg class **/
protected SMOreg m_SVM = null;
/**
* the default constructor
*/
public RegOptimizer() {
super();
m_random = new Random(m_nSeed);
}
/**
* Gets an enumeration describing the available options.
*
* @return an enumeration of all the available options.
*/
public Enumeration listOptions() {
Vector result = new Vector();
result.addElement(new Option(
"\tThe epsilon parameter in epsilon-insensitive loss function.\n"
+ "\t(default 1.0e-3)",
"L", 1, "-L "));
// result.addElement(new Option(
// "\tLoss type (L1, L2, Huber, Epsilon insensitive loss)\n",
// "L", 1, "-L [L1|L2|HUBER|EPSILON]"));
result.addElement(new Option(
"\tThe random number seed.\n"
+ "\t(default 1)",
"W", 1, "-W "));
return result.elements();
}
/**
* Parses a given list of options.
*
* Valid options are:
*
* -L <double>
* The epsilon parameter in epsilon-insensitive loss function.
* (default 1.0e-3)
*
* -W <double>
* The random number seed.
* (default 1)
*
*
* @param options the list of options as an array of strings
* @throws Exception if an option is not supported
*/
public void setOptions(String[] options) throws Exception {
String tmpStr;
tmpStr = Utils.getOption('L', options);
if (tmpStr.length() != 0) {
setEpsilonParameter(Double.parseDouble(tmpStr));
} else {
setEpsilonParameter(1.0e-3);
}
/*
tmpStr = Utils.getOption('S', options);
if (tmpStr.length() != 0)
setLossType(new SelectedTag(tmpStr, TAGS_LOSS_TYPE));
else
setLossType(new SelectedTag(EPSILON, TAGS_LOSS_TYPE));
*/
tmpStr = Utils.getOption('W', options);
if (tmpStr.length() != 0) {
setSeed(Integer.parseInt(tmpStr));
} else {
setSeed(1);
}
}
/**
* Gets the current settings of the classifier.
*
* @return an array of strings suitable for passing to setOptions
*/
public String[] getOptions() {
Vector result;
result = new Vector();
result.add("-L");
result.add("" + getEpsilonParameter());
result.add("-W");
result.add("" + getSeed());
//result.add("-S";
//result.add((new SelectedTag(m_nLossType, TAGS_LOSS_TYPE)).getSelectedTag().getReadable();
return (String[]) result.toArray(new String[result.size()]);
}
/**
* flag to indicate whether the model was built yet
*
* @return true if the model was built
*/
public boolean modelBuilt() {
return m_bModelBuilt;
}
/**
* sets the parent SVM
*
* @param value the parent SVM
*/
public void setSMOReg(SMOreg value) {
m_SVM = value;
}
/**
* returns the number of kernel evaluations
*
* @return the number of kernel evaluations
*/
public int getKernelEvaluations() {
return m_nEvals;
}
/**
* return the number of kernel cache hits
*
* @return the number of hits
*/
public int getCacheHits() {
return m_nCacheHits;
}
/**
* initializes the algorithm
*
* @param data the data to work with
* @throws Exception if m_SVM is null
*/
protected void init(Instances data) throws Exception {
if (m_SVM == null) {
throw new Exception ("SVM not initialized in optimizer. Use RegOptimizer.setSVMReg()");
}
m_C = m_SVM.getC();
m_data = data;
m_classIndex = data.classIndex();
m_nInstances = data.numInstances();
// Initialize kernel
m_kernel = Kernel.makeCopy(m_SVM.getKernel());
m_kernel.buildKernel(data);
//init m_target
m_target = new double[m_nInstances];
for (int i = 0; i < m_nInstances; i++) {
m_target[i] = data.instance(i).classValue();
}
m_random = new Random(m_nSeed);
// initialize alpha and alpha* array to all zero
m_alpha = new double[m_target.length];
m_alphaStar = new double[m_target.length];
m_supportVectors = new SMOset(m_nInstances);
m_b = 0.0;
m_nEvals = 0;
m_nCacheHits = -1;
}
/**
* wrap up various variables to save memeory and do some housekeeping after optimization
* has finished.
*
* @throws Exception if something goes wrong
*/
protected void wrapUp() throws Exception {
m_target = null;
m_nEvals = m_kernel.numEvals();
m_nCacheHits = m_kernel.numCacheHits();
if ((m_SVM.getKernel() instanceof PolyKernel) && ((PolyKernel) m_SVM.getKernel()).getExponent() == 1.0) {
// convert alpha's to weights
double [] weights = new double[m_data.numAttributes()];
for (int k = m_supportVectors.getNext(-1); k != -1; k = m_supportVectors.getNext(k)) {
for (int j = 0; j < weights.length; j++) {
if (j != m_classIndex) {
weights[j] += (m_alpha[k] - m_alphaStar[k]) * m_data.instance(k).value(j);
}
}
}
m_weights = weights;
// release memory
m_alpha = null;
m_alphaStar = null;
m_kernel = null;
}
m_bModelBuilt = true;
}
/**
* Compute the value of the objective function.
*
* @return the score
* @throws Exception if something goes wrong
*/
protected double getScore() throws Exception {
double res = 0;
double t = 0, t2 = 0;
double sumAlpha = 0.0;
for (int i = 0; i < m_nInstances; i++) {
sumAlpha += (m_alpha[i] - m_alphaStar[i]);
for (int j = 0; j < m_nInstances; j++) {
t += (m_alpha[i] - m_alphaStar[i]) * (m_alpha[j] - m_alphaStar[j]) * m_kernel.eval(i, j, m_data.instance(i));
}
// switch(m_nLossType) {
// case L1:
// t2 += m_data.instance(i).classValue() * (m_alpha[i] - m_alpha_[i]);
// break;
// case L2:
// t2 += m_data.instance(i).classValue() * (m_alpha[i] - m_alpha_[i]) - (0.5/m_SVM.getC()) * (m_alpha[i]*m_alpha[i] + m_alpha_[i]*m_alpha_[i]);
// break;
// case HUBER:
// t2 += m_data.instance(i).classValue() * (m_alpha[i] - m_alpha_[i]) - (0.5*m_SVM.getEpsilon()/m_SVM.getC()) * (m_alpha[i]*m_alpha[i] + m_alpha_[i]*m_alpha_[i]);
// break;
// case EPSILON:
//t2 += m_data.instance(i).classValue() * (m_alpha[i] - m_alphaStar[i]) - m_epsilon * (m_alpha[i] + m_alphaStar[i]);
t2 += m_target[i] * (m_alpha[i] - m_alphaStar[i]) - m_epsilon * (m_alpha[i] + m_alphaStar[i]);
// break;
// }
}
res += -0.5 * t + t2;
return res;
}
/**
* learn SVM parameters from data.
* Subclasses should implement something more interesting.
*
* @param data the data to work with
* @throws Exception always an Exceoption since subclasses must override it
*/
public void buildClassifier(Instances data) throws Exception {
throw new Exception("Don't call this directly, use subclass instead");
}
/**
* sets the loss type type to use
*
* @param newLossType the loss type to use
*/
//public void setLossType(SelectedTag newLossType) {
// if (newLossType.getTags() == TAGS_LOSS_TYPE) {
// m_nLossType = newLossType.getSelectedTag().getID();
// }
//}
/**
* returns the current loss type
*
* @return the loss type
*/
//public SelectedTag getLossType() {
// return new SelectedTag(m_nLossType, TAGS_LOSS_TYPE);
//}
/**
* SVMOutput of an instance in the training set, m_data
* This uses the cache, unlike SVMOutput(Instance)
*
* @param index index of the training instance in m_data
* @return the SVM output
* @throws Exception if something goes wrong
*/
protected double SVMOutput(int index) throws Exception {
double result = -m_b;
for (int i = m_supportVectors.getNext(-1); i != -1; i = m_supportVectors.getNext(i)) {
result += (m_alpha[i] - m_alphaStar[i]) * m_kernel.eval(index, i, m_data.instance(index));
}
return result;
}
/**
*
* @param inst
* @return
* @throws Exception
*/
public double SVMOutput(Instance inst) throws Exception {
double result = -m_b;
// Is the machine linear?
if (m_weights != null) {
// Is weight vector stored in sparse format?
for (int i = 0; i < inst.numValues(); i++) {
if (inst.index(i) != m_classIndex) {
result += m_weights[inst.index(i)] * inst.valueSparse(i);
}
}
} else {
for (int i = m_supportVectors.getNext(-1); i != -1; i = m_supportVectors.getNext(i)) {
result += (m_alpha[i] - m_alphaStar[i]) * m_kernel.eval(-1, i, inst);
}
}
return result;
}
/**
* Returns the tip text for this property
*
* @return tip text for this property suitable for
* displaying in the explorer/experimenter gui
*/
public String seedTipText() {
return "Seed for random number generator.";
}
/**
* Gets the current seed value for the random number generator
*
* @return the seed value
*/
public int getSeed() {
return m_nSeed;
}
/**
* Sets the seed value for the random number generator
*
* @param value the seed value
*/
public void setSeed(int value) {
m_nSeed = value;
}
/**
* Returns the tip text for this property
*
* @return tip text for this property suitable for
* displaying in the explorer/experimenter gui
*/
public String epsilonParameterTipText() {
return "The epsilon parameter of the epsilon insensitive loss function.(default 0.001).";
}
/**
* Get the value of epsilon parameter of the epsilon insensitive loss function.
*
* @return Value of epsilon parameter.
*/
public double getEpsilonParameter() {
return m_epsilon;
}
/**
* Set the value of epsilon parameter of the epsilon insensitive loss function.
*
* @param v Value to assign to epsilon parameter.
*/
public void setEpsilonParameter(double v) {
m_epsilon = v;
}
/**
* Prints out the classifier.
*
* @return a description of the classifier as a string
*/
public String toString() {
StringBuffer text = new StringBuffer();
text.append("SMOreg\n\n");
if (m_weights != null) {
text.append("weights (not support vectors):\n");
// it's a linear machine
for (int i = 0; i < m_data.numAttributes(); i++) {
if (i != m_classIndex) {
text.append((m_weights[i] >= 0 ? " + " : " - ") + Utils.doubleToString(Math.abs(m_weights[i]), 12, 4) + " * ");
if (m_SVM.getFilterType().getSelectedTag().getID() == SMOreg.FILTER_STANDARDIZE) {
text.append("(standardized) ");
} else if (m_SVM.getFilterType().getSelectedTag().getID() == SMOreg.FILTER_NORMALIZE) {
text.append("(normalized) ");
}
text.append(m_data.attribute(i).name() + "\n");
}
}
} else {
// non linear, print out all supportvectors
text.append("Support vectors:\n");
for (int i = 0; i < m_nInstances; i++) {
if (m_alpha[i] > 0) {
text.append("+" + m_alpha[i] + " * k[" + i + "]\n");
}
if (m_alphaStar[i] > 0) {
text.append("-" + m_alphaStar[i] + " * k[" + i + "]\n");
}
}
}
text.append((m_b<=0?" + ":" - ") + Utils.doubleToString(Math.abs(m_b), 12, 4) + "\n\n");
text.append("\n\nNumber of kernel evaluations: " + m_nEvals);
if (m_nCacheHits >= 0 && m_nEvals > 0) {
double hitRatio = 1 - m_nEvals * 1.0 / (m_nCacheHits + m_nEvals);
text.append(" (" + Utils.doubleToString(hitRatio * 100, 7, 3).trim() + "% cached)");
}
return text.toString();
}
/**
* Returns the revision string.
*
* @return the revision
*/
public String getRevision() {
return RevisionUtils.extract("$Revision: 6622 $");
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy