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