[13646] | 1 | #region License Information
|
---|
| 2 | /* HeuristicLab
|
---|
| 3 | * Copyright (C) 2002-2015 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
|
---|
| 4 | * and the BEACON Center for the Study of Evolution in Action.
|
---|
| 5 | *
|
---|
| 6 | * This file is part of HeuristicLab.
|
---|
| 7 | *
|
---|
| 8 | * HeuristicLab is free software: you can redistribute it and/or modify
|
---|
| 9 | * it under the terms of the GNU General Public License as published by
|
---|
| 10 | * the Free Software Foundation, either version 3 of the License, or
|
---|
| 11 | * (at your option) any later version.
|
---|
| 12 | *
|
---|
| 13 | * HeuristicLab is distributed in the hope that it will be useful,
|
---|
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
| 16 | * GNU General Public License for more details.
|
---|
| 17 | *
|
---|
| 18 | * You should have received a copy of the GNU General Public License
|
---|
| 19 | * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
|
---|
| 20 | */
|
---|
| 21 | #endregion
|
---|
| 22 |
|
---|
| 23 | using System;
|
---|
| 24 | using System.Collections.Generic;
|
---|
| 25 | using System.Linq;
|
---|
| 26 | using System.Threading;
|
---|
| 27 | using HeuristicLab.Analysis;
|
---|
| 28 | using HeuristicLab.Common;
|
---|
| 29 | using HeuristicLab.Core;
|
---|
| 30 | using HeuristicLab.Data;
|
---|
| 31 | using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
|
---|
| 32 | using HeuristicLab.Optimization;
|
---|
| 33 | using HeuristicLab.Parameters;
|
---|
| 34 | using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
|
---|
| 35 | using HeuristicLab.Problems.DataAnalysis;
|
---|
| 36 | using HeuristicLab.Problems.DataAnalysis.Symbolic;
|
---|
| 37 | using HeuristicLab.Problems.DataAnalysis.Symbolic.Regression;
|
---|
| 38 | using HeuristicLab.Random;
|
---|
[13653] | 39 | using HeuristicLab.Selection;
|
---|
[13646] | 40 |
|
---|
| 41 | namespace HeuristicLab.Algorithms.DataAnalysis.MctsSymbolicRegression {
|
---|
| 42 | [Item("Gradient Boosting Machine Regression (GBM)",
|
---|
| 43 | "Gradient boosting for any regression base learner (e.g. MCTS symbolic regression)")]
|
---|
| 44 | [StorableClass]
|
---|
| 45 | [Creatable(CreatableAttribute.Categories.DataAnalysisRegression, Priority = 350)]
|
---|
| 46 | public class GradientBoostingRegressionAlgorithm : BasicAlgorithm {
|
---|
| 47 | public override Type ProblemType {
|
---|
| 48 | get { return typeof(IRegressionProblem); }
|
---|
| 49 | }
|
---|
| 50 |
|
---|
| 51 | public new IRegressionProblem Problem {
|
---|
| 52 | get { return (IRegressionProblem)base.Problem; }
|
---|
| 53 | set { base.Problem = value; }
|
---|
| 54 | }
|
---|
| 55 |
|
---|
| 56 | #region ParameterNames
|
---|
| 57 |
|
---|
| 58 | private const string IterationsParameterName = "Iterations";
|
---|
| 59 | private const string NuParameterName = "Nu";
|
---|
| 60 | private const string MParameterName = "M";
|
---|
| 61 | private const string RParameterName = "R";
|
---|
| 62 | private const string RegressionAlgorithmParameterName = "RegressionAlgorithm";
|
---|
| 63 | private const string SeedParameterName = "Seed";
|
---|
| 64 | private const string SetSeedRandomlyParameterName = "SetSeedRandomly";
|
---|
| 65 | private const string CreateSolutionParameterName = "CreateSolution";
|
---|
[13889] | 66 | private const string StoreRunsParameterName = "StoreRuns";
|
---|
[13646] | 67 | private const string RegressionAlgorithmSolutionResultParameterName = "RegressionAlgorithmResult";
|
---|
| 68 |
|
---|
| 69 | #endregion
|
---|
| 70 |
|
---|
| 71 | #region ParameterProperties
|
---|
| 72 |
|
---|
| 73 | public IFixedValueParameter<IntValue> IterationsParameter {
|
---|
| 74 | get { return (IFixedValueParameter<IntValue>)Parameters[IterationsParameterName]; }
|
---|
| 75 | }
|
---|
| 76 |
|
---|
| 77 | public IFixedValueParameter<DoubleValue> NuParameter {
|
---|
| 78 | get { return (IFixedValueParameter<DoubleValue>)Parameters[NuParameterName]; }
|
---|
| 79 | }
|
---|
| 80 |
|
---|
| 81 | public IFixedValueParameter<DoubleValue> RParameter {
|
---|
| 82 | get { return (IFixedValueParameter<DoubleValue>)Parameters[RParameterName]; }
|
---|
| 83 | }
|
---|
| 84 |
|
---|
| 85 | public IFixedValueParameter<DoubleValue> MParameter {
|
---|
| 86 | get { return (IFixedValueParameter<DoubleValue>)Parameters[MParameterName]; }
|
---|
| 87 | }
|
---|
| 88 |
|
---|
| 89 | // regression algorithms are currently: DataAnalysisAlgorithms, BasicAlgorithms and engine algorithms with no common interface
|
---|
| 90 | public IConstrainedValueParameter<IAlgorithm> RegressionAlgorithmParameter {
|
---|
| 91 | get { return (IConstrainedValueParameter<IAlgorithm>)Parameters[RegressionAlgorithmParameterName]; }
|
---|
| 92 | }
|
---|
| 93 |
|
---|
| 94 | public IFixedValueParameter<StringValue> RegressionAlgorithmSolutionResultParameter {
|
---|
| 95 | get { return (IFixedValueParameter<StringValue>)Parameters[RegressionAlgorithmSolutionResultParameterName]; }
|
---|
| 96 | }
|
---|
| 97 |
|
---|
| 98 | public IFixedValueParameter<IntValue> SeedParameter {
|
---|
| 99 | get { return (IFixedValueParameter<IntValue>)Parameters[SeedParameterName]; }
|
---|
| 100 | }
|
---|
| 101 |
|
---|
| 102 | public FixedValueParameter<BoolValue> SetSeedRandomlyParameter {
|
---|
| 103 | get { return (FixedValueParameter<BoolValue>)Parameters[SetSeedRandomlyParameterName]; }
|
---|
| 104 | }
|
---|
| 105 |
|
---|
| 106 | public IFixedValueParameter<BoolValue> CreateSolutionParameter {
|
---|
| 107 | get { return (IFixedValueParameter<BoolValue>)Parameters[CreateSolutionParameterName]; }
|
---|
| 108 | }
|
---|
[13889] | 109 | public IFixedValueParameter<BoolValue> StoreRunsParameter {
|
---|
| 110 | get { return (IFixedValueParameter<BoolValue>)Parameters[StoreRunsParameterName]; }
|
---|
| 111 | }
|
---|
[13646] | 112 |
|
---|
| 113 | #endregion
|
---|
| 114 |
|
---|
| 115 | #region Properties
|
---|
| 116 |
|
---|
| 117 | public int Iterations {
|
---|
| 118 | get { return IterationsParameter.Value.Value; }
|
---|
| 119 | set { IterationsParameter.Value.Value = value; }
|
---|
| 120 | }
|
---|
| 121 |
|
---|
| 122 | public int Seed {
|
---|
| 123 | get { return SeedParameter.Value.Value; }
|
---|
| 124 | set { SeedParameter.Value.Value = value; }
|
---|
| 125 | }
|
---|
| 126 |
|
---|
| 127 | public bool SetSeedRandomly {
|
---|
| 128 | get { return SetSeedRandomlyParameter.Value.Value; }
|
---|
| 129 | set { SetSeedRandomlyParameter.Value.Value = value; }
|
---|
| 130 | }
|
---|
| 131 |
|
---|
| 132 | public double Nu {
|
---|
| 133 | get { return NuParameter.Value.Value; }
|
---|
| 134 | set { NuParameter.Value.Value = value; }
|
---|
| 135 | }
|
---|
| 136 |
|
---|
| 137 | public double R {
|
---|
| 138 | get { return RParameter.Value.Value; }
|
---|
| 139 | set { RParameter.Value.Value = value; }
|
---|
| 140 | }
|
---|
| 141 |
|
---|
| 142 | public double M {
|
---|
| 143 | get { return MParameter.Value.Value; }
|
---|
| 144 | set { MParameter.Value.Value = value; }
|
---|
| 145 | }
|
---|
| 146 |
|
---|
| 147 | public bool CreateSolution {
|
---|
| 148 | get { return CreateSolutionParameter.Value.Value; }
|
---|
| 149 | set { CreateSolutionParameter.Value.Value = value; }
|
---|
| 150 | }
|
---|
| 151 |
|
---|
[13889] | 152 | public bool StoreRuns {
|
---|
| 153 | get { return StoreRunsParameter.Value.Value; }
|
---|
| 154 | set { StoreRunsParameter.Value.Value = value; }
|
---|
| 155 | }
|
---|
| 156 |
|
---|
[13646] | 157 | public IAlgorithm RegressionAlgorithm {
|
---|
| 158 | get { return RegressionAlgorithmParameter.Value; }
|
---|
| 159 | }
|
---|
| 160 |
|
---|
| 161 | public string RegressionAlgorithmResult {
|
---|
| 162 | get { return RegressionAlgorithmSolutionResultParameter.Value.Value; }
|
---|
| 163 | set { RegressionAlgorithmSolutionResultParameter.Value.Value = value; }
|
---|
| 164 | }
|
---|
| 165 |
|
---|
| 166 | #endregion
|
---|
| 167 |
|
---|
| 168 | [StorableConstructor]
|
---|
| 169 | protected GradientBoostingRegressionAlgorithm(bool deserializing)
|
---|
| 170 | : base(deserializing) {
|
---|
| 171 | }
|
---|
| 172 |
|
---|
| 173 | protected GradientBoostingRegressionAlgorithm(GradientBoostingRegressionAlgorithm original, Cloner cloner)
|
---|
| 174 | : base(original, cloner) {
|
---|
| 175 | }
|
---|
| 176 |
|
---|
| 177 | public override IDeepCloneable Clone(Cloner cloner) {
|
---|
| 178 | return new GradientBoostingRegressionAlgorithm(this, cloner);
|
---|
| 179 | }
|
---|
| 180 |
|
---|
| 181 | public GradientBoostingRegressionAlgorithm() {
|
---|
| 182 | Problem = new RegressionProblem(); // default problem
|
---|
[13978] | 183 | var osgp = CreateOSGP();
|
---|
[13646] | 184 | var regressionAlgs = new ItemSet<IAlgorithm>(new IAlgorithm[] {
|
---|
[13653] | 185 | new RandomForestRegression(),
|
---|
[13978] | 186 | osgp,
|
---|
[13646] | 187 | });
|
---|
| 188 | foreach (var alg in regressionAlgs) alg.Prepare();
|
---|
| 189 |
|
---|
| 190 |
|
---|
| 191 | Parameters.Add(new FixedValueParameter<IntValue>(IterationsParameterName,
|
---|
| 192 | "Number of iterations", new IntValue(100)));
|
---|
| 193 | Parameters.Add(new FixedValueParameter<IntValue>(SeedParameterName,
|
---|
| 194 | "The random seed used to initialize the new pseudo random number generator.", new IntValue(0)));
|
---|
| 195 | Parameters.Add(new FixedValueParameter<BoolValue>(SetSeedRandomlyParameterName,
|
---|
| 196 | "True if the random seed should be set to a random value, otherwise false.", new BoolValue(true)));
|
---|
| 197 | Parameters.Add(new FixedValueParameter<DoubleValue>(NuParameterName,
|
---|
| 198 | "The learning rate nu when updating predictions in GBM (0 < nu <= 1)", new DoubleValue(0.5)));
|
---|
| 199 | Parameters.Add(new FixedValueParameter<DoubleValue>(RParameterName,
|
---|
| 200 | "The fraction of rows that are sampled randomly for the base learner in each iteration (0 < r <= 1)",
|
---|
| 201 | new DoubleValue(1)));
|
---|
| 202 | Parameters.Add(new FixedValueParameter<DoubleValue>(MParameterName,
|
---|
| 203 | "The fraction of variables that are sampled randomly for the base learner in each iteration (0 < m <= 1)",
|
---|
| 204 | new DoubleValue(0.5)));
|
---|
| 205 | Parameters.Add(new ConstrainedValueParameter<IAlgorithm>(RegressionAlgorithmParameterName,
|
---|
[13978] | 206 | "The regression algorithm to use as a base learner", regressionAlgs, osgp));
|
---|
[13646] | 207 | Parameters.Add(new FixedValueParameter<StringValue>(RegressionAlgorithmSolutionResultParameterName,
|
---|
| 208 | "The name of the solution produced by the regression algorithm", new StringValue("Solution")));
|
---|
| 209 | Parameters[RegressionAlgorithmSolutionResultParameterName].Hidden = true;
|
---|
| 210 | Parameters.Add(new FixedValueParameter<BoolValue>(CreateSolutionParameterName,
|
---|
| 211 | "Flag that indicates if a solution should be produced at the end of the run", new BoolValue(true)));
|
---|
| 212 | Parameters[CreateSolutionParameterName].Hidden = true;
|
---|
[13889] | 213 | Parameters.Add(new FixedValueParameter<BoolValue>(StoreRunsParameterName,
|
---|
| 214 | "Flag that indicates if the results of the individual runs should be stored for detailed analysis", new BoolValue(false)));
|
---|
| 215 | Parameters[StoreRunsParameterName].Hidden = true;
|
---|
[13646] | 216 | }
|
---|
| 217 |
|
---|
| 218 | protected override void Run(CancellationToken cancellationToken) {
|
---|
| 219 | // Set up the algorithm
|
---|
| 220 | if (SetSeedRandomly) Seed = new System.Random().Next();
|
---|
| 221 | var rand = new MersenneTwister((uint)Seed);
|
---|
| 222 |
|
---|
| 223 | // Set up the results display
|
---|
| 224 | var iterations = new IntValue(0);
|
---|
| 225 | Results.Add(new Result("Iterations", iterations));
|
---|
| 226 |
|
---|
| 227 | var table = new DataTable("Qualities");
|
---|
[13889] | 228 | table.Rows.Add(new DataRow("R² (train)"));
|
---|
| 229 | table.Rows.Add(new DataRow("R² (test)"));
|
---|
[13646] | 230 | Results.Add(new Result("Qualities", table));
|
---|
| 231 | var curLoss = new DoubleValue();
|
---|
| 232 | var curTestLoss = new DoubleValue();
|
---|
[13889] | 233 | Results.Add(new Result("R² (train)", curLoss));
|
---|
| 234 | Results.Add(new Result("R² (test)", curTestLoss));
|
---|
[13646] | 235 | var runCollection = new RunCollection();
|
---|
[13889] | 236 | if (StoreRuns)
|
---|
| 237 | Results.Add(new Result("Runs", runCollection));
|
---|
[13646] | 238 |
|
---|
| 239 | // init
|
---|
| 240 | var problemData = Problem.ProblemData;
|
---|
[13889] | 241 | var targetVarName = problemData.TargetVariable;
|
---|
[13707] | 242 | var activeVariables = problemData.AllowedInputVariables.Concat(new string[] { problemData.TargetVariable });
|
---|
[13646] | 243 | var modifiableDataset = new ModifiableDataset(
|
---|
[13707] | 244 | activeVariables,
|
---|
| 245 | activeVariables.Select(v => problemData.Dataset.GetDoubleValues(v).ToList()));
|
---|
[13646] | 246 |
|
---|
| 247 | var trainingRows = problemData.TrainingIndices;
|
---|
| 248 | var testRows = problemData.TestIndices;
|
---|
| 249 | var yPred = new double[trainingRows.Count()];
|
---|
| 250 | var yPredTest = new double[testRows.Count()];
|
---|
| 251 | var y = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, problemData.TrainingIndices).ToArray();
|
---|
| 252 | var curY = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, problemData.TrainingIndices).ToArray();
|
---|
| 253 |
|
---|
| 254 | var yTest = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, problemData.TestIndices).ToArray();
|
---|
| 255 | var curYTest = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, problemData.TestIndices).ToArray();
|
---|
| 256 | var nu = Nu;
|
---|
| 257 | var mVars = (int)Math.Ceiling(M * problemData.AllowedInputVariables.Count());
|
---|
| 258 | var rRows = (int)Math.Ceiling(R * problemData.TrainingIndices.Count());
|
---|
| 259 | var alg = RegressionAlgorithm;
|
---|
| 260 | List<IRegressionModel> models = new List<IRegressionModel>();
|
---|
| 261 | try {
|
---|
| 262 |
|
---|
| 263 | // Loop until iteration limit reached or canceled.
|
---|
| 264 | for (int i = 0; i < Iterations; i++) {
|
---|
| 265 | cancellationToken.ThrowIfCancellationRequested();
|
---|
| 266 |
|
---|
| 267 | modifiableDataset.RemoveVariable(targetVarName);
|
---|
| 268 | modifiableDataset.AddVariable(targetVarName, curY.Concat(curYTest));
|
---|
| 269 |
|
---|
| 270 | SampleTrainingData(rand, modifiableDataset, rRows, problemData.Dataset, curY, problemData.TargetVariable, problemData.TrainingIndices); // all training indices from the original problem data are allowed
|
---|
| 271 | var modifiableProblemData = new RegressionProblemData(modifiableDataset,
|
---|
| 272 | problemData.AllowedInputVariables.SampleRandomWithoutRepetition(rand, mVars),
|
---|
| 273 | problemData.TargetVariable);
|
---|
| 274 | modifiableProblemData.TrainingPartition.Start = 0;
|
---|
| 275 | modifiableProblemData.TrainingPartition.End = rRows;
|
---|
| 276 | modifiableProblemData.TestPartition.Start = problemData.TestPartition.Start;
|
---|
| 277 | modifiableProblemData.TestPartition.End = problemData.TestPartition.End;
|
---|
| 278 |
|
---|
| 279 | if (!TrySetProblemData(alg, modifiableProblemData))
|
---|
| 280 | throw new NotSupportedException("The algorithm cannot be used with GBM.");
|
---|
| 281 |
|
---|
| 282 | IRegressionModel model;
|
---|
| 283 | IRun run;
|
---|
[13898] | 284 |
|
---|
[13646] | 285 | // try to find a model. The algorithm might fail to produce a model. In this case we just retry until the iterations are exhausted
|
---|
[13898] | 286 | if (TryExecute(alg, rand.Next(), RegressionAlgorithmResult, out model, out run)) {
|
---|
[13646] | 287 | int row = 0;
|
---|
| 288 | // update predictions for training and test
|
---|
| 289 | // update new targets (in the case of squared error loss we simply use negative residuals)
|
---|
| 290 | foreach (var pred in model.GetEstimatedValues(problemData.Dataset, trainingRows)) {
|
---|
| 291 | yPred[row] = yPred[row] + nu * pred;
|
---|
| 292 | curY[row] = y[row] - yPred[row];
|
---|
| 293 | row++;
|
---|
| 294 | }
|
---|
| 295 | row = 0;
|
---|
| 296 | foreach (var pred in model.GetEstimatedValues(problemData.Dataset, testRows)) {
|
---|
| 297 | yPredTest[row] = yPredTest[row] + nu * pred;
|
---|
| 298 | curYTest[row] = yTest[row] - yPredTest[row];
|
---|
| 299 | row++;
|
---|
| 300 | }
|
---|
| 301 | // determine quality
|
---|
| 302 | OnlineCalculatorError error;
|
---|
| 303 | var trainR = OnlinePearsonsRCalculator.Calculate(yPred, y, out error);
|
---|
| 304 | var testR = OnlinePearsonsRCalculator.Calculate(yPredTest, yTest, out error);
|
---|
| 305 |
|
---|
| 306 | // iteration results
|
---|
| 307 | curLoss.Value = error == OnlineCalculatorError.None ? trainR * trainR : 0.0;
|
---|
| 308 | curTestLoss.Value = error == OnlineCalculatorError.None ? testR * testR : 0.0;
|
---|
| 309 |
|
---|
| 310 | models.Add(model);
|
---|
| 311 |
|
---|
| 312 |
|
---|
| 313 | }
|
---|
| 314 |
|
---|
[13889] | 315 | if (StoreRuns)
|
---|
| 316 | runCollection.Add(run);
|
---|
| 317 | table.Rows["R² (train)"].Values.Add(curLoss.Value);
|
---|
| 318 | table.Rows["R² (test)"].Values.Add(curTestLoss.Value);
|
---|
[13646] | 319 | iterations.Value = i + 1;
|
---|
| 320 | }
|
---|
| 321 |
|
---|
| 322 | // produce solution
|
---|
| 323 | if (CreateSolution) {
|
---|
| 324 | // when all our models are symbolic models we can easily combine them to a single model
|
---|
| 325 | if (models.All(m => m is ISymbolicRegressionModel)) {
|
---|
| 326 | Results.Add(new Result("Solution", CreateSymbolicSolution(models, Nu, (IRegressionProblemData)problemData.Clone())));
|
---|
| 327 | }
|
---|
| 328 | // just produce an ensemble solution for now (TODO: correct scaling or linear regression for ensemble model weights)
|
---|
[13699] | 329 |
|
---|
[13917] | 330 | var ensembleSolution = CreateEnsembleSolution(models, (IRegressionProblemData)problemData.Clone());
|
---|
[13699] | 331 | Results.Add(new Result("EnsembleSolution", ensembleSolution));
|
---|
[13646] | 332 | }
|
---|
[13699] | 333 | }
|
---|
| 334 | finally {
|
---|
[13646] | 335 | // reset everything
|
---|
| 336 | alg.Prepare(true);
|
---|
| 337 | }
|
---|
| 338 | }
|
---|
| 339 |
|
---|
[13917] | 340 | private static IRegressionEnsembleSolution CreateEnsembleSolution(List<IRegressionModel> models,
|
---|
| 341 | IRegressionProblemData problemData) {
|
---|
| 342 | var rows = problemData.TrainingPartition.Size;
|
---|
| 343 | var features = models.Count;
|
---|
| 344 | double[,] inputMatrix = new double[rows, features + 1];
|
---|
| 345 | //add model estimates
|
---|
| 346 | for (int m = 0; m < models.Count; m++) {
|
---|
| 347 | var model = models[m];
|
---|
| 348 | var estimates = model.GetEstimatedValues(problemData.Dataset, problemData.TrainingIndices);
|
---|
| 349 | int estimatesCounter = 0;
|
---|
| 350 | foreach (var estimate in estimates) {
|
---|
| 351 | inputMatrix[estimatesCounter, m] = estimate;
|
---|
| 352 | estimatesCounter++;
|
---|
| 353 | }
|
---|
| 354 | }
|
---|
[13653] | 355 |
|
---|
[13917] | 356 | //add target
|
---|
| 357 | var targets = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, problemData.TrainingIndices);
|
---|
| 358 | int targetCounter = 0;
|
---|
| 359 | foreach (var target in targets) {
|
---|
| 360 | inputMatrix[targetCounter, models.Count] = target;
|
---|
| 361 | targetCounter++;
|
---|
| 362 | }
|
---|
| 363 |
|
---|
| 364 | alglib.linearmodel lm = new alglib.linearmodel();
|
---|
| 365 | alglib.lrreport ar = new alglib.lrreport();
|
---|
| 366 | double[] coefficients;
|
---|
| 367 | int retVal = 1;
|
---|
| 368 | alglib.lrbuildz(inputMatrix, rows, features, out retVal, out lm, out ar);
|
---|
| 369 | if (retVal != 1) throw new ArgumentException("Error in calculation of linear regression solution");
|
---|
| 370 |
|
---|
| 371 | alglib.lrunpack(lm, out coefficients, out features);
|
---|
| 372 |
|
---|
| 373 | var ensembleModel = new RegressionEnsembleModel(models, coefficients.Take(models.Count)) { AverageModelEstimates = false };
|
---|
[13941] | 374 | var ensembleSolution = (IRegressionEnsembleSolution)ensembleModel.CreateRegressionSolution(problemData);
|
---|
[13917] | 375 | return ensembleSolution;
|
---|
| 376 | }
|
---|
| 377 |
|
---|
| 378 |
|
---|
[13653] | 379 | private IAlgorithm CreateOSGP() {
|
---|
| 380 | // configure strict osgp
|
---|
| 381 | var alg = new OffspringSelectionGeneticAlgorithm.OffspringSelectionGeneticAlgorithm();
|
---|
| 382 | var prob = new SymbolicRegressionSingleObjectiveProblem();
|
---|
| 383 | prob.MaximumSymbolicExpressionTreeDepth.Value = 7;
|
---|
| 384 | prob.MaximumSymbolicExpressionTreeLength.Value = 15;
|
---|
| 385 | alg.Problem = prob;
|
---|
| 386 | alg.SuccessRatio.Value = 1.0;
|
---|
| 387 | alg.ComparisonFactorLowerBound.Value = 1.0;
|
---|
| 388 | alg.ComparisonFactorUpperBound.Value = 1.0;
|
---|
| 389 | alg.MutationProbability.Value = 0.15;
|
---|
| 390 | alg.PopulationSize.Value = 200;
|
---|
| 391 | alg.MaximumSelectionPressure.Value = 100;
|
---|
| 392 | alg.MaximumEvaluatedSolutions.Value = 20000;
|
---|
| 393 | alg.SelectorParameter.Value = alg.SelectorParameter.ValidValues.OfType<GenderSpecificSelector>().First();
|
---|
| 394 | alg.MutatorParameter.Value = alg.MutatorParameter.ValidValues.OfType<MultiSymbolicExpressionTreeManipulator>().First();
|
---|
| 395 | alg.StoreAlgorithmInEachRun = false;
|
---|
| 396 | return alg;
|
---|
| 397 | }
|
---|
| 398 |
|
---|
[13646] | 399 | private void SampleTrainingData(MersenneTwister rand, ModifiableDataset ds, int rRows,
|
---|
| 400 | IDataset sourceDs, double[] curTarget, string targetVarName, IEnumerable<int> trainingIndices) {
|
---|
| 401 | var selectedRows = trainingIndices.SampleRandomWithoutRepetition(rand, rRows).ToArray();
|
---|
| 402 | int t = 0;
|
---|
| 403 | object[] srcRow = new object[ds.Columns];
|
---|
| 404 | var varNames = ds.DoubleVariables.ToArray();
|
---|
| 405 | foreach (var r in selectedRows) {
|
---|
| 406 | // take all values from the original dataset
|
---|
| 407 | for (int c = 0; c < srcRow.Length; c++) {
|
---|
| 408 | var col = sourceDs.GetReadOnlyDoubleValues(varNames[c]);
|
---|
| 409 | srcRow[c] = col[r];
|
---|
| 410 | }
|
---|
| 411 | ds.ReplaceRow(t, srcRow);
|
---|
| 412 | // but use the updated target values
|
---|
| 413 | ds.SetVariableValue(curTarget[r], targetVarName, t);
|
---|
| 414 | t++;
|
---|
| 415 | }
|
---|
| 416 | }
|
---|
| 417 |
|
---|
| 418 | private static ISymbolicRegressionSolution CreateSymbolicSolution(List<IRegressionModel> models, double nu, IRegressionProblemData problemData) {
|
---|
| 419 | var symbModels = models.OfType<ISymbolicRegressionModel>();
|
---|
| 420 | var lowerLimit = symbModels.Min(m => m.LowerEstimationLimit);
|
---|
| 421 | var upperLimit = symbModels.Max(m => m.UpperEstimationLimit);
|
---|
| 422 | var interpreter = new SymbolicDataAnalysisExpressionTreeLinearInterpreter();
|
---|
| 423 | var progRootNode = new ProgramRootSymbol().CreateTreeNode();
|
---|
| 424 | var startNode = new StartSymbol().CreateTreeNode();
|
---|
| 425 |
|
---|
| 426 | var addNode = new Addition().CreateTreeNode();
|
---|
| 427 | var mulNode = new Multiplication().CreateTreeNode();
|
---|
| 428 | var scaleNode = (ConstantTreeNode)new Constant().CreateTreeNode(); // all models are scaled using the same nu
|
---|
| 429 | scaleNode.Value = nu;
|
---|
| 430 |
|
---|
| 431 | foreach (var m in symbModels) {
|
---|
| 432 | var relevantPart = m.SymbolicExpressionTree.Root.GetSubtree(0).GetSubtree(0); // skip root and start
|
---|
| 433 | addNode.AddSubtree((ISymbolicExpressionTreeNode)relevantPart.Clone());
|
---|
| 434 | }
|
---|
| 435 |
|
---|
| 436 | mulNode.AddSubtree(addNode);
|
---|
| 437 | mulNode.AddSubtree(scaleNode);
|
---|
| 438 | startNode.AddSubtree(mulNode);
|
---|
| 439 | progRootNode.AddSubtree(startNode);
|
---|
| 440 | var t = new SymbolicExpressionTree(progRootNode);
|
---|
[13941] | 441 | var combinedModel = new SymbolicRegressionModel(problemData.TargetVariable, t, interpreter, lowerLimit, upperLimit);
|
---|
[13646] | 442 | var sol = new SymbolicRegressionSolution(combinedModel, problemData);
|
---|
| 443 | return sol;
|
---|
| 444 | }
|
---|
| 445 |
|
---|
| 446 | private static bool TrySetProblemData(IAlgorithm alg, IRegressionProblemData problemData) {
|
---|
| 447 | var prob = alg.Problem as IRegressionProblem;
|
---|
| 448 | // there is already a problem and it is compatible -> just set problem data
|
---|
| 449 | if (prob != null) {
|
---|
| 450 | prob.ProblemDataParameter.Value = problemData;
|
---|
| 451 | return true;
|
---|
[13653] | 452 | } else return false;
|
---|
[13646] | 453 | }
|
---|
| 454 |
|
---|
[13898] | 455 | private static bool TryExecute(IAlgorithm alg, int seed, string regressionAlgorithmResultName, out IRegressionModel model, out IRun run) {
|
---|
[13646] | 456 | model = null;
|
---|
[13898] | 457 | SetSeed(alg, seed);
|
---|
[13646] | 458 | using (var wh = new AutoResetEvent(false)) {
|
---|
[13889] | 459 | Exception ex = null;
|
---|
| 460 | EventHandler<EventArgs<Exception>> handler = (sender, args) => {
|
---|
| 461 | ex = args.Value;
|
---|
| 462 | wh.Set();
|
---|
| 463 | };
|
---|
[13646] | 464 | EventHandler handler2 = (sender, args) => wh.Set();
|
---|
| 465 | alg.ExceptionOccurred += handler;
|
---|
| 466 | alg.Stopped += handler2;
|
---|
| 467 | try {
|
---|
| 468 | alg.Prepare();
|
---|
| 469 | alg.Start();
|
---|
| 470 | wh.WaitOne();
|
---|
| 471 |
|
---|
[13889] | 472 | if (ex != null) throw new AggregateException(ex);
|
---|
[13646] | 473 | run = alg.Runs.Last();
|
---|
[13889] | 474 | alg.Runs.Clear();
|
---|
[13646] | 475 | var sols = alg.Results.Select(r => r.Value).OfType<IRegressionSolution>();
|
---|
| 476 | if (!sols.Any()) return false;
|
---|
| 477 | var sol = sols.First();
|
---|
| 478 | if (sols.Skip(1).Any()) {
|
---|
| 479 | // more than one solution => use regressionAlgorithmResult
|
---|
| 480 | if (alg.Results.ContainsKey(regressionAlgorithmResultName)) {
|
---|
| 481 | sol = (IRegressionSolution)alg.Results[regressionAlgorithmResultName].Value;
|
---|
| 482 | }
|
---|
| 483 | }
|
---|
| 484 | var symbRegSol = sol as SymbolicRegressionSolution;
|
---|
| 485 | // only accept symb reg solutions that do not hit the estimation limits
|
---|
| 486 | // NaN evaluations would not be critical but are problematic if we want to combine all symbolic models into a single symbolic model
|
---|
| 487 | if (symbRegSol == null ||
|
---|
| 488 | (symbRegSol.TrainingLowerEstimationLimitHits == 0 && symbRegSol.TrainingUpperEstimationLimitHits == 0 &&
|
---|
| 489 | symbRegSol.TestLowerEstimationLimitHits == 0 && symbRegSol.TestUpperEstimationLimitHits == 0) &&
|
---|
| 490 | symbRegSol.TrainingNaNEvaluations == 0 && symbRegSol.TestNaNEvaluations == 0) {
|
---|
| 491 | model = sol.Model;
|
---|
| 492 | }
|
---|
[13699] | 493 | }
|
---|
| 494 | finally {
|
---|
[13646] | 495 | alg.ExceptionOccurred -= handler;
|
---|
| 496 | alg.Stopped -= handler2;
|
---|
| 497 | }
|
---|
| 498 | }
|
---|
| 499 | return model != null;
|
---|
| 500 | }
|
---|
[13898] | 501 |
|
---|
| 502 | private static void SetSeed(IAlgorithm alg, int seed) {
|
---|
| 503 | // no common interface for algs that use a PRNG -> use naming convention to set seed
|
---|
| 504 | var paramItem = alg as IParameterizedItem;
|
---|
| 505 |
|
---|
| 506 | if (paramItem.Parameters.ContainsKey("SetSeedRandomly")) {
|
---|
| 507 | ((BoolValue)paramItem.Parameters["SetSeedRandomly"].ActualValue).Value = false;
|
---|
| 508 | ((IntValue)paramItem.Parameters["Seed"].ActualValue).Value = seed;
|
---|
| 509 | } else {
|
---|
| 510 | throw new ArgumentException("Base learner does not have a seed parameter (algorithm {0})", alg.Name);
|
---|
| 511 | }
|
---|
| 512 |
|
---|
| 513 | }
|
---|
[13646] | 514 | }
|
---|
| 515 | }
|
---|