/*
* 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.features.transformation;
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.operator.IOObject;
import com.rapidminer.operator.Operator;
import com.rapidminer.operator.OperatorDescription;
import com.rapidminer.operator.OperatorException;
import com.rapidminer.operator.UserError;
import com.rapidminer.operator.preprocessing.PreprocessingOperator;
import com.rapidminer.parameter.ParameterType;
import com.rapidminer.parameter.ParameterTypeBoolean;
import com.rapidminer.parameter.ParameterTypeDouble;
import com.rapidminer.parameter.ParameterTypeInt;
import com.rapidminer.tools.math.som.KohonenNet;
import com.rapidminer.tools.math.som.RandomDataContainer;
import com.rapidminer.tools.math.som.RitterAdaptation;
/**
* This operator performs a dimensionality reduction based on a SOM (Self Organizing Map, aka Kohonen net).
*
* @author Sebastian Land, Ingo Mierswa
* @version $Id: SOMDimensionalityReduction.java,v 1.8 2008/07/07 07:06:44 ingomierswa Exp $
*/
public class SOMDimensionalityReduction extends Operator {
/** The parameter name for "Defines the number of dimensions, the data shall be reduced." */
public static final String PARAMETER_NUMBER_OF_DIMENSIONS = "number_of_dimensions";
/** The parameter name for "Defines the size of the SOM net, by setting the length of every edge of the net." */
public static final String PARAMETER_NET_SIZE = "net_size";
/** The parameter name for "Defines the number of trainnig rounds" */
public static final String PARAMETER_TRAINING_ROUNDS = "training_rounds";
/** The parameter name for "Defines the strength of an adaption in the first round. The strength will decrease every round until it reaches the learning_rate_end in the last round." */
public static final String PARAMETER_LEARNING_RATE_START = "learning_rate_start";
/** The parameter name for "Defines the strength of an adaption in the last round. The strength will decrease to this value in last round, beginning with learning_rate_start in the first round." */
public static final String PARAMETER_LEARNING_RATE_END = "learning_rate_end";
/** The parameter name for "Defines the radius of the sphere around an stimulus, within an adaption occoures. This radius decreases every round, starting by adaption_radius_start in first round, to adaption_radius_end in last round." */
public static final String PARAMETER_ADAPTION_RADIUS_START = "adaption_radius_start";
/** The parameter name for "Defines the radius of the sphere around an stimulus, within an adaption occoures. This radius decreases every round, starting by adaption_radius_start in first round, to adaption_radius_end in last round." */
public static final String PARAMETER_ADAPTION_RADIUS_END = "adaption_radius_end";
public SOMDimensionalityReduction(OperatorDescription description) {
super(description);
}
public Class<?>[] getInputClasses() {
return new Class[] { ExampleSet.class };
}
public Class<?>[] getOutputClasses() {
return new Class[] { ExampleSet.class };
}
public IOObject[] apply() throws OperatorException {
ExampleSet exampleSet = getInput(ExampleSet.class);
// Get and check parameter values
int trainingRounds = getParameterAsInt(PARAMETER_TRAINING_ROUNDS);
int netSize = getParameterAsInt(PARAMETER_NET_SIZE);
int dimensions = getParameterAsInt(PARAMETER_NUMBER_OF_DIMENSIONS);
double learningRateStart = getParameterAsDouble(PARAMETER_LEARNING_RATE_START);
double learningRateEnd = getParameterAsDouble(PARAMETER_LEARNING_RATE_END);
double adaptionRadiusStart = getParameterAsDouble(PARAMETER_ADAPTION_RADIUS_START);
double adaptionRadiusEnd = getParameterAsDouble(PARAMETER_ADAPTION_RADIUS_END);
// checking sanity of parameter values
if (learningRateStart < learningRateEnd) {
throw new UserError(this, 116, "learning_rate_start", "Learning rate at first round must be greater than in last round: (" + learningRateEnd + ")");
}
if (adaptionRadiusStart < adaptionRadiusEnd) {
throw new UserError(this, 116, "adaption_radius_start", "Adaption radius at first round must be greater than in last round: (" + adaptionRadiusEnd + ")");
}
// train SOM
KohonenNet net = prepareSOM(exampleSet, dimensions, netSize, trainingRounds, learningRateStart, learningRateEnd, adaptionRadiusStart, adaptionRadiusEnd);
SOMDimensionalityReductionModel model = new SOMDimensionalityReductionModel(exampleSet, net, dimensions);
ExampleSet newExampleSet = model.apply(exampleSet);
if (getParameterAsBoolean(PreprocessingOperator.PARAMETER_RETURN_PREPROCESSING_MODEL)) {
return new IOObject[] { newExampleSet, model };
} else {
return new IOObject[] { newExampleSet };
}
}
private KohonenNet prepareSOM(ExampleSet exampleSet, int netDimensions, int netSize, int trainingRounds, double learningRateStart, double learningRateEnd, double adaptionRadiusStart, double adaptionRadiusEnd) {
// generating data for SOM
int dataDimension;
RandomDataContainer data = new RandomDataContainer();
synchronized (exampleSet) {
Iterator<Example> iterator = exampleSet.iterator();
dataDimension = exampleSet.getAttributes().size();
while (iterator.hasNext()) {
data.addData(getDoubleArrayFromExample(iterator.next()));
}
}
// generating SOM
KohonenNet net = new KohonenNet(data);
RitterAdaptation adaptionFunction = new RitterAdaptation();
adaptionFunction.setAdaptationRadiusStart(adaptionRadiusStart);
adaptionFunction.setAdaptationRadiusEnd(adaptionRadiusEnd);
adaptionFunction.setLearnRateStart(learningRateStart);
adaptionFunction.setLearnRateEnd(learningRateEnd);
net.setAdaptationFunction(adaptionFunction);
int[] dimensions = new int[netDimensions];
for (int i = 0; i < netDimensions; i++)
dimensions[i] = netSize;
net.init(dataDimension, dimensions, false);
// train SOM
net.setTrainingRounds(trainingRounds);
net.train();
return net;
}
public static double[] getDoubleArrayFromExample(Example example) {
double[] doubleRow = new double[example.getAttributes().size()];
int i = 0;
for (Attribute attribute : example.getAttributes()) {
doubleRow[i++] = example.getValue(attribute);
}
return doubleRow;
}
public List<ParameterType> getParameterTypes() {
List<ParameterType> types = super.getParameterTypes();
types.add(new ParameterTypeBoolean(PreprocessingOperator.PARAMETER_RETURN_PREPROCESSING_MODEL, "Indicates if the preprocessing model should also be returned", false));
ParameterType type = new ParameterTypeInt(PARAMETER_NUMBER_OF_DIMENSIONS, "Defines the number of dimensions, the data shall be reduced.", 1, Integer.MAX_VALUE, 2);
type.setExpert(false);
types.add(type);
type = new ParameterTypeInt(PARAMETER_NET_SIZE, "Defines the size of the SOM net, by setting the length of every edge of the net.", 1, Integer.MAX_VALUE, 30);
type.setExpert(false);
types.add(type);
type = new ParameterTypeInt(PARAMETER_TRAINING_ROUNDS, "Defines the number of trainnig rounds", 1, Integer.MAX_VALUE, 30);
type.setExpert(false);
types.add(type);
types.add(new ParameterTypeDouble(PARAMETER_LEARNING_RATE_START, "Defines the strength of an adaption in the first round. The strength will decrease every round until it reaches the learning_rate_end in the last round.", 0.0d,
Double.POSITIVE_INFINITY, 0.8d));
types.add(new ParameterTypeDouble(PARAMETER_LEARNING_RATE_END, "Defines the strength of an adaption in the last round. The strength will decrease to this value in last round, beginning with learning_rate_start in the first round.", 0.0d,
Double.POSITIVE_INFINITY, 0.01d));
types.add(new ParameterTypeDouble(PARAMETER_ADAPTION_RADIUS_START,
"Defines the radius of the sphere around an stimulus, within an adaption occoures. This radius decreases every round, starting by adaption_radius_start in first round, to adaption_radius_end in last round.", 0.0d,
Double.POSITIVE_INFINITY, 10.0d));
types.add(new ParameterTypeDouble(PARAMETER_ADAPTION_RADIUS_END,
"Defines the radius of the sphere around an stimulus, within an adaption occoures. This radius decreases every round, starting by adaption_radius_start in first round, to adaption_radius_end in last round.", 0.0d,
Double.POSITIVE_INFINITY, 1.0d));
return types;
}
}