/*
* File: InputOutputTransformedLearner.java
* Authors: Justin Basilico
* Project: Cognitive Foundry
*
* Copyright 2011 Cognitive Foundry. All rights reserved.
*/
package gov.sandia.cognition.learning.algorithm;
import gov.sandia.cognition.evaluator.CompositeEvaluatorTriple;
import gov.sandia.cognition.evaluator.Evaluator;
import gov.sandia.cognition.evaluator.IdentityEvaluator;
import gov.sandia.cognition.evaluator.ReversibleEvaluator;
import gov.sandia.cognition.learning.algorithm.baseline.ConstantLearner;
import gov.sandia.cognition.learning.data.DatasetUtil;
import gov.sandia.cognition.learning.data.DefaultInputOutputPair;
import gov.sandia.cognition.learning.data.DefaultWeightedInputOutputPair;
import gov.sandia.cognition.learning.data.InputOutputPair;
import gov.sandia.cognition.learning.data.WeightedInputOutputPair;
import gov.sandia.cognition.util.ObjectUtil;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* An adapter class for performing supervised learning from data where both
* the input and output have to be transformed before they are passed to the
* learning algorithm. The typical use-case for this class is to make use of
* some supervised learning algorithm on data that does not directly fit its
* input and/or output types. Thus, the data must be transformed before the
* learner is run on the data. It must also be transformed when the resulting
* evaluator is applied to new data. Since both the inputs and outputs need
* to be converted, unsupervised learning algorithms are to be provided to
* learn those transforms from the collection of inputs and outputs,
* respectively. While the input learner just needs to be an evaluator to
* forward-transform the data, the output learner needs to be reversible so
* that the training labels can be reversed from values that the supervised
* learner can be trained on. Thus, the forward evaluation is used during
* training reverse evaluation of the output, while the reverse is used when
* applying it to new data. The result of this learning adapter is the triple
* containing the learned input, supervised, and output evaluators, which can
* be applied to the same data types that the adapter was given in training.
*
* Note that this class can also be used in cases where only one side needs
* to be converted, either by using the static {@code create} methods, or by
* passing in {@code ConstantLearner}s that contain
* {@code IdentityEvaluator}s. Thus, this class can act as a very flexible
* adapter for many types of supervised learning problems.
*
* @param <InputType>
* The input type of the data to learn on. This is passed into the
* unsupervised learner for the input transform to create the evaluator
* that will then produce the {@code TransformedInputType}.
* @param <TransformedInputType>
* The output type of the input transformer, which will be used as the
* input values to train the (middle) supervised learner.
* @param <TransformedOutputType>
* The input type of the output transformer, which will be used as the
* output values to train the (middle) supervised learner. It will be
* the output type of the output transformer (and input type of its
* reverse).
* @param <OutputType>
* The output type of the data to learn on. This is passed into the
* unsupervised learner for the output transform to create the reversible
* data converter for mapping the {@code OutputType} to the
* {@code TransformedOutputType}, or vice-versa.
* @author Justin Basilico
* @since 3.3.3
*/
public class InputOutputTransformedBatchLearner<InputType, TransformedInputType, TransformedOutputType, OutputType>
extends AbstractBatchLearnerContainer<BatchLearner<? super Collection<? extends InputOutputPair<? extends TransformedInputType, TransformedOutputType>>, ? extends Evaluator<? super TransformedInputType, ? extends TransformedOutputType>>>
implements SupervisedBatchLearner<InputType, OutputType, CompositeEvaluatorTriple<InputType, TransformedInputType, TransformedOutputType, OutputType>>
{
/** The unsupervised learning algorithm for creating the input
* transformation. */
protected BatchLearner<? super Collection<? extends InputType>, ? extends Evaluator<? super InputType, ? extends TransformedInputType>> inputLearner;
/** The unsupervised learning algorithm for creating the output
* transformation, which must be reversible for evaluation. */
protected BatchLearner<? super Collection<? extends OutputType>, ? extends ReversibleEvaluator<OutputType, TransformedOutputType, ?>> outputLearner;
/**
* Creates a new, empty {@code InputOutputTransformedBatchLearner}.
*/
public InputOutputTransformedBatchLearner()
{
this(null, null, null);
}
/**
* Creates a new {@code InputOutputTransformedBatchLearner} with the given
* learners.
*
* @param inputLearner
* The unsupervised learning algorithm for creating the input
* transformation.
* @param learner
* The supervised learner whose input and output are being
* adapted.
* @param outputLearner
* The unsupervised learning algorithm for creating the
* output transformation, which must be reversible for evaluation.
*/
public InputOutputTransformedBatchLearner(
final BatchLearner<? super Collection<? extends InputType>, ? extends Evaluator<? super InputType, ? extends TransformedInputType>> inputLearner,
final BatchLearner<? super Collection<? extends InputOutputPair<? extends TransformedInputType, TransformedOutputType>>, ? extends Evaluator<? super TransformedInputType, ? extends TransformedOutputType>> learner,
final BatchLearner<? super Collection<? extends OutputType>, ? extends ReversibleEvaluator<OutputType, TransformedOutputType, ?>> outputLearner)
{
super(learner);
this.setInputLearner(inputLearner);
this.setOutputLearner(outputLearner);
}
@Override
public InputOutputTransformedBatchLearner<InputType, TransformedInputType, TransformedOutputType, OutputType> clone()
{
@SuppressWarnings("unchecked")
final InputOutputTransformedBatchLearner<InputType, TransformedInputType, TransformedOutputType, OutputType> clone =
(InputOutputTransformedBatchLearner<InputType, TransformedInputType, TransformedOutputType, OutputType>)
super.clone();
clone.inputLearner = ObjectUtil.cloneSafe(this.inputLearner);
clone.outputLearner = ObjectUtil.cloneSafe(this.outputLearner);
return clone;
}
/**
* Learn by first calling the input transformation learner on all the
* input values and the output transformation on the output values. After
* these are created, the adapted supervised data is constructed by
* applying the learned input transformation to each input and the learned
* output transformation to each output. The third (middle) learner is then
* trained on the transformed supervised learning problem.
*
* @param data
* The training data.
* @return
* The composite evaluator triple that applies the input transform,
* the learned function, and then the output transform.
*/
@Override
public CompositeEvaluatorTriple<InputType, TransformedInputType, TransformedOutputType, OutputType> learn(
final Collection<? extends InputOutputPair<? extends InputType, OutputType>> data)
{
// First learn the input transformer.
final List<InputType> originalInputs = DatasetUtil.inputsList(data);
final Evaluator<? super InputType, ? extends TransformedInputType> inputTransformer =
this.inputLearner.learn(originalInputs);
// Now learn the output transformer and get its reverse.
final List<OutputType> originalOutputs = DatasetUtil.outputsList(data);
final ReversibleEvaluator<OutputType, TransformedOutputType, ?> outputTransformer =
this.outputLearner.learn(originalOutputs);
final Evaluator<? super TransformedOutputType, ? extends OutputType> outputReverseTransfomer =
outputTransformer.reverse();
// Now transform all the data.
final ArrayList<InputOutputPair<TransformedInputType, TransformedOutputType>> transformedData =
new ArrayList<InputOutputPair<TransformedInputType, TransformedOutputType>>(data.size());
for (InputOutputPair<? extends InputType, OutputType> originalExample : data)
{
final TransformedInputType transformedInput = inputTransformer.evaluate(
originalExample.getInput());
final TransformedOutputType transformedOutput = outputTransformer.evaluate(
originalExample.getOutput());
// Create the transformed input-output pair.
// TODO: We could put in a factory for input-output pairs to special-case this
// type of transform.
if (originalExample instanceof WeightedInputOutputPair<?, ?>)
{
// Handle weighted examples.
transformedData.add(DefaultWeightedInputOutputPair.create(
transformedInput, transformedOutput,
DatasetUtil.getWeight(originalExample)));
}
else
{
// Unweighted data.
transformedData.add(DefaultInputOutputPair.create(
transformedInput, transformedOutput));
}
}
// Finally, learn the real learner.
final Evaluator<? super TransformedInputType, ? extends TransformedOutputType> learned =
this.learner.learn(transformedData);
return CompositeEvaluatorTriple.create(
inputTransformer, learned, outputReverseTransfomer);
}
/**
* Gets the unsupervised learning algorithm for creating the input
* transformation.
*
* @return
* The input transformation learner.
*/
public BatchLearner<? super Collection<? extends InputType>, ? extends Evaluator<? super InputType, ? extends TransformedInputType>> getInputLearner()
{
return this.inputLearner;
}
/**
* Sets the unsupervised learning algorithm for creating the input
* transformation.
*
* @param inputLearner
* The input transformation learner.
*/
public void setInputLearner(
final BatchLearner<? super Collection<? extends InputType>, ? extends Evaluator<? super InputType, ? extends TransformedInputType>> inputLearner)
{
this.inputLearner = inputLearner;
}
/**
* Gets the unsupervised learning algorithm for creating the
* output transformation, which must be reversible for evaluation.
*
* @return
* The output transformation learner.
*/
public BatchLearner<? super Collection<? extends OutputType>, ? extends ReversibleEvaluator<OutputType, TransformedOutputType, ?>> getOutputLearner()
{
return this.outputLearner;
}
/**
* Gets the unsupervised learning algorithm for creating the
* output transformation, which must be reversible for evaluation.
*
* @param outputLearner
* The output transformation learner.
*/
public void setOutputLearner(
final BatchLearner<? super Collection<? extends OutputType>, ? extends ReversibleEvaluator<OutputType, TransformedOutputType, ?>> outputLearner)
{
this.outputLearner = outputLearner;
}
/**
* Creates a new {@code InputOutputTransformedBatchLearner} from the
* three learners.
*
* @param <InputType>
* The input type of the data to learn on. This is passed into the
* unsupervised learner for the input transform to create the evaluator
* that will then produce the {@code TransformedInputType}.
* @param <TransformedInputType>
* The output type of the input transformer, which will be used as the
* input values to train the (middle) supervised learner.
* @param <TransformedOutputType>
* The input type of the output transformer, which will be used as the
* output values to train the (middle) supervised learner. It will be
* the output type of the output transformer.
* @param <OutputType>
* The output type of the data to learn on. This is passed into the
* unsupervised learner for the output transform to create the reversible
* data converter for mapping the {@code OutputType} to the
* {@code TransformedOutputType} to the, or vice-versa.
* @param inputLearner
* The unsupervised learning algorithm for creating the input
* transformation.
* @param learner
* The supervised learner whose input and output are being
* adapted.
* @param outputLearner
* The unsupervised learning algorithm for creating the output
* transformation, which must be reversible for evaluation.
* @return
* A new input-output transformed batch learner.
*/
public static <InputType, TransformedInputType, TransformedOutputType, OutputType> InputOutputTransformedBatchLearner<InputType, TransformedInputType, TransformedOutputType, OutputType> create(
final BatchLearner<? super Collection<? extends InputType>, ? extends Evaluator<? super InputType, ? extends TransformedInputType>> inputLearner,
final BatchLearner<? super Collection<? extends InputOutputPair<? extends TransformedInputType, TransformedOutputType>>, ? extends Evaluator<? super TransformedInputType, ? extends TransformedOutputType>> learner,
final BatchLearner<? super Collection<? extends OutputType>, ? extends ReversibleEvaluator<OutputType, TransformedOutputType, ?>> outputLearner)
{
return new InputOutputTransformedBatchLearner<InputType, TransformedInputType, TransformedOutputType, OutputType>(
inputLearner, learner, outputLearner);
}
/**
* Creates a new {@code InputOutputTransformedBatchLearner} from the
* input and supervised learners, performing no transformation on the
* output type.
*
* @param <InputType>
* The input type of the data to learn on. This is passed into the
* unsupervised learner for the input transform to create the evaluator
* that will then produce the {@code TransformedInputType}.
* @param <TransformedInputType>
* The output type of the input transformer, which will be used as the
* input values to train the (middle) supervised learner.
* @param <OutputType>
* The output type of the data to learn on. It will be used as the
* output values to train the (middle) supervised learner.
* @param inputLearner
* The unsupervised learning algorithm for creating the input
* transformation.
* @param learner
* The supervised learner whose input is being adapted.
* @return
* A new input transformed batch learner.
*/
public static <InputType, TransformedInputType, OutputType> InputOutputTransformedBatchLearner<InputType, TransformedInputType, OutputType, OutputType> createInputTransformed(
final BatchLearner<? super Collection<? extends InputType>, ? extends Evaluator<? super InputType, ? extends TransformedInputType>> inputLearner,
final BatchLearner<? super Collection<? extends InputOutputPair<? extends TransformedInputType, OutputType>>, ? extends Evaluator<? super TransformedInputType, ? extends OutputType>> learner)
{
return create(inputLearner, learner,
new IdentityEvaluator<OutputType>());
}
/**
* Creates a new {@code InputOutputTransformedBatchLearner} from the
* supervised and output learners, performing no transformation on the
* input type.
*
* @param <InputType>
* The input type of the data to learn on. It will be used as the
* input values to train the (middle) supervised learner.
* @param <TransformedOutputType>
* The input type of the output transformer, which will be used as the
* output values to train the (middle) supervised learner. It will be
* the output type of of the output transformer.
* @param <OutputType>
* The output type of the data to learn on. This is passed into the
* unsupervised learner for the output transform to create the reversible
* data converter for mapping the {@code OutputType} to the
* {@code TransformedOutputType} to the, or vice-versa.
* @param learner
* The supervised learner whose output is being adapted.
* @param outputLearner
* The unsupervised learning algorithm for creating the output
* transformation, which must be reversible for evaluation.
* @return
* A new output transformed batch learner.
*/
public static <InputType, TransformedOutputType, OutputType> InputOutputTransformedBatchLearner<InputType, InputType, TransformedOutputType, OutputType> createOutputTransformed(
final BatchLearner<? super Collection<? extends InputOutputPair<? extends InputType, TransformedOutputType>>, ? extends Evaluator<? super InputType, ? extends TransformedOutputType>> learner,
final BatchLearner<? super Collection<? extends OutputType>, ? extends ReversibleEvaluator<OutputType, TransformedOutputType, ?>> outputLearner)
{
return create(new IdentityEvaluator<InputType>(),
learner, outputLearner);
}
/**
* Creates a new {@code InputOutputTransformedBatchLearner} from the
* predefined input and output transforms and the supervised learner.
*
* @param <InputType>
* The input type of the data to learn on. This is passed into the
* input transform to produce the {@code TransformedInputType}.
* @param <TransformedInputType>
* The output type of the input transformer, which will be used as the
* input values to train the (middle) supervised learner.
* @param <TransformedOutputType>
* The input type of the output transformer, which will be used as the
* output values to train the (middle) supervised learner. It will be
* the output type of the output transformer.
* @param <OutputType>
* The output type of the data to learn on. This is passed into the
* reversible output data converter for mapping the {@code OutputType}
* to the {@code TransformedOutputType} to the, or vice-versa.
* @param inputTransform
* The predefined input transformation.
* @param learner
* The supervised learner whose input and output are being
* adapted.
* @param outputTransform
* The predefined output transformation.
* @return
* A new input-output transformed batch learner.
*/
public static <InputType, TransformedInputType, TransformedOutputType, OutputType> InputOutputTransformedBatchLearner<InputType, TransformedInputType, TransformedOutputType, OutputType> create(
final Evaluator<? super InputType, ? extends TransformedInputType> inputTransform,
final BatchLearner<? super Collection<? extends InputOutputPair<? extends TransformedInputType, TransformedOutputType>>, ? extends Evaluator<? super TransformedInputType, ? extends TransformedOutputType>> learner,
final ReversibleEvaluator<OutputType, TransformedOutputType, ?> outputTransform)
{
return create(
ConstantLearner.create(inputTransform), learner,
ConstantLearner.create(outputTransform));
}
/**
* Creates a new {@code InputOutputTransformedBatchLearner} from the
* predefined input transform and the supervised learner. It uses no
* output transformation.
*
* @param <InputType>
* The input type of the data to learn on. This is passed into the
* input transform to produce the {@code TransformedInputType}.
* @param <TransformedInputType>
* The output type of the input transformer, which will be used as the
* input values to train the (middle) supervised learner.
* @param <OutputType>
* The output type of the data to learn on. It will be used as the
* output values to train the (middle) supervised learner.
* @param inputTransform
* The predefined input transformation.
* @param learner
* The supervised learner whose input and output are being
* adapted.
* @return
* A new input transformed batch learner.
*/
public static <InputType, TransformedInputType, OutputType> InputOutputTransformedBatchLearner<InputType, TransformedInputType, OutputType, OutputType> createInputTransformed(
final Evaluator<? super InputType, ? extends TransformedInputType> inputTransform,
final BatchLearner<? super Collection<? extends InputOutputPair<? extends TransformedInputType, OutputType>>, ? extends Evaluator<? super TransformedInputType, ? extends OutputType>> learner)
{
return create(inputTransform, learner,
new IdentityEvaluator<OutputType>());
}
/**
* Creates a new {@code InputOutputTransformedBatchLearner} from the
* predefined output transforms and the supervised learner. It uses no
* input transformation.
*
* @param <InputType>
* The input type of the data to learn on. It will be used as the
* input values to train the (middle) supervised learner.
* @param <TransformedOutputType>
* The input type of the output transformer, which will be used as the
* output values to train the (middle) supervised learner. It will be
* the output type of of the output transformer.
* @param <OutputType>
* The output type of the data to learn on. This is passed into the
* reversible output data converter for mapping the {@code OutputType}
* to the {@code TransformedOutputType} to the, or vice-versa.
* @param learner
* The supervised learner whose input and output are being
* adapted.
* @param outputTransform
* The predefined output transformation.
* @return
* A new output transformed batch learner.
*/
public static <InputType, TransformedOutputType, OutputType> InputOutputTransformedBatchLearner<InputType, InputType, TransformedOutputType, OutputType> createOutputTransformed(
final BatchLearner<? super Collection<? extends InputOutputPair<? extends InputType, TransformedOutputType>>, ? extends Evaluator<? super InputType, ? extends TransformedOutputType>> learner,
final ReversibleEvaluator<OutputType, TransformedOutputType, ?> outputTransform)
{
return create(new IdentityEvaluator<InputType>(),
learner, outputTransform);
}
/**
* Creates a new {@code InputOutputTransformedBatchLearner} from the
* unsupervised input transform learner, supervised learners, and output
* transform.
*
* @param <InputType>
* The input type of the data to learn on. This is passed into the
* unsupervised learner for the input transform to create the evaluator
* that will then produce the {@code TransformedInputType}.
* @param <TransformedInputType>
* The output type of the input transformer, which will be used as the
* input values to train the (middle) supervised learner.
* @param <TransformedOutputType>
* The input type of the output transformer, which will be used as the
* output values to train the (middle) supervised learner. It will be
* the output type of the output transformer.
* @param <OutputType>
* The output type of the data to learn on. This is passed into the
* reversible output data converter for mapping the {@code OutputType}
* to the {@code TransformedOutputType} to the, or vice-versa.
* @param inputLearner
* The unsupervised learning algorithm for creating the input
* transformation.
* @param learner
* The supervised learner whose input and output are being
* adapted.
* @param outputTransform
* The predefined output transformation.
* @return
* A new input-output transformed batch learner.
*/
public static <InputType, TransformedInputType, TransformedOutputType, OutputType> InputOutputTransformedBatchLearner<InputType, TransformedInputType, TransformedOutputType, OutputType> create(
final BatchLearner<? super Collection<? extends InputType>, ? extends Evaluator<? super InputType, ? extends TransformedInputType>> inputLearner,
final BatchLearner<? super Collection<? extends InputOutputPair<? extends TransformedInputType, TransformedOutputType>>, ? extends Evaluator<? super TransformedInputType, ? extends TransformedOutputType>> learner,
final ReversibleEvaluator<OutputType, TransformedOutputType, ?> outputTransform)
{
return create(inputLearner, learner,
ConstantLearner.create(outputTransform));
}
/**
* Creates a new {@code InputOutputTransformedBatchLearner} from the
* input transform, supervised learner, and unsupervised output transform
* learner.
*
* @param <InputType>
* The input type of the data to learn on. This is passed into the
* input transform to produce the {@code TransformedInputType}.
* @param <TransformedInputType>
* The output type of the input transformer, which will be used as the
* input values to train the (middle) supervised learner.
* @param <TransformedOutputType>
* The input type of the output transformer, which will be used as the
* output values to train the (middle) supervised learner. It will be
* the output type of of the output transformer.
* @param <OutputType>
* The output type of the data to learn on. This is passed into the
* unsupervised learner for the output transform to create the reversible
* data converter for mapping the {@code OutputType} to the
* {@code TransformedOutputType} to the, or vice-versa.
* @param inputTransform
* The predefined input transformation.
* @param learner
* The supervised learner whose input and output are being
* adapted.
* @param outputLearner
* The unsupervised learning algorithm for creating the output
* transformation, which must be reversible for evaluation.
* @return
* A new input-output transformed batch learner.
*/
public static <InputType, TransformedInputType, TransformedOutputType, OutputType> InputOutputTransformedBatchLearner<InputType, TransformedInputType, TransformedOutputType, OutputType> create(
final Evaluator<? super InputType, ? extends TransformedInputType> inputTransform,
final BatchLearner<? super Collection<? extends InputOutputPair<? extends TransformedInputType, TransformedOutputType>>, ? extends Evaluator<? super TransformedInputType, ? extends TransformedOutputType>> learner,
final BatchLearner<? super Collection<? extends OutputType>, ? extends ReversibleEvaluator<OutputType, TransformedOutputType, ?>> outputLearner)
{
return create(ConstantLearner.create(inputTransform),
learner, outputLearner);
}
}