package LBJ2.learn;
import java.io.PrintStream;
import LBJ2.util.ExceptionlessInputStream;
import LBJ2.util.ExceptionlessOutputStream;
/**
* Simple sparse Winnow implementation. It is assumed that
* {@link Learner#labeler} is a single discrete classifier whose returned
* feature values are available through the
* {@link LBJ2.classify.Classifier#allowableValues()} method. The second
* value returned from {@link LBJ2.classify.Classifier#allowableValues()} is
* treated as "positive", and it is assumed there are exactly 2 allowable
* values. Assertions will produce error messages if these assumptions do
* not hold.
*
* <p> This algorithm's user-configurable parameters are stored in member
* fields of this class. They may be set via either a constructor that names
* each parameter explicitly or a constructor that takes an instance of
* {@link LBJ2.learn.SparseWinnow.Parameters Parameters} as input. The
* documentation in each member field in this class indicates the default
* value of the associated parameter when using the former type of
* constructor. The documentation of the associated member field in the
* {@link LBJ2.learn.SparseWinnow.Parameters Parameters} class indicates the
* default value of the parameter when using the latter type of constructor.
*
* @author Nick Rizzolo
**/
public class SparseWinnow extends LinearThresholdUnit
{
/** Default for {@link #learningRate}. */
public static final double defaultLearningRate = 2;
/** Default for {@link LinearThresholdUnit#threshold}. */
public static final double defaultThreshold = 16;
/** Default for {@link LinearThresholdUnit#initialWeight}. */
public static final double defaultInitialWeight = 1;
/**
* The rate at which weights are demoted; default equal to <code>1 /</code>
* {@link #learningRate}.
**/
protected double beta;
/**
* {@link #learningRate}, {@link #beta}, and
* {@link LinearThresholdUnit#threshold} take default values, while the
* name of the classifier gets the empty string.
**/
public SparseWinnow() { this(""); }
/**
* Sets {@link #learningRate} to the specified value, {@link #beta} to 1 /
* {@link #learningRate}, and the {@link LinearThresholdUnit#threshold}
* takes the default, while the name of the classifier gets the empty
* string.
*
* @param a The desired value of the promotion parameter.
**/
public SparseWinnow(double a) { this("", a); }
/**
* Sets {@link #learningRate} and {@link #beta} to the specified values,
* and the {@link LinearThresholdUnit#threshold} takes the default, while
* the name of the classifier gets the empty string.
*
* @param a The desired value of the promotion parameter.
* @param b The desired value of the demotion parameter.
**/
public SparseWinnow(double a, double b) { this("", a, b); }
/**
* Sets {@link #learningRate}, {@link #beta}, and
* {@link LinearThresholdUnit#threshold} to the specified values, while the
* name of the classifier gets the empty string.
*
* @param a The desired value of the promotion parameter.
* @param b The desired value of the demotion parameter.
* @param t The desired threshold value.
**/
public SparseWinnow(double a, double b, double t) {
this("", a, b, t);
}
/**
* Use this constructor to fit a thick separator, where both the positive
* and negative sides of the hyperplane will be given the specified
* thickness, while the name of the classifier gets the empty string.
*
* @param a The desired value of the promotion parameter.
* @param b The desired value of the demotion parameter.
* @param t The desired threshold value.
* @param pt The desired positive thickness.
**/
public SparseWinnow(double a, double b, double t, double pt) {
this("", a, b, t, pt);
}
/**
* Use this constructor to fit a thick separator, where the positive and
* negative sides of the hyperplane will be given the specified separate
* thicknesses, while the name of the classifier gets the empty string.
*
* @param a The desired value of the promotion parameter.
* @param b The desired value of the demotion parameter.
* @param t The desired threshold value.
* @param pt The desired positive thickness.
* @param nt The desired negative thickness.
**/
public SparseWinnow(double a, double b, double t, double pt, double nt) {
this("", a, b, t, pt, nt);
}
/**
* Use this constructor to specify an alternative subclass of
* {@link SparseWeightVector}, while the name of the classifier gets the
* empty string.
*
* @param a The desired value of the promotion parameter.
* @param b The desired value of the demotion parameter.
* @param t The desired threshold value.
* @param pt The desired positive thickness.
* @param nt The desired negative thickness.
* @param v An empty sparse weight vector.
**/
public SparseWinnow(double a, double b, double t, double pt, double nt,
SparseWeightVector v) {
this("", a, b, t, pt, nt, v);
}
/**
* Initializing constructor. Sets all member variables to their associated
* settings in the {@link SparseWinnow.Parameters} object.
*
* @param p The settings of all parameters.
**/
public SparseWinnow(Parameters p) { this("", p); }
/**
* {@link #learningRate}, {@link #beta}, and
* {@link LinearThresholdUnit#threshold} take default values.
*
* @param n The name of the classifier.
**/
public SparseWinnow(String n) { this(n, defaultLearningRate); }
/**
* Sets {@link #learningRate} to the specified value, {@link #beta} to 1 /
* {@link #learningRate}, and the {@link LinearThresholdUnit#threshold}
* takes the default.
*
* @param n The name of the classifier.
* @param a The desired value of the promotion parameter.
**/
public SparseWinnow(String n, double a) { this(n, a, 1 / a); }
/**
* Sets {@link #learningRate} and {@link #beta} to the specified values,
* and the {@link LinearThresholdUnit#threshold} takes the default.
*
* @param n The name of the classifier.
* @param a The desired value of the promotion parameter.
* @param b The desired value of the demotion parameter.
**/
public SparseWinnow(String n, double a, double b) {
this(n, a, b, defaultThreshold);
}
/**
* Sets {@link #learningRate}, {@link #beta}, and
* {@link LinearThresholdUnit#threshold} to the specified values.
*
* @param n The name of the classifier.
* @param a The desired value of the promotion parameter.
* @param b The desired value of the demotion parameter.
* @param t The desired threshold value.
**/
public SparseWinnow(String n, double a, double b, double t) {
this(n, a, b, t, LinearThresholdUnit.defaultThickness);
}
/**
* Use this constructor to fit a thick separator, where both the positive
* and negative sides of the hyperplane will be given the specified
* thickness.
*
* @param n The name of the classifier.
* @param a The desired value of the promotion parameter.
* @param b The desired value of the demotion parameter.
* @param t The desired threshold value.
* @param pt The desired positive thickness.
**/
public SparseWinnow(String n, double a, double b, double t, double pt) {
this(n, a, b, t, pt, pt);
}
/**
* Use this constructor to fit a thick separator, where the positive and
* negative sides of the hyperplane will be given the specified separate
* thicknesses.
*
* @param n The name of the classifier.
* @param a The desired value of the promotion parameter.
* @param b The desired value of the demotion parameter.
* @param t The desired threshold value.
* @param pt The desired positive thickness.
* @param nt The desired negative thickness.
**/
public SparseWinnow(String n, double a, double b, double t, double pt,
double nt) {
this(n, a, b, t, pt, nt,
(SparseWeightVector)
LinearThresholdUnit.defaultWeightVector.clone());
}
/**
* Use this constructor to specify an alternative subclass of
* {@link SparseWeightVector}.
*
* @param n The name of the classifier.
* @param a The desired value of the promotion parameter.
* @param b The desired value of the demotion parameter.
* @param t The desired threshold value.
* @param pt The desired positive thickness.
* @param nt The desired negative thickness.
* @param v An empty sparse weight vector.
**/
public SparseWinnow(String n, double a, double b, double t, double pt,
double nt, SparseWeightVector v) {
super(n);
Parameters p = new Parameters();
p.learningRate = a;
p.beta = b;
p.threshold = t;
p.positiveThickness = pt;
p.negativeThickness = nt;
p.weightVector = v;
setParameters(p);
}
/**
* Initializing constructor. Sets all member variables to their associated
* settings in the {@link SparseWinnow.Parameters} object.
*
* @param n The name of the classifier.
* @param p The settings of all parameters.
**/
public SparseWinnow(String n, Parameters p) {
super(n);
setParameters(p);
}
/**
* Sets the values of parameters that control the behavior of this learning
* algorithm.
*
* @param p The parameters.
**/
public void setParameters(Parameters p) {
super.setParameters(p);
beta = p.beta;
}
/**
* Retrieves the parameters that are set in this learner.
*
* @return An object containing all the values of the parameters that
* control the behavior of this learning algorithm.
**/
public Learner.Parameters getParameters() {
Parameters p =
new Parameters((LinearThresholdUnit.Parameters) super.getParameters());
p.beta = beta;
return p;
}
/**
* Returns the current value of the {@link #learningRate} variable.
*
* @return The value of the {@link #learningRate} variable.
**/
public double getLearningRate() { return learningRate; }
/**
* Sets the {@link #learningRate} member variable to the specified value.
*
* @param t The new value for {@link #learningRate}.
**/
public void setLearningRate(double t) { learningRate = t; }
/**
* Returns the current value of the {@link #beta} variable.
*
* @return The value of the {@link #beta} variable.
**/
public double getBeta() { return beta; }
/**
* Sets the {@link #beta} member variable to the specified value.
*
* @param t The new value for {@link #beta}.
**/
public void setBeta(double t) { beta = t; }
/**
* Returns the learning rate, which is {@link #learningRate} (alpha) if it
* is a positive example, and {@link #beta} if it is a negative example.
*
* @param exampleFeatures The example's array of feature indices.
* @param exampleValues The example's array of feature values.
* @param s The score.
* @param label The example label.
* @return The appropriate learning rate.
**/
public double computeLearningRate(int[] exampleFeatures,
double[] exampleValues, double s,
boolean label) {
if (label) return learningRate;
else return beta;
}
/**
* Promotion is simply <code>w_i *= learningRate<sup>x_i</sup></code>.
*
* @param exampleFeatures The example's array of feature indices.
* @param exampleValues The example's array of feature values.
* @param rate The learning rate at which the weights are
* updated.
**/
public void promote(int[] exampleFeatures, double[] exampleValues,
double rate) {
update(exampleFeatures, exampleValues, rate);
}
/**
* Demotion is simply <code>w_i *= beta<sup>x_i</sup></code>.
*
* @param exampleFeatures The example's array of feature indices.
* @param exampleValues The example's array of feature values.
* @param rate The learning rate at which the weights are
* updated.
**/
public void demote(int[] exampleFeatures, double[] exampleValues,
double rate) {
update(exampleFeatures, exampleValues, rate);
}
/**
* This method performs an update <code>w_i *= base<sup>x_i</sup></code>,
* initalizing weights in the weight vector as needed.
*
* @param exampleFeatures The example's array of feature indices.
* @param exampleValues The example's array of values.
* @param base As described above.
**/
public void update(int[] exampleFeatures, double[] exampleValues,
double base) {
weightVector.scaledMultiply(exampleFeatures, exampleValues, base,
initialWeight);
bias *= base;
}
/**
* Writes the algorithm's internal representation as text. In the first
* line of output, the name of the classifier is printed, followed by
* {@link #learningRate}, {@link #beta},
* {@link LinearThresholdUnit#initialWeight},
* {@link LinearThresholdUnit#threshold},
* {@link LinearThresholdUnit#positiveThickness},
* {@link LinearThresholdUnit#negativeThickness}, and finally
* {@link LinearThresholdUnit#bias}.
*
* @param out The output stream.
**/
public void write(PrintStream out) {
out.println(name + ": " + learningRate + ", " + beta + ", "
+ initialWeight + ", " + threshold + ", " + positiveThickness
+ ", " + negativeThickness + ", " + bias);
if (lexicon.size() == 0) weightVector.write(out);
else weightVector.write(out, lexicon);
}
/**
* Writes the learned function's internal representation in binary form.
*
* @param out The output stream.
**/
public void write(ExceptionlessOutputStream out) {
super.write(out);
out.writeDouble(beta);
}
/**
* Reads the binary representation of a learner with this object's run-time
* type, overwriting any and all learned or manually specified parameters
* as well as the label lexicon but without modifying the feature lexicon.
*
* @param in The input stream.
**/
public void read(ExceptionlessInputStream in) {
super.read(in);
beta = in.readDouble();
}
/**
* Simply a container for all of {@link SparseWinnow}'s configurable
* parameters. Using instances of this class should make code more
* readable and constructors less complicated.
*
* @author Nick Rizzolo
**/
public static class Parameters extends LinearThresholdUnit.Parameters
{
/**
* The rate at which weights are demoted; default equal to <code>1
* /</code> {@link #learningRate}.
**/
public double beta;
/** Sets all the default values. */
public Parameters() {
learningRate = defaultLearningRate;
beta = 1 / defaultLearningRate;
threshold = defaultThreshold;
initialWeight = defaultInitialWeight;
}
/**
* Sets the parameters from the parent's parameters object, giving
* defaults to all parameters declared in this object.
**/
public Parameters(LinearThresholdUnit.Parameters p) {
super(p);
beta = 1 / learningRate;
}
/** Copy constructor. */
public Parameters(Parameters p) {
super(p);
beta = p.beta;
}
/**
* Calls the appropriate <code>Learner.setParameters(Parameters)</code>
* method for this <code>Parameters</code> object.
*
* @param l The learner whose parameters will be set.
**/
public void setParameters(Learner l) {
((SparseWinnow) l).setParameters(this);
}
/**
* Creates a string representation of these parameters in which only
* those parameters that differ from their default values are mentioned.
**/
public String nonDefaultString() {
String result = super.nonDefaultString();
if (beta != 1 / LinearThresholdUnit.defaultLearningRate)
result += ", beta = " + beta;
if (result.startsWith(", ")) result = result.substring(2);
return result;
}
}
}