/*
* RapidMiner
*
* Copyright (C) 2001-2008 by Rapid-I and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapid-i.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.operator.learner.meta;
import java.util.Iterator;
import java.util.List;
import com.rapidminer.example.Attribute;
import com.rapidminer.example.Example;
import com.rapidminer.example.ExampleSet;
import com.rapidminer.example.table.AttributeFactory;
import com.rapidminer.operator.Model;
import com.rapidminer.operator.OperatorCreationException;
import com.rapidminer.operator.OperatorDescription;
import com.rapidminer.operator.OperatorException;
import com.rapidminer.operator.learner.Learner;
import com.rapidminer.operator.learner.LearnerCapability;
import com.rapidminer.operator.learner.PredictionModel;
import com.rapidminer.operator.learner.lazy.DefaultLearner;
import com.rapidminer.parameter.ParameterType;
import com.rapidminer.parameter.ParameterTypeDouble;
import com.rapidminer.parameter.ParameterTypeInt;
import com.rapidminer.tools.OperatorService;
/**
* <p>This operator uses regression learner as a base learner. The learner starts with a default
* model (mean or mode) as a first prediction model. In each iteration it learns a
* new base model and applies it to the example set. Then, the residuals of the labels are
* calculated and the next base model is learned. The learned meta model predicts the label
* by adding all base model predictions.</p>
*
* @author Ingo Mierswa
* @version $Id: AdditiveRegression.java,v 1.8 2008/05/09 19:22:48 ingomierswa Exp $
*/
public class AdditiveRegression extends AbstractMetaLearner {
/** The parameter name for "The number of iterations." */
public static final String PARAMETER_ITERATIONS = "iterations";
/** The parameter name for "Reducing this learning rate prevent overfitting but increases the learning time." */
public static final String PARAMETER_SHRINKAGE = "shrinkage";
public AdditiveRegression(OperatorDescription description) {
super(description);
}
public Model learn(ExampleSet exampleSet) throws OperatorException {
// create temporary label attribute
ExampleSet workingExampleSet = (ExampleSet)exampleSet.clone();
Attribute originalLabel = workingExampleSet.getAttributes().getLabel();
Attribute workingLabel = AttributeFactory.createAttribute(originalLabel, "working_label");
workingExampleSet.getExampleTable().addAttribute(workingLabel);
workingExampleSet.getAttributes().addRegular(workingLabel);
for (Example example : workingExampleSet) {
example.setValue(workingLabel, example.getValue(originalLabel));
}
workingExampleSet.getAttributes().remove(workingLabel);
workingExampleSet.getAttributes().setLabel(workingLabel);
// apply default model and calculate residuals
Learner defaultLearner = null;
try {
defaultLearner = OperatorService.createOperator(DefaultLearner.class);
} catch (OperatorCreationException e) {
throw new OperatorException(getName() + ": not able to create default classifier!", e);
}
Model defaultModel = defaultLearner.learn(workingExampleSet);
residualReplace(workingExampleSet, defaultModel, false);
// create residual models
Model[] residualModels = new Model[getParameterAsInt(PARAMETER_ITERATIONS)];
for (int iteration = 0; iteration < residualModels.length; iteration++) {
residualModels[iteration] = applyInnerLearner(workingExampleSet);
residualReplace(workingExampleSet, residualModels[iteration], true);
}
// clean up working label
workingExampleSet.getAttributes().remove(workingLabel);
workingExampleSet.getExampleTable().removeAttribute(workingLabel);
// create and return model
return new AdditiveRegressionModel(exampleSet, defaultModel, residualModels, getParameterAsDouble(PARAMETER_SHRINKAGE));
}
/** This methods replaces the labels of the given example set with the label residuals
* after using the given model. Please note that the label column will be overwritten
* and the original label should be stored! */
private void residualReplace(ExampleSet exampleSet, Model model, boolean shrinkage) throws OperatorException {
ExampleSet resultSet = model.apply(exampleSet);
Attribute label = exampleSet.getAttributes().getLabel();
Iterator<Example> originalReader = exampleSet.iterator();
Iterator<Example> predictionReader = resultSet.iterator();
while ((originalReader.hasNext()) && (predictionReader.hasNext())) {
Example originalExample = originalReader.next();
Example predictionExample = predictionReader.next();
double prediction = predictionExample.getPredictedLabel();
if (shrinkage)
prediction *= getParameterAsDouble(PARAMETER_SHRINKAGE);
double residual = originalExample.getLabel() - prediction;
originalExample.setValue(label, residual);
}
PredictionModel.removePredictedLabel(resultSet);
}
public int getMinNumberOfInnerOperators() {
return 1;
}
public int getMaxNumberOfInnerOperators() {
return 1;
}
public boolean supportsCapability(LearnerCapability capability) {
if (capability.equals(LearnerCapability.BINOMINAL_CLASS))
return false;
if (capability.equals(LearnerCapability.POLYNOMINAL_CLASS))
return false;
return super.supportsCapability(capability);
}
public List<ParameterType> getParameterTypes() {
List<ParameterType> types = super.getParameterTypes();
types.add(new ParameterTypeInt(PARAMETER_ITERATIONS, "The number of iterations.", 1, Integer.MAX_VALUE, 10));
types.add(new ParameterTypeDouble(PARAMETER_SHRINKAGE, "Reducing this learning rate prevent overfitting but increases the learning time.", 0.0d, 1.0d, 1.0d));
return types;
}
}