/* * 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.meta; import java.util.Iterator; import java.util.List; import com.rapidminer.operator.IOContainer; 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.performance.PerformanceVector; import com.rapidminer.parameter.value.ParameterValues; import com.rapidminer.parameter.value.ParameterValueRange; /** * <p>This operator finds the optimal values for a set of parameters using a grid * search. The parameter <var>parameters</var> is a list of key value pairs * where the keys are of the form <code>operator_name.parameter_name</code> and * the value is either a comma separated list of values (e.g. 10,15,20,25) or an * interval definition in the format [start;end;stepsize] (e.g. [10;25;5]). * Alternatively a value grid pattern may be used by [e.g. [start;end;no_steps;scale], * where scale identifies the type of the pattern.</p> * * <p>The operator returns an * optimal {@link ParameterSet} which can as well be written to a file with a * {@link com.rapidminer.operator.io.ParameterSetWriter}. This parameter set * can be read in another process using a * {@link com.rapidminer.operator.io.ParameterSetLoader}.</p> * * <p>The file format of the parameter set file is straightforward and can easily be * generated by external applications. Each line is of the form * <center><code>operator_name.parameter_name = value</code></center> * </p> * * <p>Please refer to section * {@rapidminer.ref sec:parameter_optimization|Advanced Processes/Parameter and performance analysis} * for an example application. Another parameter optimization schems like the * {@link EvolutionaryParameterOptimizationOperator} might also be useful if the best ranges * and dependencies are not known at all. Another operator which works similar to this parameter * optimization operator is the operator {@link ParameterIteration}. In contrast to the optimization * operator, this operator simply iterates through all parameter combinations. This might be * especially useful for plotting purposes. * </p> * * @author Simon Fischer, Helge Homburg, Ingo Mierswa, Tobias Malbrecht * @version $Id: GridSearchParameterOptimizationOperator.java,v 1.9 2006/03/21 * 15:35:49 ingomierswa Exp $ */ public class GridSearchParameterOptimizationOperator extends ParameterOptimizationOperator { protected Operator[] operators; protected String[] parameters; protected String[][] values; protected int[] currentIndex; protected int numberOfCombinations; protected int numberOfParameters; private ParameterSet best; public GridSearchParameterOptimizationOperator(OperatorDescription description) { super(description); } public int getParameterValueMode() { return VALUE_MODE_DISCRETE; } protected void getParametersToOptimize() throws OperatorException { // check parameter values List<ParameterValues> parameterValuesList = parseParameterValues(getParameterList("parameters")); numberOfCombinations = 1; numberOfParameters = parameterValuesList.size(); for (Iterator<ParameterValues> iterator = parameterValuesList.iterator(); iterator.hasNext(); ) { ParameterValues parameterValues = iterator.next(); if (parameterValues instanceof ParameterValueRange) { logWarning("found (and deleted) parameter values range (" + parameterValues.getKey() + ") which makes no sense in grid parameter optimization"); iterator.remove(); } numberOfCombinations *= parameterValues.getNumberOfValues(); } // initialize data structures operators = new Operator[parameterValuesList.size()]; parameters = new String[parameterValuesList.size()]; values = new String[parameterValuesList.size()][]; currentIndex = new int[parameterValuesList.size()]; // get parameter values and fill data structures int i = 0; for (Iterator<ParameterValues> iterator = parameterValuesList.iterator(); iterator.hasNext(); ) { ParameterValues parameterValues = iterator.next(); operators[i] = parameterValues.getOperator(); parameters[i] = parameterValues.getParameterType().getKey(); values[i] = parameterValues.getValuesArray(); i++; } } public double getCurrentBestPerformance() { if (best != null) { return best.getPerformance().getMainCriterion().getAverage(); } else { return Double.NaN; } } public IOObject[] apply() throws OperatorException { IOContainer input = getInput(); getParametersToOptimize(); // start optimization log("Total number of combinations is " + numberOfCombinations); if (numberOfCombinations <= 1) throw new UserError(this, 922); int counter = 1; best = null; while (true) { log("Using parameter set " + counter + " / " + numberOfCombinations + ":"); // set all parameter values for (int j = 0; j < operators.length; j++) { operators[j].getParameters().setParameter(parameters[j], values[j][currentIndex[j]]); log(operators[j] + "." + parameters[j] + " = " + values[j][currentIndex[j]]); } setInput(input.copy()); PerformanceVector performance = getPerformance(); if ((best == null) || ((performance != null) && (performance.compareTo(best.getPerformance()) > 0))) { String[] bestValues = new String[parameters.length]; for (int j = 0; j < parameters.length; j++) { bestValues[j] = values[j][currentIndex[j]]; } best = new ParameterSet(operators, parameters, bestValues, performance); } // next parameter values int k = 0; boolean ok = true; while (!(++currentIndex[k] < values[k].length)) { currentIndex[k] = 0; k++; if (k >= currentIndex.length) { ok = false; break; } } if (!ok) break; inApplyLoop(); counter++; } return new IOObject[] { best, best.getPerformance() }; } }