Free cookie consent management tool by TermsFeed Policy Generator

source: stable/HeuristicLab.Algorithms.DataAnalysis/3.4/GradientBoostedTrees/GradientBoostedTreesAlgorithm.cs @ 14957

Last change on this file since 14957 was 14957, checked in by mkommend, 7 years ago

#2763: Merged r14841 into stable.

File size: 13.6 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2016 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
23using System.Linq;
24using System.Threading;
25using HeuristicLab.Analysis;
26using HeuristicLab.Common;
27using HeuristicLab.Core;
28using HeuristicLab.Data;
29using HeuristicLab.Optimization;
30using HeuristicLab.Parameters;
31using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
32using HeuristicLab.PluginInfrastructure;
33using HeuristicLab.Problems.DataAnalysis;
34
35namespace HeuristicLab.Algorithms.DataAnalysis {
36  [Item("Gradient Boosted Trees (GBT)", "Gradient boosted trees algorithm. Specific implementation of gradient boosting for regression trees. Friedman, J. \"Greedy Function Approximation: A Gradient Boosting Machine\", IMS 1999 Reitz Lecture.")]
37  [StorableClass]
38  [Creatable(CreatableAttribute.Categories.DataAnalysisRegression, Priority = 125)]
39  public class GradientBoostedTreesAlgorithm : BasicAlgorithm {
40    public override Type ProblemType {
41      get { return typeof(IRegressionProblem); }
42    }
43    public new IRegressionProblem Problem {
44      get { return (IRegressionProblem)base.Problem; }
45      set { base.Problem = value; }
46    }
47
48    #region ParameterNames
49    private const string IterationsParameterName = "Iterations";
50    private const string MaxSizeParameterName = "Maximum Tree Size";
51    private const string NuParameterName = "Nu";
52    private const string RParameterName = "R";
53    private const string MParameterName = "M";
54    private const string SeedParameterName = "Seed";
55    private const string SetSeedRandomlyParameterName = "SetSeedRandomly";
56    private const string LossFunctionParameterName = "LossFunction";
57    private const string UpdateIntervalParameterName = "UpdateInterval";
58    private const string CreateSolutionParameterName = "CreateSolution";
59    #endregion
60
61    #region ParameterProperties
62    public IFixedValueParameter<IntValue> IterationsParameter {
63      get { return (IFixedValueParameter<IntValue>)Parameters[IterationsParameterName]; }
64    }
65    public IFixedValueParameter<IntValue> MaxSizeParameter {
66      get { return (IFixedValueParameter<IntValue>)Parameters[MaxSizeParameterName]; }
67    }
68    public IFixedValueParameter<DoubleValue> NuParameter {
69      get { return (IFixedValueParameter<DoubleValue>)Parameters[NuParameterName]; }
70    }
71    public IFixedValueParameter<DoubleValue> RParameter {
72      get { return (IFixedValueParameter<DoubleValue>)Parameters[RParameterName]; }
73    }
74    public IFixedValueParameter<DoubleValue> MParameter {
75      get { return (IFixedValueParameter<DoubleValue>)Parameters[MParameterName]; }
76    }
77    public IFixedValueParameter<IntValue> SeedParameter {
78      get { return (IFixedValueParameter<IntValue>)Parameters[SeedParameterName]; }
79    }
80    public FixedValueParameter<BoolValue> SetSeedRandomlyParameter {
81      get { return (FixedValueParameter<BoolValue>)Parameters[SetSeedRandomlyParameterName]; }
82    }
83    public IConstrainedValueParameter<ILossFunction> LossFunctionParameter {
84      get { return (IConstrainedValueParameter<ILossFunction>)Parameters[LossFunctionParameterName]; }
85    }
86    public IFixedValueParameter<IntValue> UpdateIntervalParameter {
87      get { return (IFixedValueParameter<IntValue>)Parameters[UpdateIntervalParameterName]; }
88    }
89    public IFixedValueParameter<BoolValue> CreateSolutionParameter {
90      get { return (IFixedValueParameter<BoolValue>)Parameters[CreateSolutionParameterName]; }
91    }
92    #endregion
93
94    #region Properties
95    public int Iterations {
96      get { return IterationsParameter.Value.Value; }
97      set { IterationsParameter.Value.Value = value; }
98    }
99    public int Seed {
100      get { return SeedParameter.Value.Value; }
101      set { SeedParameter.Value.Value = value; }
102    }
103    public bool SetSeedRandomly {
104      get { return SetSeedRandomlyParameter.Value.Value; }
105      set { SetSeedRandomlyParameter.Value.Value = value; }
106    }
107    public int MaxSize {
108      get { return MaxSizeParameter.Value.Value; }
109      set { MaxSizeParameter.Value.Value = value; }
110    }
111    public double Nu {
112      get { return NuParameter.Value.Value; }
113      set { NuParameter.Value.Value = value; }
114    }
115    public double R {
116      get { return RParameter.Value.Value; }
117      set { RParameter.Value.Value = value; }
118    }
119    public double M {
120      get { return MParameter.Value.Value; }
121      set { MParameter.Value.Value = value; }
122    }
123    public bool CreateSolution {
124      get { return CreateSolutionParameter.Value.Value; }
125      set { CreateSolutionParameter.Value.Value = value; }
126    }
127    #endregion
128
129    #region ResultsProperties
130    private double ResultsBestQuality {
131      get { return ((DoubleValue)Results["Best Quality"].Value).Value; }
132      set { ((DoubleValue)Results["Best Quality"].Value).Value = value; }
133    }
134    private DataTable ResultsQualities {
135      get { return ((DataTable)Results["Qualities"].Value); }
136    }
137    #endregion
138
139    [StorableConstructor]
140    protected GradientBoostedTreesAlgorithm(bool deserializing) : base(deserializing) { }
141
142    protected GradientBoostedTreesAlgorithm(GradientBoostedTreesAlgorithm original, Cloner cloner)
143      : base(original, cloner) {
144    }
145
146    public override IDeepCloneable Clone(Cloner cloner) {
147      return new GradientBoostedTreesAlgorithm(this, cloner);
148    }
149
150    public GradientBoostedTreesAlgorithm() {
151      Problem = new RegressionProblem(); // default problem
152
153      Parameters.Add(new FixedValueParameter<IntValue>(IterationsParameterName, "Number of iterations (set as high as possible, adjust in combination with nu, when increasing iterations also decrease nu)", new IntValue(1000)));
154      Parameters.Add(new FixedValueParameter<IntValue>(SeedParameterName, "The random seed used to initialize the new pseudo random number generator.", new IntValue(0)));
155      Parameters.Add(new FixedValueParameter<BoolValue>(SetSeedRandomlyParameterName, "True if the random seed should be set to a random value, otherwise false.", new BoolValue(true)));
156      Parameters.Add(new FixedValueParameter<IntValue>(MaxSizeParameterName, "Maximal size of the tree learned in each step (prefer smaller sizes if possible)", new IntValue(10)));
157      Parameters.Add(new FixedValueParameter<DoubleValue>(RParameterName, "Ratio of training rows selected randomly in each step (0 < R <= 1)", new DoubleValue(0.5)));
158      Parameters.Add(new FixedValueParameter<DoubleValue>(MParameterName, "Ratio of variables selected randomly in each step (0 < M <= 1)", new DoubleValue(0.5)));
159      Parameters.Add(new FixedValueParameter<DoubleValue>(NuParameterName, "Learning rate nu (step size for the gradient update, should be small 0 < nu < 0.1)", new DoubleValue(0.002)));
160      Parameters.Add(new FixedValueParameter<IntValue>(UpdateIntervalParameterName, "", new IntValue(100)));
161      Parameters[UpdateIntervalParameterName].Hidden = true;
162      Parameters.Add(new FixedValueParameter<BoolValue>(CreateSolutionParameterName, "Flag that indicates if a solution should be produced at the end of the run", new BoolValue(true)));
163      Parameters[CreateSolutionParameterName].Hidden = true;
164
165      var lossFunctions = ApplicationManager.Manager.GetInstances<ILossFunction>();
166      Parameters.Add(new ConstrainedValueParameter<ILossFunction>(LossFunctionParameterName, "The loss function", new ItemSet<ILossFunction>(lossFunctions)));
167      LossFunctionParameter.Value = LossFunctionParameter.ValidValues.First(f => f.ToString().Contains("Squared")); // squared error loss is the default
168    }
169
170    [StorableHook(HookType.AfterDeserialization)]
171    private void AfterDeserialization() {
172      // BackwardsCompatibility3.4
173      #region Backwards compatible code, remove with 3.5
174      // parameter type has been changed
175      var lossFunctionParam = Parameters[LossFunctionParameterName] as ConstrainedValueParameter<StringValue>;
176      if (lossFunctionParam != null) {
177        Parameters.Remove(LossFunctionParameterName);
178        var selectedValue = lossFunctionParam.Value; // to be restored below
179
180        var lossFunctions = ApplicationManager.Manager.GetInstances<ILossFunction>();
181        Parameters.Add(new ConstrainedValueParameter<ILossFunction>(LossFunctionParameterName, "The loss function", new ItemSet<ILossFunction>(lossFunctions)));
182        // try to restore selected value
183        var selectedLossFunction =
184          LossFunctionParameter.ValidValues.FirstOrDefault(f => f.ToString() == selectedValue.Value);
185        if (selectedLossFunction != null) {
186          LossFunctionParameter.Value = selectedLossFunction;
187        } else {
188          LossFunctionParameter.Value = LossFunctionParameter.ValidValues.First(f => f.ToString().Contains("Squared")); // default: SE
189        }
190      }
191      #endregion
192    }
193
194    protected override void Run(CancellationToken cancellationToken) {
195      // Set up the algorithm
196      if (SetSeedRandomly) Seed = new System.Random().Next();
197
198      // Set up the results display
199      var iterations = new IntValue(0);
200      Results.Add(new Result("Iterations", iterations));
201
202      var table = new DataTable("Qualities");
203      table.Rows.Add(new DataRow("Loss (train)"));
204      table.Rows.Add(new DataRow("Loss (test)"));
205      table.Rows["Loss (train)"].VisualProperties.StartIndexZero = true;
206      table.Rows["Loss (test)"].VisualProperties.StartIndexZero = true;
207
208      Results.Add(new Result("Qualities", table));
209      var curLoss = new DoubleValue();
210      Results.Add(new Result("Loss (train)", curLoss));
211
212      // init
213      var problemData = (IRegressionProblemData)Problem.ProblemData.Clone();
214      var lossFunction = LossFunctionParameter.Value;
215      var state = GradientBoostedTreesAlgorithmStatic.CreateGbmState(problemData, lossFunction, (uint)Seed, MaxSize, R, M, Nu);
216
217      var updateInterval = UpdateIntervalParameter.Value.Value;
218      // Loop until iteration limit reached or canceled.
219      for (int i = 0; i < Iterations; i++) {
220        cancellationToken.ThrowIfCancellationRequested();
221
222        GradientBoostedTreesAlgorithmStatic.MakeStep(state);
223
224        // iteration results
225        if (i % updateInterval == 0) {
226          curLoss.Value = state.GetTrainLoss();
227          table.Rows["Loss (train)"].Values.Add(curLoss.Value);
228          table.Rows["Loss (test)"].Values.Add(state.GetTestLoss());
229          iterations.Value = i;
230        }
231      }
232
233      // final results
234      iterations.Value = Iterations;
235      curLoss.Value = state.GetTrainLoss();
236      table.Rows["Loss (train)"].Values.Add(curLoss.Value);
237      table.Rows["Loss (test)"].Values.Add(state.GetTestLoss());
238
239      // produce variable relevance
240      var orderedImpacts = state.GetVariableRelevance().Select(t => new { name = t.Key, impact = t.Value }).ToList();
241
242      var impacts = new DoubleMatrix();
243      var matrix = impacts as IStringConvertibleMatrix;
244      matrix.Rows = orderedImpacts.Count;
245      matrix.RowNames = orderedImpacts.Select(x => x.name);
246      matrix.Columns = 1;
247      matrix.ColumnNames = new string[] { "Relative variable relevance" };
248
249      int rowIdx = 0;
250      foreach (var p in orderedImpacts) {
251        matrix.SetValue(string.Format("{0:N2}", p.impact), rowIdx++, 0);
252      }
253
254      Results.Add(new Result("Variable relevance", impacts));
255      Results.Add(new Result("Loss (test)", new DoubleValue(state.GetTestLoss())));
256
257      // produce solution
258      if (CreateSolution) {
259        var model = state.GetModel();
260
261        // for logistic regression we produce a classification solution
262        if (lossFunction is LogisticRegressionLoss) {
263          var classificationModel = new DiscriminantFunctionClassificationModel(model,
264            new AccuracyMaximizationThresholdCalculator());
265          var classificationProblemData = new ClassificationProblemData(problemData.Dataset,
266            problemData.AllowedInputVariables, problemData.TargetVariable, problemData.Transformations);
267          classificationProblemData.TrainingPartition.Start = Problem.ProblemData.TrainingPartition.Start;
268          classificationProblemData.TrainingPartition.End = Problem.ProblemData.TrainingPartition.End;
269          classificationProblemData.TestPartition.Start = Problem.ProblemData.TestPartition.Start;
270          classificationProblemData.TestPartition.End = Problem.ProblemData.TestPartition.End;
271
272          classificationModel.SetThresholdsAndClassValues(new double[] { double.NegativeInfinity, 0.0 }, new[] { 0.0, 1.0 });
273
274
275          var classificationSolution = new DiscriminantFunctionClassificationSolution(classificationModel, classificationProblemData);
276          Results.Add(new Result("Solution", classificationSolution));
277        } else {
278          // otherwise we produce a regression solution
279          Results.Add(new Result("Solution", new RegressionSolution(model, problemData)));
280        }
281      }
282    }
283  }
284}
Note: See TracBrowser for help on using the repository browser.