/*
* File: KernelizableBinaryCategorizerOnlineLearnerTestHarness.java
* Authors: Justin Basilico
* Company: Sandia National Laboratories
* Project: Cognitive Foundry Learning Core
*
* Copyright April 07, 2011, Sandia Corporation.
* Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive
* license for use of this work by or on behalf of the U.S. Government. Export
* of this program may require a license from the United States Government.
*/
package gov.sandia.cognition.learning.algorithm.perceptron;
import gov.sandia.cognition.math.matrix.mtj.Vector2;
import java.util.Random;
import gov.sandia.cognition.math.matrix.Vector;
import gov.sandia.cognition.learning.data.DefaultInputOutputPair;
import gov.sandia.cognition.learning.data.InputOutputPair;
import gov.sandia.cognition.learning.function.categorization.DefaultKernelBinaryCategorizer;
import gov.sandia.cognition.learning.function.categorization.LinearBinaryCategorizer;
import gov.sandia.cognition.learning.function.kernel.LinearKernel;
import gov.sandia.cognition.learning.function.kernel.PolynomialKernel;
import gov.sandia.cognition.learning.algorithm.perceptron.KernelizableBinaryCategorizerOnlineLearner;
import gov.sandia.cognition.learning.algorithm.perceptron.kernel.KernelBinaryCategorizerOnlineLearnerAdapter;
import gov.sandia.cognition.math.RingAccumulator;
import gov.sandia.cognition.math.matrix.VectorFactory;
import gov.sandia.cognition.util.WeightedValue;
import java.util.ArrayList;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Test harness for an online kernelizable binary categorizer.
*
* @author Justin Basilico
* @since 3.3.0
*/
public abstract class KernelizableBinaryCategorizerOnlineLearnerTestHarness
{
protected Random random = new Random(211);
/**
* Creates a new linear instance of the algorithm to test.
*
* @return
* A new linear instance of the algorithm.
*/
protected abstract KernelizableBinaryCategorizerOnlineLearner createLinearInstance();
/**
* Test learning a linearly separable example.
*/
@Test
public void testLearnSeparable()
{
System.out.println("testLearnSeparable");
int d = 10;
int trainCount = 1000;
int testCount = 100;
LinearBinaryCategorizer real = new LinearBinaryCategorizer(
VectorFactory.getDenseDefault().createUniformRandom(d, -1, +1, random), 0.0);
ArrayList<InputOutputPair<Vector, Boolean>> trainData =
new ArrayList<InputOutputPair<Vector, Boolean>>(trainCount);
for (int i = 0; i < trainCount; i++)
{
Vector input = VectorFactory.getDenseDefault().createUniformRandom(
d, -1, +1, random);
boolean output = real.evaluate(input);
trainData.add(DefaultInputOutputPair.create(input, output));
}
ArrayList<InputOutputPair<Vector, Boolean>> testData =
new ArrayList<InputOutputPair<Vector, Boolean>>(testCount);
for (int i = 0; i < testCount; i++)
{
Vector input = VectorFactory.getDenseDefault().createUniformRandom(
d, -1, +1, random);
boolean actual = real.evaluate(input);
testData.add(DefaultInputOutputPair.create(input, actual));
}
KernelizableBinaryCategorizerOnlineLearner instance = this.createLinearInstance();
LinearBinaryCategorizer learned = instance.createInitialLearnedObject();
for (InputOutputPair<Vector, Boolean> example : trainData)
{
this.applyUpdate(instance, learned, example);
}
int correctCount = 0;
for (InputOutputPair<Vector, Boolean> example : testData)
{
boolean actual = example.getOutput();
boolean predicted = learned.evaluate(example.getInput());
if (actual == predicted)
{
correctCount++;
}
}
double accuracy = (double) correctCount / testData.size();
System.out.println("Accuracy: " + accuracy);
assertTrue(accuracy >= 0.95);
double cosine = learned.getWeights().unitVector().cosine(real.getWeights().unitVector());
System.out.println("Cosine: " + cosine);
assertTrue(cosine >= 0.95);
}
/**
* Test learning a linearly separable example.
*/
@Test
public void testKernelLearnSeparable()
{
System.out.println("testKernelLearnSeparable");
int d = 10;
int trainCount = 1000;
int testCount = 100;
LinearBinaryCategorizer real = new LinearBinaryCategorizer(
VectorFactory.getDenseDefault().createUniformRandom(d, -1, +1, random), 0.0);
ArrayList<InputOutputPair<Vector, Boolean>> trainData =
new ArrayList<InputOutputPair<Vector, Boolean>>(trainCount);
for (int i = 0; i < trainCount; i++)
{
Vector input = VectorFactory.getDenseDefault().createUniformRandom(
d, -1, +1, random);
boolean output = real.evaluate(input);
trainData.add(DefaultInputOutputPair.create(input, output));
}
ArrayList<InputOutputPair<Vector, Boolean>> testData =
new ArrayList<InputOutputPair<Vector, Boolean>>(testCount);
for (int i = 0; i < testCount; i++)
{
Vector input = VectorFactory.getDenseDefault().createUniformRandom(
d, -1, +1, random);
boolean actual = real.evaluate(input);
testData.add(DefaultInputOutputPair.create(input, actual));
}
KernelBinaryCategorizerOnlineLearnerAdapter<Vector> instance = new KernelBinaryCategorizerOnlineLearnerAdapter<Vector>(
new LinearKernel(),
this.createLinearInstance());
DefaultKernelBinaryCategorizer<Vector> learned = instance.createInitialLearnedObject();
for (InputOutputPair<Vector, Boolean> example : trainData)
{
this.applyUpdate(instance, learned, example);
}
int correctCount = 0;
for (InputOutputPair<Vector, Boolean> example : testData)
{
boolean actual = example.getOutput();
boolean predicted = learned.evaluate(example.getInput());
if (actual == predicted)
{
correctCount++;
}
}
double accuracy = (double) correctCount / testData.size();
System.out.println("Accuracy: " + accuracy);
assertTrue(accuracy >= 0.95);
RingAccumulator<Vector> accumulator = new RingAccumulator<Vector>();
for (WeightedValue<? extends Vector> example : learned.getExamples())
{
accumulator.accumulate(example.getValue().scale(example.getWeight()));
}
double cosine = accumulator.getSum().unitVector().cosine(real.getWeights().unitVector());
System.out.println("Cosine: " + cosine);
assertTrue(cosine >= 0.95);
}
/**
* Test learning a linearly separable example.
*/
@Test
public void testKernelXOR()
{
System.out.println("testKernelXOR");
int trainCount = 1000;
int testCount = 1000;
ArrayList<InputOutputPair<Vector, Boolean>> trainData =
new ArrayList<InputOutputPair<Vector, Boolean>>(trainCount);
for (int i = 0; i < trainCount; i++)
{
double x = 2.0 * this.random.nextDouble() - 1.0;
double y = 2.0 * this.random.nextDouble() - 1.0;
Vector input = new Vector2(x, y);
boolean output = (x >= 0.0) ^ (y >= 0.0);
trainData.add(DefaultInputOutputPair.create(input, output));
}
ArrayList<InputOutputPair<Vector, Boolean>> testData =
new ArrayList<InputOutputPair<Vector, Boolean>>(testCount);
for (int i = 0; i < testCount; i++)
{
double x = 2.0 * this.random.nextDouble() - 1.0;
double y = 2.0 * this.random.nextDouble() - 1.0;
Vector input = new Vector2(x, y);
boolean output = (x >= 0.0) ^ (y >= 0.0);
testData.add(DefaultInputOutputPair.create(input, output));
}
KernelBinaryCategorizerOnlineLearnerAdapter<Vector> instance = new KernelBinaryCategorizerOnlineLearnerAdapter<Vector>(
new PolynomialKernel(2, 1.0),
this.createLinearInstance());
DefaultKernelBinaryCategorizer<Vector> learned = instance.createInitialLearnedObject();
for (InputOutputPair<Vector, Boolean> example : trainData)
{
this.applyUpdate(instance, learned, example);
}
int correctCount = 0;
for (InputOutputPair<Vector, Boolean> example : testData)
{
boolean actual = example.getOutput();
boolean predicted = learned.evaluate(example.getInput());
if (actual == predicted)
{
correctCount++;
}
}
double accuracy = (double) correctCount / testData.size();
System.out.println("Accuracy: " + accuracy);
assertTrue(accuracy >= 0.90);
}
protected void applyUpdate(
final KernelizableBinaryCategorizerOnlineLearner learner,
final LinearBinaryCategorizer target,
final InputOutputPair<Vector, Boolean> example)
{
learner.update(target, example);
}
protected void applyUpdate(
final KernelBinaryCategorizerOnlineLearnerAdapter<Vector> learner,
final DefaultKernelBinaryCategorizer<Vector> target,
final InputOutputPair<Vector, Boolean> example)
{
learner.update(target, example);
}
}